Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Goridge Laravel Package

spiral/goridge

Goridge is a high-performance PHP-to-Golang IPC bridge and codec library that lets PHP call Go service methods over TCP, Unix sockets, or pipes, with JSON/msgpack structured data and []byte support, minimal overhead, and native net/rpc integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Goridge is the wire protocol that RoadRunner (and therefore Laravel Octane with --server=roadrunner) uses between its Go server and your PHP workers. As of 4.5.0 (2026-10-10) the package was renamed from spiral/goridge to roadrunner/goridge. The PHP namespace is unchanged (Spiral\Goridge\…) — the rename only changes the Composer package name, so every use statement, class name and example in this assessment still applies. Only the composer commands change.

As of 4.6.0 (2026-10-19), MsgpackCodec and Frame::CODEC_MSGPACK are deprecated. The codec still works in the 4.6 line, and 4.6.0 fixes initialization through rybakit/msgpack when ext-msgpack is missing, but new integrations should use JSON or Protobuf rather than adopting Msgpack now. 4.6.0 also changes Goridge’s own RoadRunner dependency from spiral/roadrunner to roadrunner/roadrunner; check direct RoadRunner dependencies if you pin them yourself.

Most Laravel apps already have it installed transitively. Check for both names before adding anything — you may have the old one pinned in your lockfile:

composer show roadrunner/goridge   # 4.6.0+
composer show spiral/goridge       # legacy name, still resolves on older locks
# If missing entirely:
composer require roadrunner/goridge

If a project has spiral/goridge in composer.json directly, swap the requirement to roadrunner/goridge and run composer update roadrunner/goridge. Check composer why roadrunner/goridge and composer why spiral/goridge to make sure you don't end up resolving two entries through different dependency chains — dedupe to the new name. The old name may still work through a replace shim in the new package's composer.json; verify for your pinned version rather than assuming.

Also check which RoadRunner PHP package your app pulls directly:

composer why roadrunner/roadrunner
composer why spiral/roadrunner

4.6.0 moves Goridge onto roadrunner/roadrunner. If your application or another internal package still requires spiral/roadrunner directly, resolve that deliberately instead of letting Composer pick two paths.

Requirements that actually bite: 64-bit PHP, and PHP 8.1+ for the v4 line. The README's "ext-sockets is required" note is stale for modern releases (and the README was rewritten in the 4.5 release anyway), but having it loaded gives you the faster send/recv path.

Read these files first (they are the whole library):

  • src/Relay.php — the Relay::create('<protocol>://<arg1>:<arg2>') factory (tcp, unix, pipes).
  • src/RPC/RPC.php — the client: call(string $method, mixed $payload = null, int $options = 0).
  • src/Frame.php — protocol flags (PAYLOAD_RAW, PAYLOAD_NONE, PAYLOAD_ERROR, PAYLOAD_CONTROL).
  • src/Codec/ — JsonCodec, deprecated MsgpackCodec, ProtobufCodec.
  • examples/ — runnable request/response pairs.

First use case — a health-check artisan command against a Go service:

use Spiral\Goridge\Relay;
use Spiral\Goridge\RPC\RPC;
use Spiral\Goridge\RPC\Exception\ServiceException;

$rpc = new RPC(Relay::create('tcp://127.0.0.1:6001'));

try {
    $version = $rpc->call('app.Version');        // Go: func (s *Service) Version(_ []byte, out *string) error
    $this->info("Go service: {$version}");
} catch (ServiceException $e) {
    // The Go handler returned an error — this is a *service* failure, not a transport one.
    $this->error($e->getMessage());
}

The method name is whatever your Go net/rpc receiver exposes (Service.Method). RoadRunner's own plugins expose methods too (http.Reset, informer.Workers, …) once an rpc: block is present in .rr.yaml.

Implementation Patterns

1. Bind a singleton in a service provider, not the facade. Goridge relays are stateful sockets; one per PHP process is correct, and per Octane worker is exactly what you want.

// config/services.php
'go' => ['dsn' => env('GO_RPC_DSN', 'tcp://127.0.0.1:6001')],

// AppServiceProvider::register()
$this->app->singleton(RPCInterface::class, fn () => new RPC(
    Relay::create(config('services.go.dsn'))
));

2. Wrap it in a gateway class. Never inject RPCInterface into controllers and jobs directly — you want a seam to mock and a place to add retries/metrics. The package rename is a good reminder of why this wrapper matters: an internal gateway means a future package/namespace move touches one file, not fifty.

final class GoImageGateway
{
    public function __construct(private readonly RPCInterface $rpc) {}

    public function resize(string $binary, int $width): string
    {
        return $this->rpc->call('image.Resize', $binary, Frame::PAYLOAD_RAW);
    }
}

3. Use Frame::PAYLOAD_RAW for []byte. Without it the default JSON codec will JSON-encode a PHP string, and the Go side receives a quoted JSON string instead of raw bytes. This is the single most common integration bug.

4. Pipes relay for Go binaries you spawn yourself. Don't shell out with exec() and parse text — give the process a framed stream:

$proc = proc_open(['./bin/go-worker'], [0 => ['pipe','r'], 1 => ['pipe','w']], $pipes);
$rpc  = new RPC(new StreamRelay($pipes[0], $pipes[1]));

Relay::create('pipes://stdin:stdout') is the shorthand when the current process already owns STDIN/STDOUT (this is how RoadRunner workers boot).

