2017 © Pedro Peláez
 

library router

Yet another php routing class.

image

adinan-cenci/router

Yet another php routing class.

  • Sunday, April 15, 2018
  • by AdinanCenci
  • Repository
  • 1 Watchers
  • 0 Stars
  • 4 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 0 Forks
  • 0 Open issues
  • 4 Versions
  • 0 % Grown

The README.md

A small PHP router library

A simple PHP router to handle http requests., (*1)

This library is PSR-15 compliant and therefore works with PSR-7 messages and makes use of PSR-17 factories., (*2)




, (*3)

Instantiating

use AdinanCenci\Router\Router;
$r = new Router();




, (*4)

Adding routes

You may add routes by informing the http method, the regex pattern to be matched against the the path and the associated controller:, (*5)

$r->add('get', '#home$#', 'controller');

Http method

// You may inform a single http method:
$r->add('get', '#home#', 'controller')

// Or several inside an array ...
$r->add(['get', 'post'], '#home#', 'controller')

// Or in a pipe separated string
$r->add('get|post', '#home#', 'controller')

// Or use an sterisk to match all methods.
$r->add('*', '#home#', 'controller')

Regex patterns

A simple regex pattern. Capture groups will be passed to the controller as attributes., (*6)

Obs: The route accepts multiple patterns as an array., (*7)

$r->add('*', '#products/(?<category>\d+)/(?<id>\d+)#', function($request, $handler) 
{
   $category  = $request->getAttribute('category', null);
   $productId = $request->getAttribute('id', null);
});

Controllers

The controller will receive two paramaters: an instance of Psr\Http\Message\ServerRequestInterface and Psr\Http\Server\RequestHandlerInterface ( the router itself ) respectively., (*8)

By default, the routes accept various arguments as controllers:, (*9)

$r->add('get', '#anonymous-function$#', function($request, $handler) 
{
    echo 'Anonymous function';
})

//-------------

->add('get', '#named-function$#', 'namedFunction')

//-------------

->add('get', '#static-methods$#', ['MyClass', 'staticMethod'])
// A single string also works:
->add('get', '#static-methods$#', 'MyClass::staticMethod')

//-------------

->add('get', '#object-and-method$#', [$object, 'methodName'])

//-------------

->add('get', '#object$#', $object)
// The ::__invoke() magic method will be called.

//-------------

->add('get', '#class-and-method$#', ['MyClass', 'methodName'])
// It will attempt to instantiate the class first.
// A single string also works:
->add('get', '#class-and-method$#', 'MyClass::methodName')

//-------------

->add('get', '#class$#', ['MyClass'])
// It will attempt to instantiate the class and call the ::__invoke() magic method.

//-------------

// Of course, it also accepts instances of Psr\Http\Server\MiddlewareInterface
// ( see the PSR-15 specification for more information )
->add('get', '#psr-15$#', $middleware)

Those are the default controller types, see the contents of the "examples" directory for more detailed examples., (*10)

Check out the advanced section below to learn how to add support for new types., (*11)

Obs: If the controller does not exist or cannot be called because of some reason or another, an exception will be thrown., (*12)

::add() shorthands

// Examples
$r->get('#home#', $call);     /* is the same as */ $r->add('get', '#home#', $call);
$r->post('#home#', $call);    /* is the same as */ $r->add('post', '#home#', $call);
$r->put('#home#', $call);     /* is the same as */ $r->add('put', '#home#', $call);
$r->delete('#home#', $call);  /* is the same as */ $r->add('delete', '#home#', $call);
$r->options('#home#', $call); /* is the same as */ $r->add('options', '#home#', $call);
$r->patch('#home#', $call);   /* is the same as */ $r->add('patch', '#home#', $call);




, (*13)

Middlewares

Middlewares will be processed before the routes.
Middlewares are similar to routes but unlike routes more than one middleware may be executed., (*14)

// Example
$r->before('*', '#restricted-area#', function($request, $handler) 
{
    if (! userIsLogged()) {
        return $handler->responseFactory->movedTemporarily('/login-page');
    }
});




, (*15)

Executing

Calling ::run() will execute the router and send a respose., (*16)

$r->run();




, (*17)

Error handling

Exceptions

By default, catched exceptions will be rendered in a 500 response object, you may customize it by setting your own handler., (*18)

$r->setExceptionHandler(function($request, $handler, $path, $exception) 
{
    return $handler->responseFactory
      ->internalServerError('<h1>Error 500 (' . $path . ')</h1><p>' . $exception->getMessage() . '</p>');
});

Not found

By default when no route is found, the router will render a 404 response object, you may customize it by setting your own handler., (*19)

$r->setNotFoundHandler(function($request, $handler, $path) 
{
    return $handler->responseFactory
      ->internalServerError('<h1>Error 404</h1><p>Nothing found related to "' . $path . '"</p>');
});




, (*20)

PSR compliance and niceties

This library is PSR-15 compliant, as such your controllers may tailor the response in details as specified in the PSR-7., (*21)

IMPORTANT
If your controller does not return an instance of ResponseInterface, the router will create a generic response based out of whatever was outputed through echo and print., (*22)

Besides the defaults, the router offers some niceties., (*23)

::setDefaultNamespace($namespace)

