Flight routing is a simple, fast PHP router that is easy to get integrated with other routers.

Overview

The PHP HTTP Flight Router

Latest Version Software License Workflow Status Code Maintainability Coverage Status Quality Score Sponsor development of this project

divineniiquaye/flight-routing is a HTTP router for PHP 7.1+ based on PSR-7 and PSR-15 with support for annotations, created by Divine Niiquaye. This library helps create a human friendly urls (also more cool & prettier) while allows you to use any current trends of PHP Http Router implementation and fully meets developers' desires.

Xcode

🏆 Features

  • Basic routing (GET, POST, PUT, PATCH, UPDATE, DELETE) with support for custom multiple verbs.
  • Regular Expression Constraints for parameters.
  • Named routes.
  • Generating named routes to PSR-15 URL.
  • Route groups.
  • PSR-15 Middleware (classes that intercepts before the route is rendered).
  • Namespaces.
  • Advanced route pattern syntax.
  • Sub-domain routing and more.
  • Restful Routing
  • Custom matching strategy

📦 Installation & Basic Usage

This project requires PHP 7.2 or higher. The recommended way to install, is via Composer. Simply run:

$ composer require divineniiquaye/flight-routing

First of all, you need to configure your web server to handle all the HTTP requests with a single PHP file like index.php. Here you can see required configurations for Apache HTTP Server and NGINX.

Setting up Nginx:

If you are using Nginx please make sure that url-rewriting is enabled.

You can easily enable url-rewriting by adding the following configuration for the Nginx configuration-file for the demo-project.

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

Setting up Apache:

Nothing special is required for Apache to work. We've include the .htaccess file in the public folder. If rewriting is not working for you, please check that the mod_rewrite module (htaccess support) is enabled in the Apache configuration.


    Options -MultiViews
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php [QSA,L]



    
        RedirectMatch 307 ^/$ /index.php/
    

Setting up IIS:

On IIS you have to add some lines your web.config file. If rewriting is not working for you, please check that your IIS version have included the url rewrite module or download and install them from Microsoft web site.

">
xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <defaultDocument>
            <files>
                <remove value="index.php" />
                <add value="index.php" />
            files>
        defaultDocument>
        <rewrite>
            
            <rules>
                <rule name="request_filename" stopProcessing="true">
                    <match url="^.*$" ignoreCase="true" />
                    
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
                    conditions>
                    <action type="Rewrite" url="index.php" />
                rule>
            rules>
        rewrite>
    system.webServer>
    <system.web>
        <httpRuntime requestPathInvalidCharacters="<,>,*,%,&,\,?" />
    system.web>
configuration>

Configuration


Please note that the following documentation only covers how to use this router in a project without an existing framework using DefaultMatcher class. If you are using a framework or/and a different Flight\Routing\Interfaces\RouteMatcherInterface class instance in your project, the implementation varies.

It's not required, but you can set namespace for classes eg: 'Demo\\Controllers\\'; to prefix all routes with the namespace to your controllers. This will simplify things a bit, as you won't have to specify the namespace for your controllers on each route.

This library uses any PSR-7 implementation, for the purpose of this tutorial, we wil use biurad-http-galaxy library to provide PSR-7 complaint request, stream and response objects to your controllers and middleware

run this in command line if the package has not be added.

composer require biurad/http-galaxy

Flight routing allows you to call any controller action with namespace using * pattern, also you have have domain on route pattern using // followed by the host and path, or add a scheme to the pattern.

For dispatching a router, use an instance of Laminas\HttpHandlerRunner\Emitter\EmitterInterface to dispatch the router.

use Flight\Routing\{Router, RouteCollection};
use Biurad\Http\Factory\GuzzleHttpPsr7Factory as Psr17Factory;
use Laminas\HttpHandlerRunner\Emitter\SapiStreamEmitter;

$collector = new RouteCollection();

// Add routes
$route = $collector->get('/phpinfo', 'phpinfo'); // Will create a phpinfo route.

// Incase you want to name the route use `bind` method
$route->bind('phpinfo');

// Need to have an idea about php before using this dependency, though it easy to use.
$psr17Factory = new Psr17Factory();

$router = new Router($psr17Factory, $psr17Factory);

/**
 * The default namespace for route-callbacks, so we don't have to specify it each time.
 * Can be overwritten by using the namespace config option on your routes.
 */
$router->setOptions(['namespace' => 'Demo\\Controllers\\']);

// Incase you working with API and need a response served on HTTP OPTIONS request method.
$router->setOptions(['options_skip' => true]);

// All router configurations should be set before adding routes
$router->addRoute(...$collector->getRoutes());

// Start the routing
(new SapiStreamEmitter())->emit($router->handle(Psr17Factory::fromGlobalRequest()));

NOTE: If your handler return type isn't instance of ResponseInterface, FLight Routing will choose the best content-type for http response. Returning strings can be a bit of conflict for Flight routing, so it fallback is "text/html", a plain text where isn't xml or doesn't have a ... wrapped around contents will return a content-type of text/plain.

The Route class can accept a handler of type Psr\Http\Server\RequestHandlerInterface, callable,invocable class, or array of [class, method]. Simply pass a class or a binding name instead of a real object if you want it to be constructed on demand.

Loading Annotated Routes


This library is shipped with annotations support, check Annotation directory to find out more about collecting anotations using Flight\Routing\Router::loadAnnotation method.