5. Pick the codec deliberately. Default JSON is fine and debuggable. ProtobufCodec buys you a contract, but you must keep .proto files and generated PHP classes in lockstep with the Go side — usually overkill unless the schema is already shared. Do not start new integrations on MsgpackCodec: it is deprecated as of 4.6.0, along with Frame::CODEC_MSGPACK. Existing Msgpack users are not broken in 4.6, and 4.6.0 initializes it through rybakit/msgpack when ext-msgpack is absent, but plan a move to JSON or Protobuf rather than expanding usage.

6. Fan-out with MultiRPC (4.2+). Spiral\Goridge\RPC\MultiRPC wraps several RPC/relay instances so one call() dispatches across them — useful when you run multiple Go instances and want parallel or redundant dispatch. Check the class for the exact aggregation semantics in your pinned version.

7. In Octane, reset the connection on worker lifecycle changes. A long-lived relay survives across requests inside a worker; if the Go server restarts, the singleton is dead.

Event::listen(WorkerStarting::class, fn () => app()->forgetInstance(RPCInterface::class));

8. Offload from queue jobs, not the request path. Goridge is synchronous and blocking — a slow Go handler is a slow PHP request. Put heavy calls in a job with $tries/backoff, catch ServiceException for domain errors, and let transport errors bubble to the failed-jobs table.

9. Test by binding a fake. RPCInterface is small (essentially call()), so $this->app->instance(RPCInterface::class, $fake) keeps your suite from needing a Go binary. For relay-level tests, a proc_open'd fake worker over StreamRelay gives you real framing without a network.

Gotchas and Tips

  • The 4.5.0 rename is package-level only. spiral/goridge → roadrunner/goridge; namespace, class names, constructor signatures and protocol are untouched. If your CI grep-checks for spiral/goridge in composer.json (security scanners, licence allowlists, internal package registries), update those rules or the rename will silently skip your supply-chain checks.
  • Pin a single name per project. Mixing spiral/goridge and roadrunner/goridge requirements across a monorepo's packages can produce two paths to the same code or conflicting constraints. Migrate deliberately: bump the constraint, run composer update, then re-verify composer show reports exactly one package.
  • 4.6.0 deprecates MsgpackCodec and Frame::CODEC_MSGPACK. It still works for now, and the release fixes initialization via rybakit/msgpack when ext-msgpack is not installed. Treat that as compatibility support for existing apps, not a reason to adopt Msgpack. New code should use the default JSON codec or ProtobufCodec; existing Msgpack users should add a migration item.
  • 4.6.0 moves Goridge’s RoadRunner dependency to roadrunner/roadrunner. If you directly require spiral/roadrunner, or an internal package does, check composer why and align. Do not assume an old lockfile still describes the current dependency graph.
  • Never echo, var_dump, dd(), or let PHP warnings print to STDOUT inside a RoadRunner/goridge worker. STDOUT is the frame channel; a stray byte corrupts the stream and you get hangs or invalid frame errors that look like random crashes. Log to STDERR (php://stderr), use the Laravel logger, or configure display_errors=stderr.
  • No auto-reconnect. When the Go side dies, the relay stays broken. Catch the transport exception, forgetInstance() the singleton, and let the next call rebuild it — or wrap calls in a small retry helper.
  • Don't share one relay across forked processes. The file descriptor is inherited; two children writing frames to the same socket interleave and sequence numbers desync. Create the relay after the fork.
  • Sequence numbers are shared state. v3.2.1 fixed desync across multiple RPC instances, but the practical rule stands: one relay per RPC object, never one relay shared by two RPC instances on different threads/processes.
  • No built-in cancellation or request timeout. A hung Go handler blocks the worker until the OS-level socket gives up. Set timeouts on the underlying stream and supervise the Go process.
  • Error frames are a separate channel. A ServiceException means the Go method returned an error and the message payload carries its text; a transport exception means the frame itself failed. Handle them differently — one is a 422, the other is a 503/retry.
  • Frames are read whole into memory. Large PAYLOAD_RAW payloads (multi-hundred-MB video, for example) will blow the PHP memory limit. Chunk the transfer yourself; goridge does not stream a single payload.
  • Version pairing matters. Goridge's protocol changed substantially at v3 and again at v4 (RelayInterface::hasFrame() added, call() signature changed, StringableRelayInterface removed). 4.6.x is the current line; 4.5.x was the package rename; 4.2.x was the line that explicitly supported RoadRunner 2025. Pin your version against your RoadRunner build. The 4.5 → 4.6 bump is protocol-safe from a Goridge standpoint, but it carries a codec deprecation and a RoadRunner package-name dependency change.
  • RPC::fromEnvironment() and RPC::fromGlobals() are deprecated since 3.2.1; build the relay explicitly from config.
  • Unix sockets on Windows only work from Windows 10 build 17056+; otherwise fall back to TCP.
  • Debug by dumping frames, not logs. Capture the raw bytes from the relay and check the flags byte — most "the Go side got garbage" reports resolve to a codec mismatch (JSON on one side, msgpack on the other) or a missing PAYLOAD_RAW.
  • Extension points are real: implement RelayInterface (see SocketRelay/StreamRelay) to add, e.g., an in-memory transport for tests or a TLS-terminating proxy relay, and CodecInterface for a custom serializer. Both are injected into RPC, so nothing in the library needs editing — and this insulation is why the package rename costs you nothing beyond Composer bookkeeping.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator