Giter Club home page Giter Club logo

phroute's Introduction

PHRoute - Fast request router for PHP

Build Status Coverage Status Latest Stable Version License Total Downloads

This library provides a fast implementation of a regular expression based router.

Credit to nikic/FastRoute.

While the bulk of the library and extensive unit tests are my own, credit for the regex matching core implementation and benchmarking goes to nikic. Please go and read nikic's blog post explaining how the implementation works and why it's fast.

Many modifications to the core have been made to suit the new library wrapper, and additional features added such as optional route parameters and reverse routing etc, but please head over and checkout nikic's library to see the origins of the core and how it works.

Installation

Install via composer

composer require phroute/phroute

Usage

Example

$router->get('/example', function(){
    return 'This route responds to requests with the GET method at the path /example';
});

$router->post('/example/{id}', function($id){
    return 'This route responds to requests with the POST method at the path /example/1234. It passes in the parameter as a function argument.';
});

$router->any('/example', function(){
    return 'This route responds to any method (POST, GET, DELETE, OPTIONS, HEAD etc...) at the path /example';
});

Defining routes

use Phroute\Phroute\RouteCollector;

$router = new RouteCollector();

$router->get($route, $handler);    # match only get requests
$router->post($route, $handler);   # match only post requests
$router->delete($route, $handler); # match only delete requests
$router->any($route, $handler);    # match any request method

etc...

These helper methods are wrappers around addRoute($method, $route, $handler)

This method accepts the HTTP method the route must match, the route pattern and a callable handler, which can be a closure, function name or ['ClassName', 'method'] pair.

The methods also accept an additional parameter which is an array of middlewares: currently filters before and after, and route prefixing with prefix are supported. See the sections on Filters and Prefixes for more info and examples.

By default a route pattern syntax is used where {foo} specifies a placeholder with name foo and matching the string [^/]+. To adjust the pattern the placeholder matches, you can specify a custom pattern by writing {bar:[0-9]+}. However, it is also possible to adjust the pattern syntax by passing a custom route parser to the router at construction.

$router->any('/example', function(){
    return 'This route responds to any method (POST, GET, DELETE etc...) at the URI /example';
});

// or '/page/{id:i}' (see shortcuts)

$router->post('/page/{id:\d+}', function($id){

    // $id contains the url paramter

    return 'This route responds to the post method at the URI /page/{param} where param is at least one number';
});

$router->any('/', function(){

    return 'This responds to the default route';
});

// Lazy load autoloaded route handling classes using strings for classnames
// Calls the Controllers\User::displayUser($id) method with {id} parameter as an argument
$router->any('/users/{id}', ['Controllers\User','displayUser']);

// Optional Parameters
// simply add a '?' after the route name to make the parameter optional
// NB. be sure to add a default value for the function argument
$router->get('/user/{id}?', function($id = null) {
    return 'second';
});

# NB. You can cache the return value from $router->getData() so you don't have to create the routes each request - massive speed gains
$dispatcher = new Phroute\Phroute\Dispatcher($router->getData());

$response = $dispatcher->dispatch($_SERVER['REQUEST_METHOD'], parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));

// Print out the value returned from the dispatched function
echo $response;

Regex Shortcuts


:i => :/d+                # numbers only
:a => :[a-zA-Z0-9]+       # alphanumeric
:c => :[a-zA-Z0-9+_\-\.]+  # alnumnumeric and + _ - . characters
:h => :[a-fA-F0-9]+       # hex

use in routes:

'/user/{name:i}'
'/user/{name:a}'

###Named Routes for Reverse Routing

Pass in an array as the first argument, where the first item is your route and the second item is a name with which to reference it later.

$router->get(['/user/{name}', 'username'], function($name){
    return 'Hello ' . $name;
})
->get(['/page/{slug}/{id:\d+}', 'page'], function($id){
    return 'You must be authenticated to see this page: ' . $id;
});

// Use the routename and pass in any route parameters to reverse engineer an existing route path
// If you change your route path above, you won't need to go through your code updating any links/references to that route
$router->route('username', 'joe');
// string(9) '/user/joe'

$router->route('page', ['intro', 456]);
// string(15) '/page/intro/456'

###Filters

$router->filter('statsStart', function(){
    setPageStartTime(microtime(true));
});

$router->filter('statsComplete', function(){
    var_dump('Page load time: ' . (microtime(true) - getPageStartTime()));
});