use Biurad\Annotations\AnnotationLoader;
use Biurad\Http\Factory\GuzzleHttpPsr7Factory as Psr17Factory;
use Flight\Routing\Annotation\Listener;
use Flight\Routing\Router;
use Spiral\Attributes\AnnotationReader;
use Spiral\Attributes\AttributeReader;
use Spiral\Attributes\Composite\MergeReader;

$loader = new AnnotationLoader(new MergeReader([new AnnotationReader(), new AttributeReader()]));
$loader->attachListener(new Listener());

$loader->attach(
    'src/Controller',
    'src/Bundle/BundleName/Controller',
];

// Need to have an idea about php before using this dependency, though it easy to use.
$psr17Factory = new Psr17Factory();

$router = new Router($psr17Factory, $psr17Factory);
$router->loadAnnotation($loader);

Basic Routing


As stated earlier, this documentation for route pattern is based on DefaultMatcher class. Route pattern are path string with curly brace placeholders. Possible placeholder format are:

  • {name} - required placeholder.
  • {name=} - placeholder with default value.
  • {name:regex} - placeholder with regex definition.
  • {name:regex=} - placeholder with regex definition and default value.
  • [{name}] - optionnal placeholder.

Variable placeholders may contain only word characters (latin letters, digits, and underscore) and must be unique within the pattern. For placeholders without an explicit regex, a variable placeholder matches any number of characters other than '/' (i.e [^/]+).

NB: Do not use digit for placeholder or it's value shouldn't be greater than 31 characters.

Examples:

  • /foo/ - Matches only if the path is exactly '/foo/'. There is no special treatment for trailing slashes, and patterns have to match the entire path, not just a prefix.
  • /user/{id} - Matches '/user/bob' or '/user/1234!!!' but not '/user/' or '/user' or even '/user/bob/details'.
  • /user/{id:[^/]+} - Same as the previous example.
  • /user[/{id}] - Same as the previous example, but also match '/user'.
  • /user[/{id}]/ - Same as the previous example, but also match '/user/'.
  • /user/{id:[0-9a-fA-F]{1,8}} - Only matches if the id parameter consists of 1 to 8 hex digits.
  • /files/{path:.*} - Matches any URL starting with '/files/' and captures the rest of the path into the parameter 'path'.

Below is a very basic example of setting up a route. First parameter is the url which the route should match - next parameter is a Closure or callback function that will be triggered once the route matches.

use Flight\Routing\Route;

$route = new Route('/', 'GET|HEAD', fn () => 'Hello world'});

// Create a new route using $router.
$router->addRoute($route);

Incase you do not want to use the Flight\Routing\Router class, Flight Routing provides option to use only the DefaultMatcher while skipping the rest of routes handling processes.

use Flight\Routing\{Route, Router, RouteCollection};
use Flight\Routing\Matchers\{SimpleRouteMatcher, SimpleRouteDumper};
use Biurad\Http\Factory\GuzzleHttpPsr7Factory as Psr17Factory;

$route = new Route('/blog/{slug}', 'GET', BlogController::class);

$routes = new RouteCollection();
$routes->add($route->bind('blog_show'));


$matcher = new SimpleRouteMatcher($routes); // A simple matcher for matching routes
// or
// $matcher = new SimpleRouteDumper($routes); A symfony's style of dumping and matching routes.

// Routing can match routes with incoming requests
$matchedRoute = $matcher->match(new ServerRequest(Router::METHOD_GET, '/blog/lorem-ipsum'));

// Will match and return a $route with new aeguments accesed from $route->getArguments() method.
// [ 'slug' => 'lorem-ipsum']

// Routing can also generate URLs for a given route
$url = $matcher->generateUri('blog_show', [
    'slug' => 'my-blog-post',
]);
// $url = '/blog/my-blog-post'

Closure Handler


It is possible to pass the closure as route handler, in this case our callable will receive two arguments: Psr\Http\Message\ServerRequestInterface and Psr\Http\Message\ResponseInterface by default, including from PSR-11 container services. The problem with routes with closure handlers are that, it cannot be cached or serialized.

addRoute($route); )); ">
use Flight\Routing\Route;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

$route = new Route(
    '/{name}',
    'GET|HEAD',
    function (ServerRequestInterface $request, ResponseInterface $response) {
        $response->getBody()->write("hello world");

        return $response;
    }
);


$router->addRoute($route);
));

Route Request


You can catch the request object like this example:

use Biurad\Http\Response\{EmptyResponse, JsonResponse};
use Flight\Routing\RouteCollection;
use Psr\Http\Message\ServerRequestInterface;

$collector = new RouteCollection();

$collector->get(
    '/',
    function (ServerRequestInterface $request) {
        return new JsonResponse([
            'method'            => $request->getMethod(),
            'uri'               => $request->getUri(),
            'body'              => $request->getBody(),
            'parsedBody'        => $request->getParsedBody(),
            'headers'           => $request->getHeaders(),
            'queryParameters'   => $request->getQueryParams(),
            'attributes'        => $request->getAttributes(),
        ]);
    }
);

$collector->post(
    '/blog/posts',
    function (ServerRequestInterface $request) {
        $post           = new \Demo\Models\Post();
        $post->title    = $request->getQueryParams()['title'];
        $post->content  = $request->getQueryParams()['content'];
        $post->save();

        return new EmptyResponse(201);
    }
);

$router->addRoute(...$collector->getRoutes()); // Add new collection list to $router

Route Response


The example below illustrates supported kinds of responses.

use Biurad\Http\Response\{EmptyResponse, HtmlResponse, JsonResponse, TextResponse, RedirectResponse};
use Flight\Routing\RouteCollection;

$collector = new RouteCollection();

$collector
    ->get(
        '/html/1',
        function () {
            return 'This is an HTML response';
        }
    );
$collector
    ->get(
        '/html/2',
        function () {
            return new HtmlResponse('This is also an HTML response', 200);
        }
    );
$collector
    ->get(
        '/json',
        function () {
            return new JsonResponse(['message' => 'Unauthorized!'], 401);
        }
    );
$collector
    ->get(
        '/text',
        function () {
            return new TextResponse('This is a plain text...');
        }
    );
$collector
    ->get(
        '/empty',
        function () {
            return new EmptyResponse();
        }
    );

// In case of needing to redirecting user to another URL
$collector
    ->get(
        '/redirect',
        function () {
            return new RedirectResponse('https://biurad.com');
        }
    );

$router->addRoute(...$collector->getRoutes()); // Add new collection list to $router

Available Methods in RouteCollection

Here you can see how to declare different routes with different http methods:

use Flight\Routing\RouteCollection;

$collector = new RouteCollection();

$collector
    ->head('/', function () {
        return 'HEAD method';
    });
$collector
    ->get('/', function () {
        return 'GET method';
    });
$collector
    ->post('/', function () {
        return 'POST method';
    });
$collector
    ->patch('/', function () {
        return 'PATCH method';
    });
$collector
    ->put('/', function () {
        return 'PUT method';
    });
$collector
    ->options('/', function () {
        return 'OPTIONS method';
    });
$collector
    ->delete('/', function () {
        return 'DELETE method';
    });

$router->addRoute(...$collector->getRoutes()); // Add new collection list to $router

Multiple HTTP-Verbs


Sometimes you might need to create a route that accepts multiple HTTP-verbs. If you need to match all HTTP-verbs you can use the any method.

$collector->addRoute('/', 'get|post', function() {
  // ...
});

$collector->any('foo', function() {
  // ...
});

Route Pattern and Parameters


You can use route pattern to specify any number of required and optional parameters, these parameters will later be passed to our route handler via ServerRequestInterface attribute Flight\Routing\Route::class.

Use the {parameter_name:pattern} form to define a route parameter, where pattern is a regexp friendly expression. You can omit pattern and just use {parameter_name}, in this case the parameter will match [^\/]+.

Required Parameters


You'll properly wondering by know how you parse parameters from your urls. For example, you might want to capture the users id from an url. You can do so by defining route-parameters.

$collector->get('/user/{userId}', function ($userId) {
  return 'User with id: ' . $userId;
});

You may define as many route parameters as required by your route:

$collector->get('/posts/{postId}/comments/{commentId}', function ($postId, $commentId) {
  // ...
});

Optional Parameters


Occasionally you may need to specify a route parameter, but make the presence of that route parameter optional. Use [] to make a part of route (including the parameters) optional, for example:

// Optional parameter
$collector->get('/user[/{name}]', function ($name = null) {
  return $name;
});
//or
$collector->get('/user[/{name}]', function ($name) {
  return $name;
});
// Optional parameter with default value
$collector->get('/user/[{name}]', function ($name = 'Simon') {
  return $name;
});
//or
$collector->get('/user/[{name=}]', function ($name) {
  return $name;
});
//or with rule
$collector->get('/user/[{name:\w+=}]', function ($name) {
  return $name;
});

Obviously, if a parameter is inside an optional sequence, it's optional too and defaults to null. Sequence should define it's surroundings, in this case a slash which must follow a parameter, if set. The technique may be used for example for optional language subdomains:

$collector->get('//[{lang=}.]example.com/hello', ...);

Sequences may be freely nested and combined:

$collector->get('[{lang:[a-z]{2}}[-{sublang}]/]{name}[/page-{page=<0>}]', ...);

// Accepted URLs:
// /cs/hello
// /en-us/hello
// /hello
// /hello/page-12
// /ru/hello/page-12

Note: Route parameters are always encased within {} braces and should consist of alphabetic characters. Route parameters may not contain a - character. Use an underscore (_) instead.

Regular Expression Constraints


You may constrain the format of your route parameters using the where method on a route instance. The where method accepts the name of the parameter and a regular expression defining how the parameter should be constrained:

$collector->get('/user/{name}', function ($name) {
    //
})->assert('name', '[A-Za-z]+');

$collector->get( '/user/{id}', function (int $id) {
    //
})->assert('id', '[0-9]+');

$collector->get('/user/{id}/{name}', function (int $id, string $name) {
    //
})->assert('id', '[0-9]+')->assert('name', '[a-z]+');

$collector->get('/user/{id:[0-9]+}/{name:[a-z]+}', function (int $id, string $name) {
    //
});

Named Routes


Named routes allow the convenient generation of URLs or redirects for specific routes. It is mandatory to specify a name for a route by chaining the name onto the first argument of route definition:

