icanboogie / routing
Request routing
Requires
- php: >=8.2
- ext-json: *
- icanboogie/http: ^6.0
Requires (Dev)
- olvlvl/phpunit-given: ^11.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.4
- psr/container: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
The icanboogie/routing package handles URL rewriting in native PHP. A Request is mapped to a Route, which in turn is mapped to a Responder. If the process is successful a response is returned. Events are emitted along the way to allow listeners to alter the request or the response, or recover from failure.
Routes are defined with attributes on plain controller classes. Path, query string, and body values
are cast to the types of the action's parameters, uploaded files are provided as File, and routes
work both ways: they match requests and generate URLs.
<?php namespace App\Articles; use ICanBoogie\HTTP\File; use ICanBoogie\HTTP\Response; use ICanBoogie\Routing\Attribute\Get; use ICanBoogie\Routing\Attribute\Part; use ICanBoogie\Routing\Attribute\Post; use ICanBoogie\Routing\Attribute\Prefix; use ICanBoogie\Routing\Attribute\Query; use ICanBoogie\Routing\Attribute\Upload; use ICanBoogie\Routing\IntoResponse\Json; enum Status: string { case DRAFT = 'draft'; case PUBLISHED = 'published'; } #[Prefix(pattern: '/articles')] final readonly class ArticlesController { public function __construct( private ArticleRepository $articles, ) { } // GET /articles?page=2 #[Get(id: 'articles')] public function list(#[Query] int $page = 1): Json { return new Json($this->articles->page($page)); } // GET /articles/42 — "/articles/abc" doesn't match: 404 #[Get('/<id:\d+>', id: 'article')] public function show(int $id): Json { return new Json($this->articles->get($id)); } // GET /articles/status/draft — "/articles/status/foo" is a 404 #[Get('/status/:status')] public function by_status(Status $status): Json { return new Json($this->articles->with_status($status)); } // POST /articles, with a form or a JSON body — a missing "title" is a 400 #[Post] public function create(#[Part] string $title, #[Part] Status $status = Status::DRAFT): Json { return new Json($this->articles->create($title, $status), status: 201); } // POST /articles/42/cover, as multipart/form-data — a missing or failed upload of "cover" is a 400, // "thumb" is optional because the parameter is nullable #[Post('/<id:\d+>/cover')] public function set_cover(int $id, File $cover, #[Upload('thumb')] ?File $thumbnail): Json { return new Json($this->articles->set_cover($id, $cover, $thumbnail)); } }
Build the configuration, answer requests, and generate URLs:
<?php use ICanBoogie\HTTP\Request; use ICanBoogie\HTTP\Responder\DelegateToProvider; use ICanBoogie\Routing\ActionResponderProvider\FromConfig; use ICanBoogie\Routing\CacheStrategy; use ICanBoogie\Routing\ConfigBuilder; use ICanBoogie\Routing\RequestResponderProvider; /* @var \Psr\Container\ContainerInterface $container */ /* @var \ICanBoogie\Routing\UrlGenerator $url_generator */ $config = new ConfigBuilder() ->add_controller(App\Articles\ArticlesController::class) ->cache_strategy(CacheStrategy::COMPILE) // compiled routes for production, exportable with var_export() ->build(); $responders = new FromConfig($config, fn(string $class): object => $container->get($class)); $application = new DelegateToProvider(new RequestResponderProvider($config->routes, $responders)); $response = $application->respond(Request::from($_SERVER)); $response(); $url_generator->generate_url('article', [ 'id' => 42 ]); // "/articles/42" $url_generator->generate_url('articles'); // "/articles" $url_generator->generate_url('articles:by_status', [ 'status' => 'draft' ]); // "/articles/status/draft"
Other features include middleware and events to run code around actions, custom decoders to turn values into objects, and route providers that make route lookup fast.
Installation
composer require icanboogie/routing
Documentation
New to the package? Follow Getting started. The full index is in docs/.
- Routes: Route patterns · Defining routes · Route providers · URL generation
- Handling requests: Using the Router · Responding to a request · Action responders · Middleware and events
- Controllers: Stateless controllers · Controllers · Action controllers
- Reference: Exceptions
Continuous Integration
The project is continuously tested by GitHub Actions.
Code of Conduct
This project adheres to a Contributor Code of Conduct. By participating in this project and its community, you're expected to uphold this code.
Contributing
See CONTRIBUTING for details.