Skip to content

Commit a7d8e9b

Browse files
Support PHP serialization and update Handler signature
- Added `github.com/elliotchance/phpserialize` dependency - Added `UnserializeCommand` helper in `pkg/queue/serializer.go` - Updated `Handler` signature to `func(context.Context, *Job) error` - Updated `Job` struct to include `Payload` (*LaravelJob) and `UnserializedData` (any) - Updated worker to automatically unserialize PHP command strings if present - Updated main.go and tests to reflect new signature
1 parent 3590053 commit a7d8e9b

7 files changed

Lines changed: 81 additions & 13 deletions

File tree

cmd/worker/main.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
"context"
5-
"encoding/json"
65
"log"
76
"os"
87
"os/signal"
@@ -15,12 +14,15 @@ import (
1514
)
1615

1716
// ExampleHandler is a sample job handler
18-
func ExampleHandler(ctx context.Context, body []byte) error {
19-
var job queue.LaravelJob
20-
if err := json.Unmarshal(body, &job); err != nil {
21-
return err
17+
func ExampleHandler(ctx context.Context, job *queue.Job) error {
18+
log.Printf("Processing job: %s, ID: %s", job.Payload.DisplayName, job.Payload.UUID)
19+
20+
// Example of accessing unserialized PHP data
21+
if job.UnserializedData != nil {
22+
// Map properties if needed
23+
// props := queue.GetPHPProperty(job.UnserializedData, "podcastId")
24+
log.Printf("Unserialized data present")
2225
}
23-
log.Printf("Processing job: %s, ID: %s", job.DisplayName, job.UUID)
2426
return nil
2527
}
2628

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ require (
88
github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect
99
github.com/cespare/xxhash/v2 v2.3.0 // indirect
1010
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
11+
github.com/elliotchance/phpserialize v1.4.0 // indirect
1112
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
88
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
99
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
1010
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
11+
github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY=
12+
github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
1113
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
1214
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
1315
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=

pkg/queue/interfaces.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import (
66

77
// Job represents a generic job retrieved from the queue
88
type Job struct {
9-
ID string
10-
Body []byte
9+
ID string
10+
Body []byte
11+
Payload *LaravelJob // The parsed JSON envelope
12+
UnserializedData any // The unserialized PHP command properties (if applicable)
1113
}
1214

13-
// Handler is the function signature for processing a job's payload
14-
type Handler func(ctx context.Context, payload []byte) error
15+
// Handler is the function signature for processing a job
16+
type Handler func(ctx context.Context, job *Job) error
1517

1618
// Driver defines the interface for queue backends
1719
type Driver interface {

pkg/queue/serializer.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package queue
2+
3+
import (
4+
"encoding/json"
5+
"strings"
6+
7+
"github.com/elliotchance/phpserialize"
8+
)
9+
10+
// UnserializeCommand attempts to parse the PHP serialized command from the job payload
11+
func UnserializeCommand(data json.RawMessage) (any, error) {
12+
// First, try to unmarshal data as a map to find "command"
13+
var dataMap map[string]interface{}
14+
if err := json.Unmarshal(data, &dataMap); err != nil {
15+
return nil, err
16+
}
17+
18+
commandStr, ok := dataMap["command"].(string)
19+
if !ok {
20+
// Not a standard Laravel serialized command job
21+
return dataMap, nil
22+
}
23+
24+
var out interface{}
25+
err := phpserialize.Unmarshal([]byte(commandStr), &out)
26+
return out, err
27+
}
28+
29+
// Helper to extract a private property from a PHP object map if needed
30+
// PHP serialized objects often have keys like "\x00*\x00propName" or "\x00ClassName\x00propName"
31+
func GetPHPProperty(obj any, propName string) any {
32+
m, ok := obj.(map[interface{}]interface{})
33+
if !ok {
34+
return nil
35+
}
36+
37+
// Try direct match
38+
if val, ok := m[propName]; ok {
39+
return val
40+
}
41+
42+
// Try protected/private match (* for protected, ClassName for private)
43+
// We iterate because constructing the exact key with null bytes is tricky in Go string literals
44+
for k, v := range m {
45+
ks, ok := k.(string)
46+
if !ok {
47+
continue
48+
}
49+
if strings.HasSuffix(ks, "\x00"+propName) {
50+
return v
51+
}
52+
}
53+
return nil
54+
}

pkg/worker/worker.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ func (w *Worker) handleJob(ctx context.Context, job *queue.Job) {
8383
return
8484
}
8585

86+
// Attempt to unserialize PHP command if present
87+
unserialized, _ := queue.UnserializeCommand(payload.Data)
88+
89+
// Populate job details
90+
job.Payload = &payload
91+
job.UnserializedData = unserialized
92+
8693
// Execute handler
8794
var jobCtx context.Context
8895
var cancel context.CancelFunc
@@ -94,7 +101,7 @@ func (w *Worker) handleJob(ctx context.Context, job *queue.Job) {
94101
}
95102
defer cancel()
96103

97-
err = handler(jobCtx, job.Body)
104+
err = handler(jobCtx, job)
98105
if err != nil {
99106
log.Printf("Job %s failed: %v", payload.DisplayName, err)
100107
w.handleFailure(ctx, payload, err)

pkg/worker/worker_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func TestWorker_Run_Success(t *testing.T) {
4141
// Setup Registry
4242
jobName := "TestJob"
4343
handled := false
44-
queue.Register(jobName, func(ctx context.Context, body []byte) error {
44+
queue.Register(jobName, func(ctx context.Context, job *queue.Job) error {
4545
handled = true
4646
return nil
4747
})
@@ -77,7 +77,7 @@ func TestWorker_Run_Retry(t *testing.T) {
7777
// Setup Registry
7878
jobName := "RetryJob"
7979
calls := 0
80-
queue.Register(jobName, func(ctx context.Context, body []byte) error {
80+
queue.Register(jobName, func(ctx context.Context, job *queue.Job) error {
8181
calls++
8282
return errors.New("failed")
8383
})

0 commit comments

Comments
 (0)