$collector->get('/user/profile', function () {
    // Your code here
}->bind('profile');

You can also specify names for grouping route with a prefixed name:

use Flight\Routing\RouteCollection;

$collector->group('user.', function (RouteCollection $group) {
    $group->get('/user/profile', 'UserController@profile')->bind('profile');
}); // Will produce "user.profile"

Generating URLs From Named Routes


URL generator tries to keep the URL as short as possible (while unique), so what can be omitted is not used. The behavior of generating urls from route depends on the respective parameters sequence given.

Once you have assigned a name to a given route, you may use the route's name, its parameters and maybe add query, when generating URLs:

// Generating URLs...
$url = $router->generateUri('profile');

If the named route defines parameters, you may pass the parameters as the second argument to the url function. The given parameters will automatically be inserted into the URL in their correct positions:

generateUri('profile', [1]); // will produce "user/1/profile" ">
$collector->get('/user/{id}/profile', function ($id) {
    //
})->bind('profile');

$url = $router->generateUri('profile', ['id' => 1]); // will produce "user/1/profile"
// or
$url = $router->generateUri('profile', [1]); // will produce "user/1/profile"

Route Groups


Route groups allow you to share route attributes, such as middlewares, namespace, domain, name, prefix, patterns, or defaults, across a large number of routes without needing to define those attributes on each individual route. Shared attributes are specified in route method prefixed with a with name to the $collector->group method.

use Flight\Routing\Interfaces\RouteCollection;

$group = $collector->group(
    'group_name',
    function (RouteCollection $route) {
        // Define your routes using $route...
    }
);

// eg: $group->withPrefix(...), $group->withMethod(...), etc.

Route Middlewares


Router supports middleware, you can use it for different purposes like authentication, authorization, throttles and so forth. Middleware run before controllers and it can check and manipulate http requests. To associate route specific middleware use addMiddleware, you can access route parameters via arguments attribute of the request object:

Here you can see the request lifecycle considering some middleware:

Input --[Request]↦ Router ↦ Middleware 1 ↦ ... ↦ Middleware N ↦ Controller
                                                                      ↧
Output ↤[Response]- Router ↤ Middleware 1 ↤ ... ↤ Middleware N ↤ [Response]

We using using [laminas-stratigility] to allow better and saver middleware usage.

run this in command line if the package has not be added.

composer require laminas/laminas-stratigility

To declare a middleware, you must implements Middleware Psr\Http\Server\MiddlewareInterface interface.

Middleware must have a process() method that catches http request and a closure (which runs the next middleware or the controller) and it returns a response at the end. Middleware can break the lifecycle and return a response itself or it can run the $handler implementing Psr\Http\Server\RequestHandlerInterface to continue lifecycle.

For example see the following snippet. In this snippet, we will demonstrate how a middleware works:

use Demo\Middleware\ParamWatcher;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Flight\Routing\Route;

$collector->get(
    'watch',
    '/{param}',
    function (ServerRequestInterface $request, ResponseInterface $response) {
        return $request->getAttribute(Route::class)->getArguments();
    }
))
->middleware(ParamWatcher::class);

where ParamWatcher is:

namespace Demo\Middleware;


use Flight\Routing\Route;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Biurad\Http\Exceptions\ClientException\UnauthorizedException;

class ParamWatcher implements MiddlewareInterface
{
    public function process(Request $request, RequestHandlerInterface $handler): Response
    {
        $arguments = $request->getAttribute(Route::class)->getAttributes();

        if ($arguments['param'] === 'forbidden') {
           throw new UnauthorizedException();
        }

        return $handler->handle($request);
    }
}

This route will trigger Unauthorized exception on /forbidden.

You can add as many middlewares as you want. Middlewares can be implemented using closures but it doesn’t make sense to do so!

Multiple Routes


Flight Routing increases SEO (search engine optimization) as it prevents multiple URLs to link to different content (without a proper redirect). If more than one addresses link to the same target, the router choices the first (makes it canonical), (NB: static routes priority is highest and runned first), while the other routes are later reached. Thanks to that your page won't have duplicities on search engines and their rank won't be split.

Router will match all routes in the order they were registered. Make sure to avoid situations where previous route matches the conditions of the following routes. Also static routes are addressed first before any other type of route.

use Flight\Routing\Route;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

// this route will be trigger after static routes.
$collector->get(
    '/{param}',
    function (ServerRequestInterface $request, ResponseInterface $response) {
        return $request->getAttribute(Route::class)->getAttributes();
    }
))

// this route will be trigger first
$collector->get(
    '/hello',
    function (ServerRequestInterface $request, ResponseInterface $response) {
        return $request->getAttribute(Route::class)->getAttributes();
    }
))

Subdomain Routing


Route groups may also be used to handle sub-domain routing. The sub-domain may be specified using the domain key on the group attribute array:

use Flight\Routing\Interfaces\RouteCollection;

// Domain
$collector->get('/', 'Controller@method')->domain('domain.com');

// Subdomain
$collector->get('/', 'Controller:method')->domain('server2.domain.com');

// Subdomain regex pattern
$collector->get('/', ['Controller', 'method'])->domain('{accounts:.*}.domain.com');

$collector->group(function (RouteCollection $route) {
    $route->get('/user/{id}', function ($id) {
        //
    });
})->domain('account.myapp.com');

RESTful Routing


All of Flight\Routing\Route has a restful implementation, which specifies the method selection behavior. Add a api:// scheme to a route path, or use Flight\Routing\RouteCollection::resource method to automatically prefix all the methods in Flight\Routing\Router::HTTP_METHODS_STANDARD with HTTP verb. you can also set _api to for prefixed methods, using Route's default method.

For example, we can use the following controller:

namespace Demo\Controller;

class UserController
{
    public function getUser(int $id): string
    {
        return "get {$id}";
    }

    public function postUser(int $id): string
    {
        return "post {$id}";
    }

    public function deleteUser(int $id): string
    {
        return "delete {$id}";
    }
}

Add route using Flight\Routing\Router::addRoute:

use Demo\UserController;

$router->addRoute(new Route('api://user/user/{id:\d+}', 'GET|POST', UserController::class));

