Download the PHP package joydeep-bhowmik/quire without Composer
On this page you can find all versions of the php package joydeep-bhowmik/quire. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download joydeep-bhowmik/quire
More information about joydeep-bhowmik/quire
Files in joydeep-bhowmik/quire
Package quire
Short Description File-based page router for plain PHP: every .php or .blade.php file is a route, with middleware and named routes.
License MIT
Homepage https://github.com/joydeep-bhowmik/quire
Informations about the package quire
Quire
A page-based router for plain PHP, inspired by Laravel Folio. No framework needed.
Drop .php files in a folder and each one becomes a route. Middleware and route names are declared at the top of the page.
A quire is a stack of folio sheets bound together.
Installation
Requires PHP 8.1+. No other dependencies. For Blade pages, also install Blade (see Blade).
Usage
Send every request that isn't a real file to index.php. With Apache, use
example/public/.htaccess. With PHP's built-in server, run
php -S localhost:8000 -t public public/index.php.
No Composer? Download the repo and require 'path/to/quire/bootstrap.php'; instead of the autoloader.
Example app
example/ is a ready-made starter project: a small Blade site styled with Tailwind CSS v4,
with its own composer.json that pulls in Quire and Blade. Copy the folder anywhere and run:
The built CSS is checked in, so the demo is styled without Node. To change styles:
tests/fixtures/ has a bigger app covering middleware, catch-alls, plain PHP pages and more.
Coding agents
Quire ships an Agent Skill that teaches coding agents (Claude Code and others that read
SKILL.md) its routing rules, metadata block, middleware, error pages, Blade setup and common mistakes.
Copy it into your project:
Development
Pages
A page is a normal PHP file. Route params are local variables, and $request is always there.
What the page returns decides the response:
| Page does | Response |
|---|---|
outputs HTML (echo, ?>...) |
200 text/html |
return ['ok' => true]; |
JSON |
return redirect('/somewhere'); |
that Response |
abort(403, 'Nope'); |
error page with 403 |
respond($response); |
that response, from anywhere (even templates) |
Page metadata
Put these at the top of the page, before any other code:
Quire reads only the run of use, name(), middleware() and methods() statements at the top of the file,
so it can find a page's middleware and name without running the page. When the page actually renders, these calls do nothing.
Anything after the first other statement isn't read as metadata.
POST to a GET-only page gives 405 with an Allow header. HTML forms can fake PUT/PATCH/DELETE with a hidden _method field.
Blade
Pages can be Blade templates. Install Blade (no Laravel app needed) and register the adapter:
Now pages/team/[member].blade.php is served at /team/{member}. Route params, $request, middleware, names,
_middleware.php, and error pages (_errors/404.blade.php) all work as they do for plain PHP pages. Layouts and partials go in _-folders,
so they never become routes: @extends('_layouts.app') loads pages/_layouts/app.blade.php.
Put metadata at the top in an @php block (or a plain `` block):
A Blade template can't return a response. Call respond() instead, which works from any page:
Also on the adapter: ->directive('money', fn ($e) => ""), ->share('appName', 'Acme'),
plus ->factory() and ->compiler() for anything else Blade can do.
Until a .blade.php renderer is registered, *.blade.php files are skipped. Their raw source is never served as PHP.
Other template engines
extension() takes any callable fn (string $file, array $vars): string|array|Response, so Twig, Latte or Plates can be plugged in the same way.
If an engine wraps exceptions, Quire still finds abort() and respond() inside them.
Middleware
A middleware is any callable fn (Request $request, Closure $next, ...$args) that returns a response,
usually $next($request). It can also be a class with a handle() method, or an invokable class.
'role:admin,editor' passes 'admin' and 'editor' as extra arguments.
There are four places to attach middleware. They run in this order (outermost first):
- Global:
$quire->middleware('timing') -
By path pattern on a mount (relative to its URI,
*is a wildcard): -
By folder: a
_middleware.phpthat returns an array. It covers that folder and every subfolder, parent folders first. - On the page:
middleware(...)at the top of the file.
Using a DI container? $quire->resolveUsing(fn (string $class) => $container->get($class));
Named routes & URLs
Mounts, prefixes, base path
Route priority
More specific routes win, segment by segment: static > [param] > [...catchall] > [[...optional]].
So users/create.php beats users/[id].php. If two pages map to the same route (users.php and users/index.php,
or [id].php and [slug].php in the same folder), Quire throws an error.
Errors
abort($status, $message) anywhere (page or middleware) renders _errors/{status}.php (such as _errors/404.php),
then _errors/error.php, from the mount's root folder. Either can be .blade.php. If neither exists, you get plain text.
Error pages get $status, $message, $exception and $request.
Uncaught exceptions become 500. Turn on $quire->debug() to see the real message (never in production).
Request & Response
Inside pages and middleware, Quire\request() returns the current request.
Testing
$quire->handle(Request::create('POST', '/login', ['user' => 'bob'], cookies: [...])) returns a Response without sending it.
See tests/run.php.
License
MIT. See LICENSE.