triplestore: Use http timeout if possible

This commit is contained in:
Tom Wiesing 2023-11-15 09:51:35 +01:00
parent 6a739df24b
commit aacde06636
No known key found for this signature in database
8 changed files with 170 additions and 26 deletions

22
go.mod
View file

@ -5,25 +5,25 @@ go 1.21.3
require (
github.com/FAU-CDI/wdresolve v0.0.0-20230108072141-c9c6779d7c41
github.com/alessio/shellescape v1.4.2
github.com/compose-spec/compose-go v1.20.0
github.com/compose-spec/compose-go v1.20.1
github.com/docker/docker v24.0.7+incompatible
github.com/gliderlabs/ssh v0.3.5
github.com/gorilla/csrf v1.7.2
github.com/gorilla/sessions v1.2.1
github.com/gorilla/sessions v1.2.2
github.com/gorilla/websocket v1.5.1
github.com/julienschmidt/httprouter v1.3.0
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
github.com/rs/zerolog v1.31.0
github.com/tkw1536/goprogram v0.5.0
github.com/tkw1536/pkglib v0.0.0-20231110192201-b920fd9f7764
github.com/tkw1536/pkglib v0.0.0-20231114141909-8837d3186025
github.com/yuin/goldmark v1.6.0
github.com/yuin/goldmark-meta v1.1.0
golang.org/x/crypto v0.14.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/net v0.17.0
golang.org/x/sync v0.4.0
golang.org/x/term v0.13.0
golang.org/x/crypto v0.15.0
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/net v0.18.0
golang.org/x/sync v0.5.0
golang.org/x/term v0.14.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.2
gorm.io/gorm v1.25.5
@ -63,11 +63,11 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.14.0 // indirect
golang.org/x/tools v0.15.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

22
go.sum
View file

@ -15,6 +15,8 @@ github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyX
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/compose-spec/compose-go v1.20.0 h1:h4ZKOst1EF/DwZp7dWkb+wbTVE4nEyT9Lc89to84Ol4=
github.com/compose-spec/compose-go v1.20.0/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM=
github.com/compose-spec/compose-go v1.20.1 h1:I6gCMGLl96kEf8XZwaozeTwnNfxA2eVsO46W+5ciTEg=
github.com/compose-spec/compose-go v1.20.1/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -51,6 +53,8 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY=
@ -122,6 +126,8 @@ github.com/tkw1536/goprogram v0.5.0 h1:7vcIjmMdcZPJyRhgdlCaGfHAoOG3oYlFrno1pWXy1
github.com/tkw1536/goprogram v0.5.0/go.mod h1:MDCwqLmvcc2QryMm6oSC9h/QAdE9PewZ2Mp2Lm7MmAg=
github.com/tkw1536/pkglib v0.0.0-20231110192201-b920fd9f7764 h1:rJStxc6PoFUQcsRRqoUNtaM80GZWT69WfWeA+EDbvXM=
github.com/tkw1536/pkglib v0.0.0-20231110192201-b920fd9f7764/go.mod h1:Qi/vpuxuxo5D40O9jLUSmcUF01B5LmJqDxs8o8Lc6bg=
github.com/tkw1536/pkglib v0.0.0-20231114141909-8837d3186025 h1:t3ewoi0rdqQo0a8zFFpmtsUi+O4C+kCYoOM/QkXV7b0=
github.com/tkw1536/pkglib v0.0.0-20231114141909-8837d3186025/go.mod h1:Qi/vpuxuxo5D40O9jLUSmcUF01B5LmJqDxs8o8Lc6bg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -141,12 +147,18 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -155,11 +167,15 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -180,12 +196,16 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -194,6 +214,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View file

@ -32,7 +32,7 @@ func (ts *Triplestore) Backup(scontext *component.StagingContext) error {
}
func (ts Triplestore) listRepositories(ctx context.Context) (repos []Repository, err error) {
res, err := ts.OpenRaw(ctx, "GET", "/rest/repositories", nil, "", "application/json")
res, err := ts.OpenRaw(ctx, "GET", "/rest/repositories", nil, "", "application/json", 0)
if err != nil {
return nil, err
}

View file

@ -27,11 +27,15 @@ type TriplestoreUserAppSettings struct {
ExecuteCount bool `json:"EXECUTE_COUNT"`
}
// http.Client Timeout to be used for "trivial" triplestore operations.
// This includes e.g. CRUDing a specific repo.
const tsTrivialTimeout = time.Minute
// OpenRaw makes an http request to the triplestore api.
//
// When bodyName is non-empty, expect body to be a byte slice representing a multipart/form-data upload with the given name.
// When bodyName is empty, simply marshal body as application/json
func (ts Triplestore) OpenRaw(ctx context.Context, method, url string, body any, bodyName string, accept string) (*http.Response, error) {
func (ts Triplestore) OpenRaw(ctx context.Context, method, url string, body any, bodyName string, accept string, timeout time.Duration) (*http.Response, error) {
var reader io.Reader // to read the body from
var contentType string // content-type of the request being sent
@ -66,6 +70,7 @@ func (ts Triplestore) OpenRaw(ctx context.Context, method, url string, body any,
// create the request object
client := &http.Client{
Timeout: timeout,
Transport: &http.Transport{
DisableKeepAlives: true,
},
@ -92,7 +97,7 @@ func (ts Triplestore) OpenRaw(ctx context.Context, method, url string, body any,
// This is achieved using a polling strategy.
func (ts Triplestore) Wait(ctx context.Context) error {
return timex.TickUntilFunc(func(time.Time) bool {
res, err := ts.OpenRaw(ctx, "GET", "/rest/repositories", nil, "", "")
res, err := ts.OpenRaw(ctx, "GET", "/rest/repositories", nil, "", "", tsTrivialTimeout)
zerolog.Ctx(ctx).Trace().Err(err).Msg("Triplestore wait")
if err != nil {
return false
@ -105,7 +110,7 @@ func (ts Triplestore) Wait(ctx context.Context) error {
// PurgeUser deletes the specified user from the triplestore.
// When the user does not exist, returns no error.
func (ts Triplestore) PurgeUser(ctx context.Context, user string) error {
res, err := ts.OpenRaw(ctx, "DELETE", "/rest/security/users/"+user, nil, "", "")
res, err := ts.OpenRaw(ctx, "DELETE", "/rest/security/users/"+user, nil, "", "", tsTrivialTimeout)
if err != nil {
return err
}
@ -118,7 +123,7 @@ func (ts Triplestore) PurgeUser(ctx context.Context, user string) error {
// PurgeRepo deletes the specified repo from the triplestore.
// When the repo does not exist, returns no error.
func (ts Triplestore) PurgeRepo(ctx context.Context, repo string) error {
res, err := ts.OpenRaw(ctx, "DELETE", "/rest/repositories/"+repo, nil, "", "")
res, err := ts.OpenRaw(ctx, "DELETE", "/rest/repositories/"+repo, nil, "", "", tsTrivialTimeout)
if err != nil {
return err
}

View file

@ -61,7 +61,7 @@ func (ts *Triplestore) CreateRepository(ctx context.Context, name, domain, user,
// do the create!
{
res, err := ts.OpenRaw(ctx, "POST", "/rest/repositories", createRepo.Bytes(), "config", "")
res, err := ts.OpenRaw(ctx, "POST", "/rest/repositories", createRepo.Bytes(), "config", "", tsTrivialTimeout)
if err != nil {
return errTripleStoreFailedRepository.WithMessageF(err)
}
@ -87,7 +87,7 @@ func (ts *Triplestore) CreateRepository(ctx context.Context, name, domain, user,
"READ_REPO_" + name,
"WRITE_REPO_" + name,
},
}, "", "")
}, "", "", tsTrivialTimeout)
if err != nil {
return errTripleStoreFailedRepository.WithMessageF(err)
}

View file

@ -27,7 +27,7 @@ var errTSBackupWrongStatusCode = errors.New("Triplestore.Backup: Wrong status co
// SnapshotDB snapshots the provided repository into dst
func (ts Triplestore) SnapshotDB(ctx context.Context, dst io.Writer, repo string) (int64, error) {
res, err := ts.OpenRaw(ctx, "GET", "/repositories/"+repo+"/statements?infer=false", nil, "", "application/n-quads")
res, err := ts.OpenRaw(ctx, "GET", "/repositories/"+repo+"/statements?infer=false", nil, "", "application/n-quads", 0)
if err != nil {
return 0, err
}

View file

@ -30,7 +30,7 @@ func (ts Triplestore) Update(ctx context.Context, progress io.Writer) error {
ExecuteCount: true,
},
GrantedAuthorities: []string{"ROLE_ADMIN"},
}, "", "")
}, "", "", tsTrivialTimeout)
if err != nil {
return fmt.Errorf("failed to create triplestore user: %s", err)
}
@ -52,7 +52,7 @@ func (ts Triplestore) Update(ctx context.Context, progress io.Writer) error {
logging.LogMessage(progress, "Enabling Triplestore security")
{
res, err := ts.OpenRaw(ctx, "POST", "/rest/security", true, "", "")
res, err := ts.OpenRaw(ctx, "POST", "/rest/security", true, "", "", tsTrivialTimeout)
if err != nil {
return fmt.Errorf("failed to enable triplestore security: %s", err)
}

View file

@ -2,10 +2,14 @@ package phpx
import (
"encoding/json"
"math"
"strconv"
"strings"
"github.com/tkw1536/pkglib/collection"
)
// Marshal marshals data as a PHP expression, meaning it can safely be used inside code.
// Marshal marshals data as a PHP expression, so that it can be safely used inside a php expession.
//
// Typically data is marshaled using [json.Marshal] and decoded in PHP using 'json_decode'.
// Special cases may exist for specific datatypes.
@ -23,11 +27,124 @@ func Marshal(data any) (string, error) {
return "json_decode(" + MarshalString(string(bytes)) + ")", nil
}
var replacer = strings.NewReplacer("'", "\\'", "\\", "\\\\")
// MarshalJSON marshals a json-value safely as an expression to be used as a php string.
//
// A json value is one returned by calling [json.Unmarshal] on a value of type any.
// These are then marshaled by the appropriate function:
//
// - a nil is turned into [PHPNil]
// - a bool is passed to [MarshalBool]
// - a float64 is passed to [MarshalFloat]
// - a string is passed to [MarshalString]
// - an []any is passed to [MarshalSlice]
// - an map[string]any is passed to [MarshalMap]
//
// All marshaling attempts to minify the length of the returned string, meaning compact encodings
// are prefered over length ones.
//
// If a value is none of these types, an empty string is returned.
// No valid value ever returns the empty string
func MarshalJSON(v any) string {
switch v := v.(type) {
case nil:
return PHPNil
case bool:
return MarshalBool(v)
case float64:
return MarshalFloat(v)
case string:
return MarshalString(v)
case []any:
return MarshalSlice(v)
case map[string]any:
return MarshalMap(v)
}
return ""
}
const (
// PHPNil represents the equivalent of a nil value in php
PHPNil = "null"
phpTrue = "!0"
phpFalse = "!1"
phpNaN = "NAN"
phpPositiveInfinity = "INF"
phpNegativeInfinity = "-INF"
)
// MarshalBool marshals b as a boolean to be used in php code.
// This corresponds to the strings "true" or "false".
func MarshalBool(b bool) string {
if b {
return phpTrue
}
return phpFalse
}
// MarshalFloat marshals a floating point number or integer
func MarshalFloat(f float64) string {
// if we actually have an integer, return it!
if i := int64(f); f == float64(i) {
return MarshalInt(i)
}
// special cases
if math.IsNaN(f) {
return phpNaN
}
if math.IsInf(f, 1) {
return phpPositiveInfinity
}
if math.IsInf(f, -1) {
return phpNegativeInfinity
}
// all other cases
return strconv.FormatFloat(f, 'E', -1, 64)
}
// MarshalInt marshals an integer as a string to be used inside a php literal
func MarshalInt(i int64) string {
return strconv.FormatInt(i, 10)
}
var stringReplacer = strings.NewReplacer("'", "\\'", "\\", "\\\\")
// MarshalString marshals s as a php string that can be used safely as a PHP expression.
//
// See [https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.single].
func MarshalString(s string) string {
return "'" + replacer.Replace(s) + "'"
// See [https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.single]
// we just escape
return "'" + stringReplacer.Replace(s) + "'"
}
func MarshalSlice(slice []any) string {
var builder strings.Builder
builder.WriteRune('[')
{
for _, v := range slice {
builder.WriteString(MarshalJSON(v))
builder.WriteRune(',')
}
}
builder.WriteRune(']')
return builder.String()
}
func MarshalMap(m map[string]any) string {
var builder strings.Builder
builder.WriteString("array(")
collection.IterateSorted(m, func(k string, v any) {
builder.WriteString(MarshalString(k))
builder.WriteString("=>")
builder.WriteString(MarshalJSON(v))
builder.WriteString(",")
})
builder.WriteString(")")
return builder.String()
}