// The api:// means, the route is restful, the first "user" is to be prefixed on class object method. Eg: getUser() and the last part means its gonna be served on uri: /user/23

Add route using Flight\Routing\RouteCollection::resource:

use Demo\UserController;

$collector->resource('user', '/user/{id:\d+}', UserController::class);

Invoking /user/1 with different HTTP methods will call different controller methods. Note, you still need to specify the action name.

Custom Route Matcher


If these offered route pattern do not fit your needs, you may create your own route matcher and add it to router. Router is nothing more than an implementation of RouteMatcherInterface with its three methods:

use Flight\Routing\Interfaces\RouteInterface;
use Flight\Routing\RouteCollection;
use Flight\Routing\Traits\ValidationTrait;
use Flight\Routing\Interfaces\RouteMatcherInterface;
use Psr\Http\Message\ServerRequestInterface;

class MyRouteMatcher implements RouteMatcherInterface
{
    use ValidationTrait;

    /** @var Route[] */
    private $routes;

    /**
     * @param RouteCollection|Route[] $collection
     */
    public function __construct($collection)
    {
        if ($collection instanceof RouteCollection) {
            // compile routes from $collection->getRoutes() for matching against request
            $collection = $collection->getRoutes();
        }

        $this->routes = $collection;
        // You can add your compiler instance here, if any.
    }

    /**
     * {@inheritdoc}
     */
    public function match(ServerRequestInterface $request): ?Route
    {
        $requestUri    = $request->getUri();
        $requestMethod = $request->getMethod();
        $requestPath   = \rawurldecode($this->resolvePath($request));

        foreach ($this->routes as $route) {
            // ... compile the route and match using $requestUri, $requestMethod and $requestPath.
        }

        // Return null if route wasn't matched.
        return null;
    }

	/**
     * {@inheritdoc}
     */
    public function generateUri(string $routeName, array $parameters = [], array $queryParams = [])
    {
        static $uriRoute;

        // If this route matcher support caching, then name can be found as key in $this->routes.
        // else you can remove it and only work with code inside the `else` statement.
        if (isset($this->routes[$routeName])) {
            $uriRoute = $this->routes[$routeName];
        } else {
            foreach ($this->routes as $route) {
                if ($routeName === $route->getName()) {
                    $uriRoute = $route;

                    break;
                }
            }
        }

        // ... generate a named route path using $parameters and $queryParams from $uriRoute
    }
}

If your custom route matcher support caching you can create a new class implementing MatchDumperInterface, then extend it to your newly created route matcher.

📓 Documentation

For in-depth documentation before using this library.. Full documentation on advanced usage, configuration, and customization can be found at docs.biurad.com.

Upgrading

Information on how to upgrade to newer versions of this library can be found in the UPGRADE.

🏷️ Changelog

SemVer is followed closely. Minor and patch releases should not introduce breaking changes to the codebase; See CHANGELOG for more information on what has changed recently.

Any classes or methods marked @internal are not intended for use outside of this library and are subject to breaking changes at any time, so please avoid using them.

🛠️ Maintenance & Support

When a new major version is released (1.0, 2.0, etc), the previous one (0.19.x) will receive bug fixes for at least 3 months and security updates for 6 months after that new release comes out.

(This policy may change in the future and exceptions may be made on a case-by-case basis.)

Professional support, including notification of new releases and security updates, is available at Biurad Commits.

??‍♀️ Contributing

To report a security vulnerability, please use the Biurad Security. We will coordinate the fix and eventually commit the solution in this project.

Contributions to this library are welcome, especially ones that:

  • Improve usability or flexibility without compromising our ability to adhere to PSR-7 and PSR-15
  • Optimize performance
  • Fix issues with adhering to PSR-7, PSR-15 and this library

Please see CONTRIBUTING for additional details.

🧪 Testing

$ composer test

This will tests biurad/php-cache will run against PHP 7.2 version or higher.

👥 Credits & Acknowledgements

This code is partly a reference implementation of Sunrise Http Router which is written, maintained and copyrighted by Anatoly Fenric. This project new features starting from version 1.0 was referenced from Sunrise Http Router

🙌 Sponsors

Are you interested in sponsoring development of this project? Reach out and support us on Patreon or see https://biurad.com/sponsor for a list of ways to contribute.

📄 License

divineniiquaye/flight-routing is licensed under the BSD-3 license. See the LICENSE file for more details.

🏛️ Governance

This project is primarily maintained by Divine Niiquaye Ibok. Members of the Biurad Lap Leadership Team may occasionally assist with some of these duties.

🗺️ Who Uses It?

You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us an email or message mentioning this library. We publish all received request's at https://patreons.biurad.com.

Check out the other cool things people are doing with divineniiquaye/flight-routing: https://packagist.org/packages/divineniiquaye/flight-routing/dependents