$router->get('/user/{name}', function($name){
    return 'Hello ' . $name;
}, ['before' => 'statsStart', 'after' => 'statsComplete']);

###Filter Groups

Wrap multiple routes in a route group to apply that filter to every route defined within. You can nest route groups if required.

// Any thing other than null returned from a filter will prevent the route handler from being dispatched
$router->filter('auth', function(){
    if(!isset($_SESSION['user']))
    {
        header('Location: /login');

        return false;
    }
});

$router->group(['before' => 'auth'], function($router){

    $router->get('/user/{name}', function($name){
        return 'Hello ' . $name;
    })
    ->get('/page/{id:\d+}', function($id){
        return 'You must be authenticated to see this page: ' . $id;
    });

});

###Prefix Groups

// You can combine a prefix with a filter, eg. `['prefix' => 'admin', 'before' => 'auth']`

$router->group(['prefix' => 'admin'], function($router){

    $router->get('pages', function(){
        return 'page management';
    });

    $router->get('products', function(){
        return 'product management';
    });

    $router->get('orders', function(){
        return 'order management';
    });
});

###Controllers

namespace MyApp;

class Test {

    public function anyIndex()
    {
        return 'This is the default page and will respond to /controller and /controller/index';
    }

    /**
    * One required paramter and one optional parameter
    */
    public function anyTest($param, $param2 = 'default')
    {
        return 'This will respond to /controller/test/{param}/{param2}? with any method';
    }

    public function getTest()
    {
        return 'This will respond to /controller/test with only a GET method';
    }

    public function postTest()
    {
        return 'This will respond to /controller/test with only a POST method';
    }

    public function putTest()
    {
        return 'This will respond to /controller/test with only a PUT method';
    }

    public function deleteTest()
    {
        return 'This will respond to /controller/test with only a DELETE method';
    }
}

$router->controller('/controller', 'MyApp\\Test');

// Controller with associated filter
$router->controller('/controller', 'MyApp\\Test', ['before' => 'auth']);

Dispatching a URI

A URI is dispatched by calling the dispatch() method of the created dispatcher. This method accepts the HTTP method and a URI. Getting those two bits of information (and normalizing them appropriately) is your job - this library is not bound to the PHP web SAPIs.

$response = (new Phroute\Phroute\Dispatcher($router)) ->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);

The dispatch() method will call the matched route, or if no matches, throw one of the exceptions below:

# Route not found
Phroute\Phroute\Exception\HttpRouteNotFoundException;

# Route found, but method not allowed
Phroute\Phroute\Exception\HttpMethodNotAllowedException;

NOTE: The HTTP specification requires that a 405 Method Not Allowed response include the Allow: header to detail available methods for the requested resource. This information can be obtained from the thrown exception's message content: which will look like: "Allow: HEAD, GET, POST" etc... depending on the methods you have set You should catch the exception and use this to send a header to the client: header($e->getMessage());

###Dependency Injection

Defining your own dependency resolver is simple and easy. The router will attempt to resolve filters, and route handlers via the dependency resolver.

The example below shows how you can define your own resolver to integrate with orno/di, but pimple/pimple or others will work just as well.

use Orno\Di\Container;
use Phroute\Phroute\HandlerResolverInterface;

class RouterResolver implements HandlerResolverInterface
{
    private $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    public function resolve($handler)
    {
        /*
         * Only attempt resolve uninstantiated objects which will be in the form:
         *
         *      $handler = ['App\Controllers\Home', 'method'];
         */
        if(is_array($handler) and is_string($handler[0]))
        {
            $handler[0] = $this->container[$handler[0]];
        }

        return $handler;
    }
}

When you create your dispatcher:

$appContainer = new Orno\Di;

// Attach your controllers as normal
// $appContainer->add('App\Controllers\Home')


$resolver = new RouterResolver($appContainer);
$response = (new Phroute\Phroute\Dispatcher($router, $resolver))->dispatch($requestMethod, $requestUri);

A Note on HEAD Requests

The HTTP spec requires servers to [support both GET and HEAD methods][2616-511]:

The methods GET and HEAD MUST be supported by all general-purpose servers

To avoid forcing users to manually register HEAD routes for each resource we fallback to matching an available GET route for a given resource. The PHP web SAPI transparently removes the entity body from HEAD responses so this behavior has no effect on the vast majority of users.