Set the default namespace, so there will be no need to write the entire class name of the controller when defining routes., (*24)

// Example
$r->setDefaultNamespace('MyProject');

$r->add('get', '#home#', 'MyClass::method');
// If MyClass does not exist, the router will assume it refers to 
// MyProject\MyClass::method()

Factories

The handler makes available a PSR-17 response and stream factories to make creating responses more convenient., (*25)

$r->add('get', '#home$#', function($request, $handler)
{
   // Psr\Http\Message\ResponseFactoryInterface instance.
   $responseFactory = $handler->responseFactory;

   // Psr\Http\Message\StreamFactoryInterface instance.
   $streamFactory = $handler->streamFactory;
});

In this spirit of making things easier, the default response factory comes with a series of useful methods:, (*26)

$responseFactory = $handler->responseFactory;

// Response with code 200
$responseFactory->ok('your html here');

// Response with code 201
$responseFactory->created('your html here');

// Response with code 301
$responseFactory->movedPermanently('https://redirect.here.com');

// Response with code 302
$responseFactory->movedTemporarily('https://redirect.here.com');

// Response with code 400
$responseFactory->badRequest('your html here');

// Response with code 401
$responseFactory->unauthorized('your html here');

// Response with code 403
$responseFactory->forbidden('your html here');

// Response with code 404
$responseFactory->notFound('your html here');

// Response with code 500
$responseFactory->internalServerError('your html here');

// Response with code 501
$responseFactory->notImplemented('your html here');

// Response with code 502
$responseFactory->badGateway('your html here');

// Response with code 503
$responseFactory->serviceUnavailable('your html here');

Cookies

Similarly the response objects have the ::withAddedCookie():, (*27)

$response = $responseFactory->ok('your html here');

$expires  = null;  // optional
$path     = '';    // optional
$domain   = '';    // optional
$secure   = false; // optional
$httpOnly = false; // optional

$response = $response->withAddedCookie('cookieName', 'cookieValue', $expires, $path, $domain, $secure, $httpOnly);




, (*28)

Advanced

Working inside sub-directories

The router will automatically work inside sub-directories., (*29)

Consider the example: Your URL: http://yourwebsite.com/foobar/about, (*30)

Your document root is
/var/www/html/ and your router is inside of
/var/www/html/foobar/., (*31)

The router will match the routes against about and NOT foobar/about., (*32)

Still, if you really need to work with foobar/about , then you must pass /var/www/html/ as your base directory to the Router's constructor., (*33)

//               /var/www/html/foobar/index.php
$r = new Router('/var/www/html/');

Factories

The constructor's second and third parameters are a PSR-17 response and stream factories respectively.
If not provided, generic implementations will be used., (*34)

Those factories will made available to the controllers, as explained earlier., (*35)

Custom controller types

The constructor's fourth parameter is an instance of AdinanCenci\Router\Caller\CallerInterface, it is the object that will execute the controllers., (*36)

If not provided, the default caller will be used.
This default caller makes use of several implementations of AdinanCenci\Router\Caller\Handler\HandlerInterface to support the different types of controllers listed earlier., (*37)

You can write your own handlers and callers to support your own version of a controller., (*38)

Dependency injection

So the router supports classes as controllers, how are they instantiated ?, (*39)

The default ClassHandler and MethodHandler depend on an instance of AdinanCenci\Router\Instantiator\InstantiatorInterface.
The default implementation simply calls new to instantiate them., (*40)

If you wish use automatic dependency injection, you will need to provide your own caller/handler/instantiator., (*41)

How dependency injection is handled is beyond the escope of the library., (*42)




, (*43)

Server configuration

In order for it to work, we need to rewrite the requests to the file containing our router. Below are some examples:, (*44)

Apache

Here is the example of a .htaccess for Apache:, (*45)

RewriteEngine on

# Condition: Requested resource does not exist
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d

# Rewrite to index.php
RewriteRule ^.{1,}$   index.php   [QSA]




, (*46)

Nginx

Here is the example for nginx:, (*47)

location / {
    if ($script_filename !~ "-f") {
        rewrite "^/.{1,}$" /index.php;
    }
}




, (*48)

IIS

Here is the example of a web.config for Microsoft IIS:, (*49)

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="RewriteNonExistingFiles">
                    <match url="^.{1,}$" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/index.php" appendQueryString="true" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>




, (*50)

Installing

Use composer, (*51)

composer require adinan-cenci/router




, (*52)

License

MIT, (*53)

The Versions

15/04 2018

dev-master

9999999-dev

Yet another php routing class.

  Sources   Download

MIT

The Requires

  • php >=5.3

 

The Development Requires

by Adinan Cenci

router

03/02 2018

0.2.0

0.2.0.0

Yet another php routing class.

  Sources   Download

MIT

The Requires

  • php >=5.3

 

The Development Requires

by Adinan Cenci

router

30/01 2018

0.1.1

0.1.1.0

Yet another php routing class.

  Sources   Download

MIT

The Requires

  • php >=5.3

 

The Development Requires

by Adinan Cenci

router

27/01 2018

0.1.0

0.1.0.0

Yet another php routing class.

  Sources   Download

MIT

The Requires

  • php >=5.3

 

The Development Requires

by Adinan Cenci

router