Comments
Releases(v2.1.0)
  • v2.1.0(Dec 8, 2022)

  • v2.0.11(Nov 7, 2022)

    What's Changed

    • Fixed missing default value in regex issue by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/39

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v2.0.10...v2.0.11

    Source code(tar.gz)
    Source code(zip)
  • v2.0.10(Nov 3, 2022)

    What's Changed

    • Added route path prefixing freedom #37 by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/38
    • Improved route validation performance 80abb600fe15f01b16877eab6ed383d1b7f942da @divineniiquaye
    • Improved no-cached routes performance e3cb2e39bac8048e351129a761f30b57db27e6de @divineniiquaye

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v2.0.2...v2.0.10

    Source code(tar.gz)
    Source code(zip)
  • v2.0.2(Oct 11, 2022)

    Hotfix

    • Fixed issue prototyping route's collection methods arguments @divineniiquaye
    • Updated symfony's polyfill-php from 80 to 81 @divineniiquaye

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v2.0.1...v2.0.2

    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Oct 10, 2022)

    Hotfix

    • Fixed prefix group merging issue in the route collection's class @divineniiquaye

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v2.0.0...v2.0.1

    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Oct 10, 2022)

    What's Changed

    • [BC BREAK] Removed the route class to use array instead of object
    • [BC BREAK] Removed the route matcher class to use only the Flight\Routing\Router class for route matching
    • [BC BREAK] Removed the buildRoutes method from the route collection class, use the getRoutes method directly
    • [BC BREAK] Removed the getRoute method from the route collection class, use the offGet method instead
    • [BC BREAK] Removed the routes method from the route collection class with no replacement
    • [BC BREAK] Removed the addRoute method from the route collection class, use the add method instead
    • [BC BREAK] Removed the isCached and addRoute methods from the default router class
    • [BC BREAK] Removed classes, traits and class methods which are unnecessary or affects performance of routing
    • [BC BREAK] Improved the route collection class to use array based routes instead of objects
    • [BC BREAK] Improved how the default route handler handles array like callable handlers
    • [BC BREAK] Replaced the route matcher implementation in the router class for compiler's implementation instead
    • [BC BREAK] Replaced unmatched route host exception to instead return null and a route not found exception
    • [BC BREAK] Renamed the Flight\Routing\Generator\GeneratedUri class to Flight\Routing\RouteUri
    • Removed symfony/var-exporter library support from caching support, using PHP var-export function instead
    • Added a new FileHandler handler class to return contents from a valid file as PSR-7 response
    • Added new sets of requirements to the Flight\Routing\RouteCompiler::SEGMENT_TYPES constant
    • Added a offGet method to the route collection class for finding route by it index number
    • Added PHP Countable support to the route collection class, for faster routes count
    • Added PHP ArrayAccess support to the route collection class for easier access to routes
    • Added support for the default route compiler placeholder's default rule from \w+ to .*?
    • Added a static export method to the default router class to export php values in a well formatted way for caching
    • Improved the route annotation's listener and attribute class for better performance
    • Improved the default route matcher's generateUri method reversing a route path and strictly matching parameters
    • Improved the default route matcher's class Flight\Routing\Router::match() method for better performance
    • Improved the default route handler's class for easier extending of route handler and arguments rendering
    • Improved the default route handler's class ability to detect content type of string
    • Improved and fixed route namespacing issues
    • Improved thrown exceptions messages for better debugging of errors
    • Improved the sorting of routes in the route's collection class
    • Improved the README.md doc file for better understanding on how to use this library
    • Improved coding standard in making the codebase more readable
    • Improved benchmarking scenarios for better performance comparison
    • Improved performance tremendously, see Benchmark Results
    • Updated all tests units rewritten with pestphp/pest for easier maintenance and improved benchmarking
    • Updated minimum requirement for installing this library to PHP 8.0
    • Version 2 Release by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/36

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v1.6.4...v2.0.0

    Source code(tar.gz)
    Source code(zip)
  • v1.6.4(Jun 1, 2022)

  • v1.6.3(May 29, 2022)

  • v1.6.2(May 24, 2022)

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v1.6.1...v1.6.2

    Fixed issue with the route collection class unable to resolve prototyped routes

    Source code(tar.gz)
    Source code(zip)
  • v1.6.1(May 18, 2022)

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v1.6.0...v1.6.1

    It is required/recommended to update to this version or later as it has better performance than all previous versions.

    Source code(tar.gz)
    Source code(zip)
  • v1.6.0(May 9, 2022)

  • v1.5.1(Apr 17, 2022)

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v1.5.0...v1.5.1

    It is required/recommended to update to this version or later.

    Source code(tar.gz)
    Source code(zip)
  • v1.5.0(Apr 14, 2022)

  • v1.4.1(Apr 1, 2022)

  • v1.4.0(Mar 12, 2022)

    What's Changed

    • Fixed major issues with Publisher class namespace by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/10
    • Update Dependabot config file by @dependabot-preview in https://github.com/divineniiquaye/flight-routing/pull/11
    • Fixed performance in route collection class by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/15
    • Fixed annotations loading from routes by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/14
    • Performance Update by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/16
    • Improved issues & config templates in .github folder by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/17
    • Optimized route matcher to be 22% better #19 by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/20
    • Improved the route collection class by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/26
    • fix: errors encountered in compiled regex from routes by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/34
    • fix: cached route match causing memory leak by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/33
    • feat: custom route matcher support by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/31

    New Contributors

    • @dependabot-preview made their first contribution in https://github.com/divineniiquaye/flight-routing/pull/11

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v1.0.0...v1.4.0

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Dec 21, 2020)

    What's Changed

    • Fixed major issues with Publisher class namespace by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/10
    • Update Dependabot config file by @dependabot-preview in https://github.com/divineniiquaye/flight-routing/pull/11
    • Merge original commits from develop branch by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/12
    • Has a lot of breaking changes, renamed a few classes, added new classes for new features, and removed a few classes.
    • Added restful routing, PHP 8 support and increased performance almost 50% since previous version.

    New Contributors

    • @dependabot-preview made their first contribution in https://github.com/divineniiquaye/flight-routing/pull/11

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v0.5.2...v1.0.0

    Source code(tar.gz)
    Source code(zip)
  • v0.5.2(Jul 31, 2020)

    • Fixed major issues with Publisher class namespace

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v0.5.1...v0.5.2

    Source code(tar.gz)
    Source code(zip)
  • v0.5.1(Jul 29, 2020)

  • v0.5.0(Jul 24, 2020)

    What's Changed

    • Delete .scrutinizer.yml by @cyclonecopp in https://github.com/divineniiquaye/flight-routing/pull/9
    • Updated php version to 7.1 for stable release and dev release @divineniiquaye in #6bc2765
    • Fixed minor issues with parsing static method as string @divineniiquaye in #ab593da

    New Contributors

    • @cyclonecopp made their first contribution in https://github.com/divineniiquaye/flight-routing/pull/9

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v0.2.9...v0.5.0

    Source code(tar.gz)
    Source code(zip)
  • v0.2.9(Jun 23, 2020)

    What's Changed

    • Fixed php files end of line to "\n" @divineniiquaye in #7435f94
    • Added calendar (year, month, day) segment types to default compiler class @divineniiquaye in #70083eb
    • Added performance improvements to the whole router.

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v0.2.8...v0.2.9

    Source code(tar.gz)
    Source code(zip)
  • v0.2.8(Jun 11, 2020)

    What's Changed

    • Apply fixes from StyleCI by @divineniiquaye in https://github.com/divineniiquaye/flight-routing/pull/7
    • Fixed minor issue apending namespace to class @divineniiquaye in #6810449

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v0.2.7...v0.2.8

    Source code(tar.gz)
    Source code(zip)
  • v0.2.7(Jun 7, 2020)

    What's Changed

    • Scrutinizer Auto-Fixes by @scrutinizer-auto-fixer in https://github.com/divineniiquaye/flight-routing/pull/6
    • Improved code complexity to add some extra performance @divineniiquaye
    • Removed some unnecessary methods from the route collector class @divineniiquaye
    • Renamed dispatch() method to handle method requiring psr-7 server request interface @divineniiquaye

    New Contributors

    • @scrutinizer-auto-fixer made their first contribution in https://github.com/divineniiquaye/flight-routing/pull/6

    Full Changelog: https://github.com/divineniiquaye/flight-routing/compare/v0.2.5...v0.2.7

    Source code(tar.gz)
    Source code(zip)
  • v0.2.5(May 21, 2020)

  • v0.2.4(May 21, 2020)

    Added

    • Added Flight\Routing\Traits\GroupsTrait and Flight\Routing\Traits\PathsTrait
    • Added tests for Flight\Routing\RouteGroup, Flight\Routing\RouteCollector::generateUri() and more

    Changed

    • Made changes to CHANGELOG.md file
    • Made changes to Flight\Routing\RouteResults::handle()
    • Moved a few methods from Flight\Routing\Route to new paths and groups traits

    Fixed

    • Fixed major issues with weak route grouping
    • Fixed major issues with faulty route uri generation
    • Improved code quality, applied PSR fixtures, and routing performance

    Removed

    • Deleted deprecated class Flight\Routing\Middlewares\RouteRunnerMiddleware
    • Deleted deprecated class Flight\Routing\RouteResource
    • Deleted Flight\Routing\RouteMiddleware class, in favor of Flight\Routing\Middlewares\MiddlewareDisptcher class
    • Deleted Flight\Routing\Interfaces\ResourceController interface, since Flight\Routing\RouteResource doesn't exists
    • Removed argument ['6'] from Flight\Routing\Route class
    Source code(tar.gz)
    Source code(zip)
  • v0.2.2(May 17, 2020)

  • v0.2.1(May 16, 2020)

    Changed

    • Made changes to composer.json file
    • Made changes to .travis.yml file
    • Made changes to README.md file

    Fixed

    • Fixed major issues with Psr\Log\LoggerInterface class not found
    Source code(tar.gz)
    Source code(zip)
  • v0.2.0(May 16, 2020)

    Added

    • Added Flight\Routing\Concerns\CallableHandler class for content-type detection
    • Added Flight\Routing\Exceptions\UriHandlerException class
    • Added serializable support for Flight\Routing\Route class
    • Added Flight\Routing\Middlewares\MiddlewareDisptcher class for handling middleware
    • Added ability to match domain and scheme from path.
    • Added ability to match controller and method on a path
    • Added phpunit tests

    Changed

    • Renamed Flight\Routing\Services\SymfonyRouteCompiler class to Flight\Routing\Services\SimpleRouteCompiler
    • Improved how routes are handled and dispatched (has breaking changes)
    • Made changes to several classes for new route dispatching
    • Made changes to CHANGELOG.md file

    Fixed

    • Improved performance of routing x1.5
    • Fixed minor issues with handling routes

    Removed

    • Moved most methods from Flight\Routing\Route class to traits in Traits folder
    • Marked Flight\Routing\RouteResource class as deprecated
    Source code(tar.gz)
    Source code(zip)
  • v0.1.1(May 6, 2020)

    Changed

    • Made changes to README.md file
    • Made changes to CHANGELOG.md file
    • Made changes to .scrutinizer.yml file

    Fixed

    • Fixed major issues with Flight\Routing\Concerns\CallableResolver::addInstanceToClosure() for PHP 7.1
    • Fixed major issues with Flight\Routing\Interfaces\CallableResolverInterface::addInstanceToClosure() for PHP 7.1
    Source code(tar.gz)
    Source code(zip)
  • v0.1.0(May 2, 2020)

    Added

    • Added license scan report and status to README.md file
    • Added Flight\Routing\Exceptions\MethodNotAllowedException class
    • Added extra support for route grouping
    • Added new methods to most classes and minimal complexity and performance

    Changed

    • Changed how routes are dispatched. (has breaking changes)
    • Made changes to several classes for new route dispatching
    • Made changes to composer.json file

    Fixed

    • Fixed major issues breaking routing of urls to handlers
    • Fixed major issues with type-hints due to declare(strict_types=1); found in php files
    • Fixed major issues with array not able to convert into callable
    • Fixed major issues generating and parsing url patterns
    • Fixed major issues with classes, methods, properties and variables documentation

    Removed

    • Marked Flight\Routing\Middlewares\RouteRunnerMiddleware class as deprecated
    Source code(tar.gz)
    Source code(zip)
  • v0.1-beta(Apr 27, 2020)