However, implementors using Phroute outside the web SAPI environment (e.g. a custom server) MUST NOT send entity bodies generated in response to HEAD requests. If you are a non-SAPI user this is your responsibility; Phroute has no purview to prevent you from breaking HTTP in such cases.

Finally, note that applications MAY always specify their own HEAD method route for a given resource to bypass this behavior entirely.

Performance

Performed on a machine with :

  • Processor 2.3 GHz Intel Core i7
  • Memory 8 GB 1600 MHz DDR3

####Phroute

This test is to illustrate, in part, the efficiency of the lightweight routing-core, but mostly the lack of degradation of matching speed as the number of routes grows, as compared to conventional libraries.

With 10 routes, matching 1st route (best case)
$ /usr/local/bin/ab -n 1000 -c 100 http://127.0.0.1:9943/

Finished 1000 requests

Time taken for tests:   3.062 seconds
Requests per second:    326.60 [#/sec] (mean)
Time per request:       306.181 [ms] (mean)
Time per request:       3.062 [ms] (mean, across all concurrent requests)
Transfer rate:          37.32 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%    306
  66%    307
  75%    307
  80%    308
  90%    309
  95%    309
  98%    310
  99%    310
 100%    310 (longest request)
With 10 routes, matching last route (worst case)

Note that the match is just as quick as against the first route

$ /usr/local/bin/ab -n 1000 -c 100 http://127.0.0.1:9943/thelastroute

Finished 1000 requests

Time taken for tests:   3.079 seconds
Requests per second:    324.80 [#/sec] (mean)
Time per request:       307.880 [ms] (mean)
Time per request:       3.079 [ms] (mean, across all concurrent requests)
Transfer rate:          37.11 [Kbytes/sec] received


Percentage of the requests served within a certain time (ms)
  50%    307
  66%    308
  75%    309
  80%    309
  90%    310
  95%    311
  98%    312
  99%    312
 100%    313 (longest request)
With 100 routes, matching last route (worst case)
$ /usr/local/bin/ab -n 1000 -c 100 http://127.0.0.1:9943/thelastroute

Finished 1000 requests

Time taken for tests:   3.195 seconds
Requests per second:    312.97 [#/sec] (mean)
Time per request:       319.515 [ms] (mean)
Time per request:       3.195 [ms] (mean, across all concurrent requests)
Transfer rate:          35.76 [Kbytes/sec] received


Percentage of the requests served within a certain time (ms)
  50%    318
  66%    319
  75%    320
  80%    320
  90%    322
  95%    323
  98%    323
  99%    324
 100%    324 (longest request)
With 1000 routes, matching the last route (worst case)
$ /usr/local/bin/ab -n 1000 -c 100 http://127.0.0.1:9943/thelastroute

Finished 1000 requests

Time taken for tests:   4.497 seconds
Complete requests:      1000
Requests per second:    222.39 [#/sec] (mean)
Time per request:       449.668 [ms] (mean)
Time per request:       4.497 [ms] (mean, across all concurrent requests)
Transfer rate:          25.41 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%    445
  66%    447
  75%    448
  80%    449
  90%    454
  95%    456
  98%    457
  99%    458
 100%    478 (longest request)

###For comparison, Laravel 4.0 routing core

Please note, this is no slight against laravel - it is based on a routing loop, which is why the performance worsens as the number of routes grows

With 10 routes, matching first route (best case)
$ /usr/local/bin/ab -n 1000 -c 100 http://127.0.0.1:4968/

Finished 1000 requests

Time taken for tests:   13.366 seconds
Requests per second:    74.82 [#/sec] (mean)
Time per request:       1336.628 [ms] (mean)
Time per request:       13.366 [ms] (mean, across all concurrent requests)
Transfer rate:          8.55 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%   1336
  66%   1339
  75%   1340
  80%   1341
  90%   1346
  95%   1348
  98%   1349
  99%   1351
 100%   1353 (longest request)
With 10 routes, matching last route (worst case)
$ /usr/local/bin/ab -n 1000 -c 100 http://127.0.0.1:4968/thelastroute

Finished 1000 requests

Time taken for tests:   14.621 seconds
Requests per second:    68.39 [#/sec] (mean)
Time per request:       1462.117 [ms] (mean)
Time per request:       14.621 [ms] (mean, across all concurrent requests)
Transfer rate:          7.81 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%   1461
  66%   1465
  75%   1469
  80%   1472
  90%   1476
  95%   1479
  98%   1480
  99%   1482
 100%   1484 (longest request)
With 100 routes, matching last route (worst case)
$ /usr/local/bin/ab -n 1000 -c 100 http://127.0.0.1:4968/thelastroute

Finished 1000 requests

Time taken for tests:   31.254 seconds
Requests per second:    32.00 [#/sec] (mean)
Time per request:       3125.402 [ms] (mean)
Time per request:       31.254 [ms] (mean, across all concurrent requests)
Transfer rate:          3.66 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%   3124
  66%   3145
  75%   3154
  80%   3163
  90%   3188
  95%   3219
  98%   3232
  99%   3236
 100%   3241 (longest request)
With 1000 routes, matching last route (worst case)
$ /usr/local/bin/ab -n 1000 -c 100 http://127.0.0.1:5740/thelastroute

Finished 1000 requests

Time taken for tests:   197.366 seconds
Requests per second:    5.07 [#/sec] (mean)
Time per request:       19736.598 [ms] (mean)
Time per request:       197.366 [ms] (mean, across all concurrent requests)
Transfer rate:          0.58 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)
  50%  19736
  66%  19802
  75%  19827
  80%  19855
  90%  19898
  95%  19918
  98%  19945
  99%  19960
 100%  19975 (longest request)

phroute's People

Contributors

daghostman avatar frederikbosch avatar mrjgreen avatar nikic avatar pascal-hofmann avatar pfuhrmann avatar rdlowrey avatar tyler-sommer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phroute's Issues

Problem with delete() routes

Hi guys,

I have a problem with delete routes.. How to implement this with html forms?

My code:

<form class="" action="{{ route('admin.students.destroy', [s.id]) }}" method="POST">
     <input type="hidden" name="method" value="DELETE"> <!-- send method? -->
     <button type="submit" name="button"><i class="icon icon-delete"></i></button>
</form>

Current Status

Is this package still in active development? If it is, are you planning to keep up with Fastroute's development?

Unicode routing

Hi,
How do you enable Unicode in Phroute? I tried changing \w with \pL but it did not work. Any solutions?

  • I can't use \x{600}-\x{6FF} becuase phroute considers {} variable only, while they can be use in \x modifier too.
    Thanks

Problem with install via composer

File: composer.json

{
    "config": {
        "preferred-install": dist
    },
    "require": {
        "phroute/phroute": "dev-master"
    }
}

$ composer.phar update

  - Installing phroute/phroute (dev-master c00c7a5)
    Downloading: connection...    Failed to download phroute/phroute from dist: The "https://api.github.com/repos/joegreen0991/phroute/zipball/c00c7a579c4e273885651a6da2a465f1ac3a5ffc" file could not be downloaded (HTTP/1.1 404 Not Found)

The link was broken if I prefer "dist", but if I use mrjgreen instead of joegreen0991, it works. So, could you please change composer's source from "https://github.com/joegreen0991/phroute" to "https://github.com/mrjgreen/phroute" ?

Thanks.

PSR-7 Compliance

Would a PSR-7 request be appropriate?

I think, that having a PSR-7 compliant method will make it easier for implementing #5 Sub-domain routing as PSR-7 server request object should contain all the necessary information for the dispatcher to do its work?

I was thinking of making a PR for a method dispatchPSR (although it looks fugly..) so that such implementation does not break existing implementations using Phroute.

Passing addittional data to functions

I want to pass additional data along with the dispatcher but I don't want to use global variables. I patched the dispatchter to support variables arguments which are passed to the function:

Dispatcher.php
45: public function dispatch($httpMethod, $uri, ...$params)
58: $vars = array_merge($vars, $params);

If you think this is a good idea it would be great if you could integrate it, if not please tell me which way you would choose.

Passing params to controller

I can do the following:

$router->get('/', function() use ($site) {
    $index = new App\Controllers\Admin\Index;
    return $index->anyIndex($site);
});

Is there anyway to achieve a similar effect using:

$router->controller('/', 'App\\Controllers\Admin\Index');

Called Method On Filter

Hello,

Supposing I'm implementing an authentication filter similar to the readme example:

$router->filter('auth', function(){    
     // Authentication logic lives here!
});

And I'm implementing the router as:

$router->group(['before' => 'auth'], function($router){
     $router->get("posts/{id}", ["Posts", "getFullDetails"]);
});

Is there a way I can know on my filter callback method the name of the method being called to dispatch the request?

On the example above I would get a parameter on the callback with the string Posts->getFullDetails or similar telling me what method will be called to dispatch the request. Something like:

$router->filter('auth', function($dispatchMethodName){    
    print $dispatchMethodName // Posts->getFullDetails
});

Thank you.

Cant get things to start accordingly

Hi

Im trying to run a simple Hello World. But as it seems, I'm retarded.

In index.php i have the following:

<?php
require __DIR__ . '/../vendor/autoload.php';

use Phroute\Phroute\RouteCollector;

$route = new RouteCollector();

$route->get('/', function(){
    return 'Home Page';
});

But i just receive a bank page!(?) Nor error messages in php_error.log.
I can add echo 'Hello world!; at bottom of index.php and that prints. But the router won't work.

If i var_dump($route); i get:

object(Phroute\Phroute\RouteCollector)#3 (7) {
  ["routeParser":"Phroute\Phroute\RouteCollector":private]=>
  object(Phroute\Phroute\RouteParser)#2 (6) {
    ["parts":"Phroute\Phroute\RouteParser":private]=>
    array(0) {
    }
    ["reverseParts":"Phroute\Phroute\RouteParser":private]=>
    array(0) {
    }
    ["partsCounter":"Phroute\Phroute\RouteParser":private]=>
    int(0)
    ["variables":"Phroute\Phroute\RouteParser":private]=>
    array(0) {
    }
    ["regexOffset":"Phroute\Phroute\RouteParser":private]=>
    int(0)
    ["regexShortcuts":"Phroute\Phroute\RouteParser":private]=>
    array(4) {
      [":i}"]=>
      string(8) ":[0-9]+}"
      [":a}"]=>
      string(14) ":[0-9A-Za-z]+}"
      [":h}"]=>
      string(14) ":[0-9A-Fa-f]+}"
      [":c}"]=>
      string(20) ":[a-zA-Z0-9+_\-\.]+}"
    }
  }
  ["filters":"Phroute\Phroute\RouteCollector":private]=>
  array(0) {
  }
  ["staticRoutes":"Phroute\Phroute\RouteCollector":private]=>
  array(1) {
    [""]=>
    array(1) {
      ["GET"]=>
      array(3) {
        [0]=>
        object(Closure)#4 (0) {
        }
        [1]=>
        array(0) {
        }
        [2]=>
        array(0) {
        }
      }
    }
  }
  ["regexToRoutesMap":"Phroute\Phroute\RouteCollector":private]=>
  array(0) {
  }
  ["reverse":"Phroute\Phroute\RouteCollector":private]=>
  array(0) {
  }
  ["globalFilters":"Phroute\Phroute\RouteCollector":private]=>
  array(0) {
  }
  ["globalRoutePrefix":"Phroute\Phroute\RouteCollector":private]=>
  NULL
}

So it seems to be loaded?

How should I cache the routes?

Hello, You mention caching the $router->getData() in your example, but you're using closures so it looks like serialize($router->getData()) is not an option.

I'm using closures too, how do you suggest I cache the $router->getData()?

Parameter in route that contains /

I am trying to achieve my route to work with parameter path:
$router->get('/files/{path}', function($path){ return $path; });

It would be great if parameters could contain slashes.

Provide access to $router->globalRoutePrefix

It would be useful to be able to access globalRoutePrefix of the router, so I can replace the hardcoded prefix for the legacy redirect

$router->group(array('prefix'=>'cpudb'), function($router) {
    $router->get('/', array(__CLASS__, 'actionIndex'));
    $router->get('/legacyRoute', function() { throw new HttpException301('/cpudb/'); });
});

Controller and Filters

Hi, I think currently it is not possible to add filters using controllers? This is great library for small projects however anything of medium/bigger size need controllers and filters (together).

Get route data and prevent calling controller

Hi,

The reason I open this issue is that I need to use phroute as a router only and I dont want it to try to execute the handler and there is no way (as far as I know) to only get the data that matched.

The actual issue is that the method dispatchRoute (that does exactly what I'm looking for), is private and I cant use it even by extending the dispatcher.

Would you mind to make it protected or public? Or maybe do you have an other way to do that?
Thanks

Optional parameter in controller

Hi

I might not be using this as intended, but it it possible to add parameters to a controller class?

$router->get(['/purchased/view/{id}', 'viewPurchase'], ['controllers\PurchaseController','view']);

As an example I would like the named route to be available.

  • thanks in advance

Documentation for optional routes

Hi,

I was naively wanting to implement a default route with two optional parameters something like:

{controller}/{action}?/{id}?

...but it was unexpectedly matching to the previous static rule when processing a url such as "/controller/action".

When i stepped through the code I saw this was because the previous dynamic route only had 1 variable but my default route was trying to match 3, so in Dispatcher::dispatchVariableRoute(...) after the preg_match returns 2 matches when it cycles down through the routes (line 194 in the version i've got) it ends up matching whatever was the previous route rather than the default one I was hoping for.

I fixed this by defining two default routes with just a single optional route at the end, and this works just fine like this
{controller}/{action}?
{controller}/{action}/{id}?

Unless I'm missing something, It may be worth mentioning that only one optional parameter should be used per route?

Anwyay, thanks for the great library!

Multiple optional params

This route is not working:

user/{name}?/{id}? 

Tried and failed:

user/{name}?/?{id}? 

Any ideas ?

Working with PUT and DELETE HTTP methods from AJAX

Hello, this issue of mine is similar to #43 but the difference is that the request came from AJAX.

In my own understanding about these kind of requests, the web browser sends a preflight request (OPTIONS) first to the server before sending the real request (PUT or DELETE) in $_SERVER['REQUEST_METHOD'].

The problem is that the dispatcher treats it as an existing route, thus returning an error of Allow: PUT. Can I ask if there is a way on how to implement it using your library?

Blank Pages

I set up an index.php page, imported the library, set it up the same way as the example. I set up the nginx as described and the same way I had other frameworks that used routers. But when I go to the URL via the browser I get blank pages.

I tried the example with the dispatch and it seems like it works but not when the pages are hit through the url. What am I missing?

Passing Routing Variables To Global Scope

I need to get routing parameters available in the global scope. I am not using controllers in a project, so I am including script file based on route rules using dispatcher.

Basically, when I set route rules as

$router->any('/page', function(){
    return '/path/to/page.php';
});

I include the returned script path as

$dispatcher = new Phroute\Phroute\Dispatcher($router->getData());
$response = $dispatcher->dispatch($_SERVER['REQUEST_METHOD'], parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
include $response;

I cannot include script in the callback function due to other reasons. So the problem for me is to pass route parameters in dynamic routes to outside of the callback function's scope. Example:

$router->any('/blog/{post:[a-zA-Z0-9+_\-\.]+}', function($post){
    return '/path/to/post.php';
});

I need $post parameters out of callback function

My best attempt is using get_defined_vars() as a generic solution:

$router->any('/blog/{post:[a-zA-Z0-9+_\-\.]+}', function($post){
    return array('path/to/post.php',get_defined_vars());
});

So that I can get $post at dispatcher.

Is there any better method to get route parameters at the dispatcher level?

Extending Dispatcher

Could you make the private methods in Dispatcher class as protected so we can properly extend the functionality without duplicating code ?

I get a blank page

After installing this package, which seems to be the most simple to use of all, I have created this file index.php:

$router = new \Phroute\Phroute\RouteCollector();

$router->get('/home', function () {
    return 'hello world';
});

and all I get is a white page. The htaccess file looks like this:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]

Cache problem with closures

Hi,
When I try to cache route data array, I get this error:
Exception: Serialization of 'Closure' is not allowed
That's why the filters are also included in the array! Closures are not normally serializable, and caching can not be done without serializing. Any workarounds?
Thanks

Query string in request exception

// Excuse my beginner's English

There is possible bug.

My route: get /api/account/{user_id:i}/token/

My request: get /api/account/285/token/?name=info-product

Exception: Route api/account/285/token/?name=info-product does not exist

Thanks!

Not work

This is my .htaccess.
I put router at index.php
But it doesn't works

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /index.php [NC,L,QSA]

Escaping Dash

Thanks for creating a simple router. Great work!

I am having issues with the generated URLs. If I use dashes phrouter adds backslash and it doesn't add backslashes when I don't use regex. See this:
$router->any(
[ 'user-account/edit/{id:\d+}', 'user-account-edit'],
['UserAccount', 'edit']
);

And here is what I get when I output URL:
http://local.dev/user\-account/edit/1/"

How do I escape dashes in the URL?

At the minute, I am overriding your RouteCollector class's route method and stripping all backslashes in the url.

Optional parts in route

In FasteRoute I used to use regex /[home/] to match both / and /home/ for a particular route.

Extract from FasteRoute Readme.md:

Furthermore parts of the route enclosed in [...] are considered optional, so that /foo[bar] will match both /foo and /foobar. Optional parts are only supported in a trailing position, not in the middle of a route.

Is there a similar functionality in phroute?

Missing initial '/' with reverse routing

I have a problem with reverse routing. It omits the initial '/', which messes up the URL's when using $router->route('name') within a URL-subfolder.

$router->post(['/admin/login', 'login.do'], ['MyController','doLogin']);
echo $router->route('login.do');

Expected: /admin/login
Actual: admin/login

Using latest tagged release.

Feature request: nested routes/resources

As far as I am aware of, there is no other PHP router that has this feature. The idea of nested routes is not completely new though. I derived it from EmberJS - Route - Nested Resources. Below you find a business case for this. This issue allows to discuss if users are interested in such feature. If so, we could think on how to implement it and what the API should be.

What is the business case?

It especially suites administrative apps. Suppose we have the following route: /event/1/subscriptions. This represents the subscriptions of event 1. With the current setup, we assign a controller to this route. This controller will probably fetch the event and the subscriptions from one or multiple repositories, normalize data and the view will display the information.

Things get more complicated when we go deeper. Suppose we have the following route: /event/1/subscription/50/product/50, which represents the additional products that subscription 50 bought for event 1 (e.g. dinner ticket). What is our controller doing then? It will first fetch the event, then fetch subscription 50 and then fetch its additional products. Of course this controller has REST/CRUD options. How many repositories are we injecting in this controller?

What changes when there are nested resources?

Suppose we have the same route /event/1/subscriptions. With nested routes, then there are two routes active during the same request. /event/{id:i} is assigned to the EventController and /event/{id:i}/subscriptions is assigned to the EventSubscriptionController. The code for those controllers will be much cleaner. Probably there is only repository injected into this controller. In Ember there is something like modelFor to fetch the model/entity of the parent route, but in Ember every active route also results in a route class. How this would work out for Phroute could be discussed later on.

If we take the more complicated example, /event/1/subscription/50/product/50 will have 3 controllers, the same as mentioned earlier and the EventSubscriptionProductController. This controller only knows the specific part of the application that it is responsible for: products of a subscription. No need to fetch an event. There should only be a way to fetch the subscription to which the products are related. Either through something similar as modelFor or some dependency injection.

Problem with reverse routes

$router->any(['/products/store/{store:i}?', 'products'], ['Ruvilla\Admin\Controller\Products', 'anyList']);

$router->router('products', array());
OUTPUT: /products/store
OK

$router->router('products', array(1));
OUTPUT: /products/store1
EXPECTED: /products/store/1

Prefix routes with optional language

Example defined route:

/{lang}?/confirm/{token}
// this example works
$collector->route('confirm', ['en', 'test']); // => /en/confirm/test

// this example fails
$collector->route('confirm', ['', 'test']); // => //confirm/test

// this example fails
$collector->route('confirm', [false, 'test']); // => //confirm/test

// this example throws exception
$collector->route('confirm', [null, 'test']); // => PHP message: exception 'Phroute\Phroute\Exception\BadRouteException' with message 'Expecting route variable 'token'

Is there a way to get /confirm/test instead of //confirm/test ?
I tried to make / optional after lang but it throws an error.

Maybe something like: https://github.com/nikic/FastRoute/releases/tag/v0.6.0

Quantifier in route regex

Because Phroutes uses { and } in the matcher, you cannot use quantifiers in the regex for it, for example: {year:\d{4}} to only match if 4 digits are found.

Link http method

// Excuse my beginner's English

Hi, guys!

What about link and unlink http headers?
Yes, i know about any method, but...

Thanks!

Controllers Routing

Hello,

I was trying to setup a controller like explained here: https://github.com/mrjgreen/phroute#controllers

But it doesn't work properly. It always ends up on the anyIndex() method and ignores the for example postTest() if I set my method to POST. If I remove the anyIndex() method it doesn't work.

Is this poorly implemented or I'm using it the wrong way?

Working with put and delete method

I am trying to route the PUT and DELETE method to a controller. Here is my router code:
$router->put('country/{id:\d+}',['app\controllers\Country','update']);
the same for DELETE.

the problem here is, how could I make PUT and DELETE request from html form, since html forms does not support http verbs except GET and POST. But other php frameworks implement this method by adding:

Can I use this technique in this library or if there is any other method to achieve this ???

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.