laminas/laminas-json-server
Laminas JSON Server provides a JSON-RPC server implementation for Laminas applications, enabling PHP classes and services to be exposed as JSON-RPC endpoints with request handling, method dispatch, error responses, and MVC integration.
laminas/laminas-json-server is a single-purpose, framework-agnostic JSON-RPC server component split out of the former Zend Framework JSON stack. It gives you a Server object you populate with PHP callables or a class instance, a Request/Response pair, an Error type, and an SMD (Service Mapping Description) generator for JSON-RPC service discovery. It is not a Laravel package: there is no service provider, facade, config file, or middleware, and no PSR-7 or PSR-15 entry point that Laravel speaks natively.
Architecture fit. Poor-to-conditional. The component's handle() path is designed to read php://input, set HTTP headers, and echo the response body itself. That is fundamentally at odds with Laravel's kernel/response lifecycle: middleware (sessions, cookies, CORS, terminating middleware), Response decoration, and Octane's worker model all assume the framework returns a response object rather than the library writing to the output stream. You can work around this (subclass Response to suppress sendResponse(), buffer output, re-emit through response()->json()), but every workaround is a fork-by-subclass that future upstream changes can silently break.
Integration feasibility. Feasible in a day or two for a competent PHP engineer if you accept the adapter: one route, one controller that builds the server, one custom response subclass, and a whitelist of exposed service classes. The library's actual value-add over a hand-rolled dispatcher is (a) reflection-based method binding and (b) SMD generation. The JSON-RPC 2.0 surface itself is small — a hand-written dispatcher with typed request/response DTOs is roughly 150–300 lines and gives you native Laravel validation, container injection, and exception mapping. If you do not need SMD/service discovery, the buy-versus-build case is weak.
Technical risk.
setClass exposes public methods reflectively. Without a strict allowlist you risk exposing unintended methods; SMD generation can advertise that surface to clients. Treat exposure as an explicit, reviewed contract.composer why-not before committing.Key questions.
sendResponse() emitting headers, or is subclassing required?Stack fit. Conditional. Works cleanly only if the JSON-RPC endpoint is treated as a self-contained edge: a dedicated route (/rpc), a dedicated controller, token-based auth (Sanctum or signed API keys), rate limiting, and structured logging. It does not fit if you expect JSON-RPC methods to participate in the normal Laravel request pipeline (form requests, policies, resource transformers) without a translation layer. It is a hard no for Octane-hosted services unless the response-capture adapter is written and tested first.
Migration path. Isolate the library behind a first-party interface — e.g. JsonRpcDispatcher::dispatch(string $payload): JsonRpcResult — with the Laminas implementation as one adapter and a hand-rolled dispatcher as the fallback. Service classes registered with the server should be thin, container-resolved facades over existing application services, so that business logic never depends on the library. This makes the eventual swap to native Laravel JSON handling a one-file change. If migrating existing JSON-RPC traffic, keep the wire contract byte-compatible: method names, parameter shapes, error codes, and IDs must not shift, or clients break.
Compatibility. Verify the package's PHP and Laminas constraints against the target Laravel version before scoping. Do not assume the Symfony HttpFoundation request can be handed to the library — plan to extract the raw body, headers, and method yourself and construct the Laminas Request explicitly. Confirm behavior under php artisan serve versus php-fpm versus Octane; the output-buffering adapter is the single highest-risk compatibility item.
Sequencing.
Response subclass or capture layer, dispatcher interface, controller, route, auth middleware, rate limiter, request-size cap.Maintenance. Low code volume, low maintenance frequency — but also low bus factor. Upstream is a small community-maintained component with limited test coverage in the wild; expect to own your adapter and possibly to pin versions. Budget a small recurring review to track Laminas releases and PHP/Laravel deprecations. Because you wrap it behind an interface, routine maintenance is cheap; the expensive scenario is discovering a lifecycle bug after wide adoption.
Support. No vendor, no SLA, no paid tier. Support is GitHub issues plus Laminas community channels. Internal documentation must therefore carry the institutional knowledge: how the response is captured, why headers are suppressed, which methods are exposed, and how errors map to codes. Treat the adapter as owned code with a named maintainer.
Scaling. The protocol is stateless over HTTP, so horizontal scaling is the same as any Laravel endpoint — put it behind the same load balancer and cache layers. Two protocol-specific concerns: batch requests are an amplification vector, so cap batch size and total payload; and reflection-based dispatch plus SMD generation adds CPU per request, which matters under high volume. SMD output is a good candidate for response caching. Under Octane, the echo-based response path must be eliminated or concurrency will corrupt responses across requests.
Failure modes.
Ramp-up. For a mid-level PHP engineer, one to two days to be productive with the library, plus roughly double that to understand the Laravel lifecycle workarounds and the security model around exposed methods. The real ramp-up cost is not the library — it is the JSON-RPC 2.0 spec itself and the absence of internal precedent. Mitigate with the contract test suite built in step 3; it becomes the team's reference documentation. Given the low stars, low opportunity signal, and the fact that the core spec is small, the pragmatic recommendation is: adopt only if SMD/service discovery or an existing Laminas footprint requires it; otherwise implement a thin first-party dispatcher behind the same interface and revisit the library later.
How can I help you explore Laravel packages today?