Owner
Divine Niiquaye Ibok
Software Engineer | Cybersecurity | Open-Source Enthusiast | Researcher and a Content Creator
Divine Niiquaye Ibok
PHPRouter is an easy-to-use, fast, and flexible PHP router package with express-style routing.

PHP-Router is a modern, fast, and adaptable composer package that provides express-style routing in PHP without a framework.

Ayodeji O. 4 Oct 20, 2022
PHP routing class. Lightweight yet flexible. Supports REST, dynamic and reversed routing.

AltoRouter AltoRouter is a small but powerful routing class, heavily inspired by klein.php. $router = new AltoRouter(); // map homepage $router->map(

Danny van Kooten 1.1k Jan 3, 2023
Routing - The Routing component maps an HTTP request to a set of configuration variables.

Routing Component The Routing component maps an HTTP request to a set of configuration variables. Getting Started $ composer require symfony/routing

Symfony 7.3k Jan 6, 2023
PHP Router class - A simple Rails inspired PHP router class.

PHP Router class A simple Rails inspired PHP router class. Usage of different HTTP Methods REST / Resourceful routing Reversed routing using named rou

Danny van Kooten 565 Jan 8, 2023
Fast PSR-7 based routing and dispatch component including PSR-15 middleware, built on top of FastRoute.

