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

Framework Laravel Package

psx/framework

PSX is a PHP framework for building REST APIs and web services. It bundles routing, controller and data-processing components, API documentation, and SQL/Doctrine integration, letting you define resources and expose them as JSON or XML endpoints with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

psx/framework is the kernel of the PSX stack — routing, dispatch, DI wiring, the filter chain, and the console — not a full application. Do not composer require it into an existing Laravel or Symfony app: it is its own engine with its own bootstrap, config format, and container. Treat it as a framework you adopt wholesale or not at all.

Start from a PSX skeleton project rather than hand-assembling the dependencies. The engine is only meaningful alongside its siblings (psx/api, psx/data, psx/http, psx/uri, psx/json, psx/sql), and the skeleton pins a combination that actually boots. Assemble it yourself and you will spend a day on inter-package version mismatches before writing a single endpoint.

Once you have a working checkout, read these five files in this order — they are the entire mental model:

  1. public/index.php — the front controller; everything else hangs off it.
  2. config.php — returns a flat array (psx_url, psx_dispatch, psx_path_cache, psx_debug, psx_commands, filter list). There is no config/ directory; this one file is it.
  3. routing.php — returns an array of [methods, path, controllerClass] triples.
  4. container.php — the service definitions the object builder resolves.
  5. src/ — your controllers.

Bring it up with the bundled console (typically php bin/psx serve, and php bin/psx list to see exactly which commands your version ships), then hit the default route in a browser to confirm dispatch works before touching anything.

The right first use case is a JSON controller returned from a route you wrote by hand — no schema, no codegen. It exercises the routing parser, the container, dispatch, and the response path all at once, and when it breaks the failure is unambiguous.

<?php
namespace App\Controller;

use PSX\Framework\Controller\ControllerInterface;
use PSX\Http\Environment\HttpContextInterface;
use PSX\Http\Environment\HttpResponse;
use PSX\Http\Environment\HttpResponseInterface;

class PingController implements ControllerInterface
{
    public function handle(HttpContextInterface $context): HttpResponseInterface
    {
        return new HttpResponse(200, [], ['pong' => true]);
    }
}
// routing.php
return [
    [['GET'], '/ping', App\Controller\PingController::class],
];

Note the signature: PSX controllers receive an HttpContext, not a PSR-7 ServerRequestInterface. Older releases used handle(RequestInterface $request). Confirm the signature against the ControllerInterface in your installed version before writing more than one controller.

Implementation Patterns

Map your Laravel reflexes onto PSX deliberately. Routes live in routing.php instead of routes/api.php; middleware are filters composed into a FilterChain; the service container is container.php + the PSX object builder, which is annotation/reflection-driven and noticeably less magic than Laravel's; Artisan is bin/psx; Eloquent is replaced by psx/sql with psx/data hydrating rows into typed POPOs. There is no Blade, no facades, no global helpers. Almost every "how do I..." maps to something, but never to the same name.

Every controller must be resolvable from the container. Routing maps a path to a class name; dispatch then asks the container for that class. A controller with a constructor dependency that isn't declared in container.php fails at dispatch time, not at boot — and the error surfaces as a 500 with a stack trace only when psx_debug is on. Register explicit definitions for anything with dependencies, and keep controllers thin: parse input, call a service, return HttpResponse.

Work API-first once past the prototype. The real value of this stack is generating controllers and schema classes from an OpenAPI/TypeSchema/RAML definition, then filling in the generated stubs. Enumerate the generator commands with php bin/psx list (names have churned across majors) and pick the ones matching your spec dialect. The workflow is: edit the spec → regenerate → implement the body of each action → never hand-edit generated files, because the next regeneration overwrites them. Keep generated code in its own namespace and directory so you can blow it away cleanly.

Use filters for cross-cutting concerns, and mind their order. Auth, CORS, session, request logging, and exception-to-status conversion all live in the filter chain rather than in middleware classes you register per-route. Order is meaningful: exception conversion should be outermost so it catches failures from everything inside it, CORS should run before auth so preflights don't 401, and logging should wrap as much as possible. Read the chain definition in config.php/container.php before appending — you are editing a global pipeline, not a route group.

Let psx/data do the hydration. Rather than hand-reading $context->getBody() into arrays, wire a reader/writer pair and let PSX map JSON into a typed POPO using the schema class generated from your API definition. You get validation and type coercion at the boundary for free, and your controller receives an object instead of a Record.

Test against the HTTP surface, not the controller method. Boot the app, issue a request, assert on status and body. There is a test harness shipped under the framework's Test namespace — check what your version exposes and build on it; do not mock the container, because container wiring is precisely the thing that breaks.

Gotchas and Tips

The cache directory is the answer to half your problems. Routing and DI metadata are cached. After editing routing.php, container.php, or config.php and seeing no change, clear the cache path (psx_path_cache) before you start debugging anything else. Make sure the directory is writable by the web user — a silent cache-write failure presents as stale routes.

psx_url and psx_dispatch must match reality. Behind a reverse proxy, in a subdirectory, or on HTTPS, mismatched values break route generation and redirects in ways that look like framework bugs. This is the PSX equivalent of Laravel's ASSET_URL/TrustProxies confusion and it burns everyone once.

Pin every psx/* package to the same minor. The engine assumes matching internals across psx/api, psx/data, and psx/http. Major versions bump frequently and are not shy about signature changes. If you must upgrade, upgrade the whole family in one commit and run the test suite; never bump psx/framework alone.

Read the request body exactly once. The body is a stream. If a filter or your controller reads it and something downstream tries again, the second read is empty. Snapshot it at the earliest point you need it.

Prefer typed exceptions over manual status codes. Throw a StatusCodeException (or the framework's equivalents) and let the exception filter translate it. Hand-building HttpResponse(400, ...) inside every branch spreads HTTP semantics across your domain code.

Turn psx_debug off in production, and expect the failure mode to be terse. With it off you get status codes and little else. Set up your own logging filter early rather than discovering this during an incident.

Extension points are the container, the filter chain, and the routing parser. Need routes from a database or a different file format? Implement the routing-parser interface and register it. Need a new cross-cutting behavior? Write a filter. Everything else in the engine is glue you should not subclass.

Assume no community safety net. Single-digit stars and effectively zero dependents means no Stack Overflow answers, no blog posts, no third-party bundles. Your sources of truth are the skeleton repos, the package test suites (which are genuinely good and double as documentation), and the source itself. Budget time for reading code, and prefer upgrading rarely and deliberately over tracking upstream.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core