keel / framework
Worker-first PHP micro-framework: PSR stack, attribute routing, compiled container.
Requires
- php: >=8.4
- nyholm/psr7: ^1.8
- nyholm/psr7-server: ^1.1
- psr/clock: ^1.0
- psr/container: ^2.0
- psr/event-dispatcher: ^1.0
- psr/http-client: ^1.0
- psr/http-factory: ^1.1
- psr/http-message: ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
- psr/log: ^3.0
- psr/simple-cache: ^3.0
Requires (Dev)
- doctrine/dbal: ^4.2
- friendsofphp/php-cs-fixer: ^3.75
- infection/infection: ^0.35.8
- latte/latte: ^3.0
- league/flysystem: ^3.29
- monolog/monolog: ^3.8
- open-telemetry/sdk: ^1.15
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.0
- spiral/roadrunner-http: ^4.2
- symfony/console: ^7.3|^8.0
- symfony/mailer: ^8.1
- twig/twig: ^3.15
Suggests
- ext-apcu: Shared cache, rate-limit counters and metrics across workers
- ext-curl: Keel\HttpClient\CurlClient
- ext-intl: ICU plural and select patterns in Keel\I18n
- ext-memcached: Keel\Cache\MemcachedCache
- ext-redis: Keel\Redis\RedisProvider: Redis cache, sessions, counters and metrics
- doctrine/dbal: Keel\Database\DbalProvider
- latte/latte: Keel\View\LatteProvider
- league/flysystem: Keel\Storage\FlysystemStorage
- monolog/monolog: PSR-3 logger implementation
- open-telemetry/sdk: Keel\Telemetry\TracingMiddleware (OpenTelemetry spans per request)
- spiral/roadrunner-http: RoadRunner runtime adapter
- symfony/console: Console runtime and keel:* commands (any 7.3+ or 8.x)
- symfony/mailer: Keel\Mail\MailerProvider: SMTP, sendmail or API mail with Mailer::send() and defer()
- twig/twig: Keel\View\TwigProvider
Provides
None
Conflicts
None
Replaces
- keel/container: v1.5.0
- keel/http: v1.5.0
- keel/router: v1.5.0
- keel/validation: v1.5.0
README
A small, worker-first PHP micro-framework: PSR-7/11/14/15/17 all the way, attribute-based routing with scoped discovery, a compiled container and router, and the same code running under php-fpm, FrankenPHP worker mode and RoadRunner.
Status: 1.5.0, API frozen under semantic versioning. Documentation site: https://alonecoder.github.io/keel/ Start with composer create-project keel/skeleton my-app. See CHANGELOG.md and the roadmap below.
- Container · по-русски
- Routing · по-русски
- HTTP and kernel · по-русски
- Runtimes: php-fpm, FrankenPHP, RoadRunner · по-русски
- Application: config, discovery, events, console · по-русски
- Testing · по-русски
- Sessions and signed cookies · по-русски
- Authentication and rate limiting · по-русски
- OpenAPI · по-русски
- Health and metrics · по-русски
- Database, Templates, Cache, HTTP client · по-русски
- File storage, Localization, Mail · по-русски
- API stability · по-русски
Try the example:
php -S 127.0.0.1:8080 -t examples/hello/public examples/hello/public/index.php curl http://127.0.0.1:8080/hello/world php examples/hello/bin/console keel:routes
The console ships keel:routes, keel:compile, keel:cache-clear, keel:debug:container, keel:doctor,
keel:preload, keel:openapi, keel:serve (built-in server or FrankenPHP worker mode with
--watch), keel:install (a component's package, provider, middleware and config in one step),
keel:ai:init (AGENTS.md and llms.txt) and the keel:make:* generators for controllers,
commands, middleware, listeners, health checks, authenticators and policies; see
Application.
Requirements: PHP >= 8.4.
composer install
composer test
composer stan
composer cs
composer infection
CI runs the tests on PHP 8.4 and 8.5, PHPStan at level max, php-cs-fixer, and Infection on the container, router, auth, rate limiting, health and metrics (MSI gate 80%).
Packages
keel/framework is the whole thing. Four components are also published on their own as read-only
splits of this repository and are replaced by the framework: keel/container,
keel/router, keel/http
(depends on router and validation) and keel/validation.
Issues and pull requests go here; bin/split vX.Y.Z pushes the splits.
Benchmarks
In-process, same route (GET /json/42, JSON body), production mode with warm caches,
PHP 8.4.21 with opcache, JIT off, Apple Silicon laptop, 30 000 iterations after warm-up.
The number measures framework overhead per request; the SAPI and the network are excluded.
Reproduce with cd benchmarks && composer install && php -d opcache.enable_cli=1 run.php.
| Framework | boot (ms) | µs / request | requests / s | memory after boot |
|---|---|---|---|---|
| Keel 0.1.0 (compiled container, cached routes) | 11.8 | 4.8 | 208 000 | 0.21 MB |
| Slim 4.15 (closure routes, no container) | 5.9 | 4.8 | 206 000 | 0.07 MB |
Symfony 8.1 MicroKernel (FrameworkBundle, #[Route]) |
29.5 | 31.0 | 32 000 | 0.52 MB |
Memory: a production boot loads 30 files (26 classes, 3 cache files, 1 config file), about
0.3 MB of heap without opcache and well under 100 KB with it; Route objects are materialized
only for routes that match, and nothing survives a request: 2000 requests in a row add 0 bytes.
keel:preload generates an opcache.preload script so php-fpm links every class once: with
it a boot allocates 4 KB and loads 3 files, and boot plus first request drops from 12.8 ms to 2.7 ms.
Measure with php -d opcache.enable_cli=1 memory.php and memory-classes.php in benchmarks/.
Under a real server: FrankenPHP 1.12 worker mode (8 workers), oha with 64 connections for 10 s on
the same laptop, same JSON route: Keel 8 486 requests/s, Slim 8 603 requests/s, median latency 5.2 ms
and p99 34 ms for both, every response 200. Reproduce with benchmarks/frankenphp/run.sh.
Keel does more work per request than the Slim app in this table (attribute controller,
argument resolution with type casting, Problem Details handler, container-resolved
middleware) at the same cost. Symfony's number includes its full HttpKernel event cycle
and terminate().
Roadmap
1.0 closed the core; 1.1 added what a production API needs: authentication (#[Auth], bearer,
API key, HMAC), #[RateLimit], health endpoints, Prometheus metrics, an OpenAPI 3.1 document
derived from the code, and keel:serve for FrankenPHP worker mode in development. 1.2 wired the
usual neighbours: PDO or Doctrine DBAL with health checks, Twig or Latte, a dependency-free PSR-16
cache and a PSR-18 client on curl; 1.2.1 added the health-check and authenticator generators and
widened mutation testing. 1.3 hardened the code after a full review, added keel:doctor, and
closed the gaps against Slim and Lumen: optional path segments, redirects, route groups, HTTP
caching, CSRF, policies, middleware with arguments, the Input reader, log channels, Redis and
Memcached, file storage and localization. 1.4 added what other ecosystems ship first-party:
security headers, body limits, trusted hosts, idempotency keys, Server-Timing, deprecation
headers, streaming and Server-Sent Events, load shedding, lifecycle events, work after the
response, a JWT/JWKS authenticator, an output cache with tags, keel:install and
keel:ai:init. 1.5 added HTTP-client retries with a circuit breaker, signed URLs and Symfony Mailer. Next:
feedback from real projects decides; queues and schedulers stay out of
scope.
License: MIT.