Route This package is compliant with PSR-1, PSR-2, PSR-4, PSR-7, PSR-11 and PSR-15. If you notice compliance oversights, please send a patch via pull

The League of Extraordinary Packages 608 Dec 30, 2022
klein.php is a fast & flexible router for PHP 5.3+

Klein.php klein.php is a fast & flexible router for PHP 5.3+ Flexible regular expression routing (inspired by Sinatra) A set of boilerplate methods fo

null 2.6k Jan 7, 2023
Fast request router for PHP

FastRoute - Fast request router for PHP This library provides a fast implementation of a regular expression based router. Blog post explaining how the

Nikita Popov 4.7k Dec 23, 2022
Pux is a fast PHP Router and includes out-of-box controller tools

Pux Pux is a faster PHP router, it also includes out-of-box controller helpers. 2.0.x Branch Build Status (This branch is under development) Benchmark

Yo-An Lin 1.3k Dec 21, 2022
:tada: Release 2.0 is released! Very fast HTTP router for PHP 7.1+ (incl. PHP8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger)

HTTP router for PHP 7.1+ (incl. PHP 8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger) Installation compo

Sunrise // PHP 151 Jan 5, 2023
PhpRouter is a powerful, lightweight, and very fast HTTP URL router for PHP projects.

PhpRouter PhpRouter is a powerful, lightweight, and very fast HTTP URL router for PHP projects. Some of the provided features: Route parameters Predef

Milad Rahimi 152 Dec 28, 2022
A lightweight and fast router for PHP

Piko Router A lightweight and blazing fast router (see benchmarks) using a radix trie to store dynamic routes. This router maps routes to user defined

Piko framework 62 Dec 27, 2022
A fast & flexible router

Klein.php klein.php is a fast & flexible router for PHP 5.3+ Flexible regular expression routing (inspired by Sinatra) A set of boilerplate methods fo

null 2.6k Dec 28, 2022
:bird: Simple PHP router

Macaw Macaw is a simple, open source PHP router. It's super small (~150 LOC), fast, and has some great annotated source code. This class allows you to

Noah Buscher 895 Dec 21, 2022
A lightweight and simple object oriented PHP Router

bramus/router A lightweight and simple object oriented PHP Router. Built by Bram(us) Van Damme (https://www.bram.us) and Contributors Features Support

Bramus! 935 Jan 1, 2023
A simple PHP Router

Panda Router Description the panda-router is a small alternative PHP router that can be used for small projects. With this router you can use differen

Jan Behrens 1 Dec 27, 2021
The simple PHP router

Macaw Macaw is a simple, open source PHP router. It's super small (~150 LOC), fast, and has some great annotated source code. This class allows you to

Noah Buscher 895 Dec 21, 2022
Generate a PHP script for faster routing :rocket:

SwitchRoute Generating a PHP script for faster routing. The traditional way of routing uses regular expressions. This method was improved by FastRoute

Arnold Daniels 75 Nov 20, 2022
Convention based routing for PHP

Croute Convention based routing for PHP based on Symfony components. Croute is great because: You don't need to maintain a routing table Promotes cons

Michael O'Connell 12 Nov 9, 2021
PHP routing (like laravel) (not complete yet)

PHP Router (under construction) This repository contains routing classes that enables you to define your routes similar to laravel 8 routes. Features

Kareem M. Fouad 6 Jan 16, 2022