Skip to content

SoloPHPSmall PSR packages for PHP

A router, a container, request DTOs, repositories and more, each one independent and replaceable. Use one, or all of them together.

SoloPHP

Quick Start ​

Start from one of the two starters. Each is a working application with a module to copy, tests and static analysis.

bash
composer create-project solophp/skeleton my-app
cd my-app
composer serve    # http://127.0.0.1:8000
bash
composer create-project solophp/inertia-svelte-skeleton my-app
cd my-app
npm install
composer serve    # http://127.0.0.1:8000
npm run dev       # in a second terminal: Vite with hot reload

Both need PHP 8.4 and Composer, and the Inertia starter also needs Node.js. Getting Started walks through the JSON API starter, from the install to a module of your own with its tests; Inertia Starter covers the other one.

Or take a single package into any project:

bash
composer require solophp/router

The Idea ​

Not a framework. There is no core that every package depends on. Each package does one job and installs on its own. The starters are ordinary applications that put the packages together, and their code is yours to change.

PSR at the seams. The packages meet at PSR interfaces: PSR-7 and PSR-17 for HTTP messages, PSR-11 for the container, PSR-15 for middleware, PSR-3 for logging, PSR-14 for events, PSR-16 for caching and PSR-18 for HTTP clients. Where PSR has no interface, solophp/contracts adds one, such as a writable container or a validator. Any package can be replaced by another implementation of the same interface, and any PSR library works beside them.

Explicit code. Providers, middleware and routes are plain PHP files you read from top to bottom. A request becomes a typed, immutable DTO, a repository returns your own readonly models, and the starters pass PHPStan at level max.

How the Packages Fit ​

One request through the JSON API starter:

public/index.php
└─ Application              PSR-15 kernel; service providers fill the Container
   ├─ ErrorMiddleware       your code: every exception below becomes a JSON error
   ├─ JsonBodyMiddleware    your code: a JSON body becomes getParsedBody()
   └─ RoutingMiddleware     Router matches the route, or throws a 404 / 405
      └─ TaskController     built by the Container, dependencies injected by type
         ├─ InputFactory    Input + Validator: the request becomes a TaskStoreInput, or throws a 422
         └─ TaskRepository  Repository on Doctrine DBAL: rows become Task models
→ the PSR-7 response, sent by HTTP-Emitter

Every name in that tree is a class you can open: the middleware and the controller live in the application, the rest in the packages.

Requirements ​

  • PHP 8.3 or later for the packages; Container and the starters need PHP 8.4.
  • Composer, and Node.js for the Inertia starter.
  • Every package is released under the MIT License.

Released under the MIT License.