nonaje / quill
A simple framework to make lightweight PHP APIs
Requires
- php: ^8.3
- psr/container: ^2.0
- psr/http-factory: ^1.1
- psr/http-message: ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- nyholm/psr7: ^1.8
- nyholm/psr7-server: ^1.1
- pestphp/pest: ^2.34
- symfony/var-dumper: ^7.0
This package is auto-updated.
Last update: 2026-03-30 04:31:48 UTC
README
quill-framework is a reusable HTTP kernel: a container, router, middleware pipeline, request/response contracts, and supporting loaders that can be embedded inside any composition root. It does not ship a runtime executable or starter project—those responsibilities move to quill-app.
- Read
docs/framework-boundary.mdfor the canonical split between the framework and consumers. - Read
docs/quill-app-skeleton.mdfor the minimal responsibilities planned for the official starter.
Programmatic Bootstrap
Provide the framework with explicit filesystem inputs and service overrides, then let your own runtime feed PSR-7 requests into the resulting application:
use Nyholm\Psr7Server\ServerRequestCreator; use Quill\Contracts\Response\ResponseSenderInterface; use Quill\Factory\QuillFactory; require __DIR__ . '/vendor/autoload.php'; $root = __DIR__; $app = QuillFactory::shared($root, [ 'config' => [ 'paths' => [$root . '/config'], 'env_paths' => [$root . '/.env', $root . '/.env.local'], 'defaults' => ['app' => ['debug' => false]], 'overrides' => ['app' => ['debug' => getenv('APP_DEBUG') === 'true']], ], 'routes' => [ 'paths' => [$root . '/routes'], ], 'singletons' => [ ResponseSenderInterface::class => static fn () => new CustomSender(), ], ]); $app->get('/health', static fn ($request, $response) => $response->json(['ok' => true])); $creator = ServerRequestCreator::fromGlobals(); $app->handle($creator->fromGlobals());
QuillFactory::make($root, $options)returns a fresh container every call.QuillFactory::shared($root, $options)caches one instance per root; callQuillFactory::forget($root)(or without arguments) when workers/tests need a reset.app($root, $options)is a thin helper that records the default root and proxies toQuillFactory::shared(). Once the root is set you may callapp()without arguments.QuillFactory::container($root)exposes the DI container if you need lower-level access.
Nothing is auto-loaded anymore: if you want .env files, configuration directories, or route trees to load, pass those paths under config.env_paths, config.paths, and routes.paths respectively.
Composition Responsibilities
This package provides loaders (Quill\Loaders\ConfigurationFilesLoader, Quill\Loaders\DotEnvLoader, Quill\Loaders\RouteFilesLoader) and contracts, but it is up to your repository to decide when they run. A typical consumer will:
- Define its own filesystem layout (
config/*.php,routes/**/*.php,.env*). - Call
QuillFactorywith explicit paths and per-environment overrides. - Supply runtime adapters (PSR-7 factories, event loop, web server) and wire them to
$app->handle($psrRequest)or$app->processRequest($psrRequest). - Register application services via the
bindings,singletons, orboothooks instead of touching internal container state.
Contracts & Extension Points
The following contracts form the public boundary that other repositories can depend on (see the boundary doc for the authoritative list):
- Container & lifecycle:
Quill\Contracts\ApplicationInterface,Quill\Contracts\Container\ContainerInterface. - Configuration:
Quill\Contracts\Configuration\ConfigurationInterfaceplus the loaders mentioned above. - HTTP:
Quill\Contracts\Request\RequestInterface,Quill\Contracts\Request\RequestFactoryInterface,Quill\Contracts\Response\ResponseInterface,Quill\Contracts\Response\ResponseSenderInterface. - Routing & middleware:
Quill\Contracts\Router\RouterInterface,RouteInterface,RouteGroupInterface,RouteStoreInterface,MiddlewareStoreInterface,Quill\Contracts\Middleware\MiddlewarePipelineInterface,Quill\Contracts\Middleware\MiddlewareFactoryInterface. - Error handling & support:
Quill\Contracts\ErrorHandler\ErrorHandlerInterface,Quill\Contracts\Support\PathResolverInterface.
All extension takes place through those contracts: register singletons/bindings, run configuration overrides, or contribute routes/middleware with the exposed router APIs.
Runtime Expectations
quill-framework no longer couples to Nyholm or any specific server runtime. Tests rely on Nyholm inside require-dev purely to create PSR-7 requests; consumers are free to swap in any PSR-7 implementation or server runner as long as they feed PSR-7 messages into the application and honour the interfaces above.
The upcoming quill-app repository will ship the default runtime entrypoints, DX tooling, and scaffolding expected by most projects. Until then, this package focuses exclusively on the reusable framework surface.