1
0
mirror of https://github.com/dannyvankooten/AltoRouter.git synced 2025-08-23 16:42:48 +02:00

51 Commits

Author SHA1 Message Date
Danny van Kooten
39c5009247 Merge pull request #136 from koenpunt/travis-docker
Optimize Travis builds, thanks @koenpunt!
2015-11-30 07:47:43 +07:00
Koen Punt
018771bee3 add php 7.0 2015-11-29 16:30:06 +01:00
Koen Punt
13f227e843 add composer to travis cache 2015-11-29 16:26:36 +01:00
Koen Punt
d5f3643888 disable sudo to use docker builds 2015-11-29 16:10:24 +01:00
Danny van Kooten
b0d115431e Add tests for non-regex routes, see #100 2015-11-03 08:03:34 +07:00
Danny van Kooten
97b2ba7daf Add PHPDocs for class properties. 2015-11-03 07:37:53 +07:00
Danny van Kooten
c3f7a669a8 Add @throws declaration to method that can throw an Exception. 2015-11-03 07:37:36 +07:00
Danny van Kooten
c9ce952af1 Remove unused alteration of $_REQUEST superglobal. Closes #104 2015-11-03 07:37:04 +07:00
Danny van Kooten
e7c5afefca Add .gitattributes to ignore certain files & dirs when preparing export for things as Composer. (http://www.reddit.com/r/PHP/comments/2jzp6k/i_dont_need_your_tests_in_my_production/) 2015-03-28 11:51:38 +01:00
Koen Punt
f0e9c913a6 blacklist vendor 2015-03-02 16:51:27 +01:00
Koen Punt
7b2e59a444 Merge pull request #107 from dannyvankooten/phpunit-config
Use local phpunit
2015-03-02 15:41:30 +01:00
Koen Punt
2035dab8be rename phpunit.xml to phpunit.xml.dist 2015-03-02 15:13:56 +01:00
Koen Punt
88af3a6fa5 remove path to individual test 2015-03-02 15:10:38 +01:00
Koen Punt
ed1c3edfbd Use local phpunit
Also, no need to specify the path to the config
2015-03-02 15:10:03 +01:00
Danny van Kooten
0e1e2b7d63 Add CodeClimate test coverage reporter to Travis. Refactor tests into /tests subdirectory. 2015-03-02 19:35:56 +07:00
Danny van Kooten
0c01df1128 Add Test Coverage badge to README 2015-03-02 19:28:32 +07:00
Koen Punt
179d2fe5bc Merge pull request #105 from koenpunt/add-getroutes
add test for getRoutes
2015-03-01 20:53:39 +01:00
Koen Punt
3a932e7588 add test for getRoutes
remove obsolete getRoutes method from debug class
2015-03-01 20:47:54 +01:00
Danny van Kooten
60cf81c963 Merge pull request #103 from koenpunt/phpunit-composer
add phpunit to composer.json
2015-03-01 23:09:36 +07:00
Koen Punt
9bf911515c add phpunit to composer.json 2015-03-01 17:07:47 +01:00
Danny van Kooten
fa50148d7b Merge pull request #97 from SebastianPoell/patch-1
Update AltoRouter.php
2015-03-01 22:35:53 +07:00
Danny van Kooten
24d42506ec Fix badges in README 2015-03-01 22:32:15 +07:00
Danny van Kooten
6a77802419 Add badges to README 2015-03-01 22:29:40 +07:00
Danny van Kooten
f4b09c3b6c Add CodeClimate to README. Update MIT copyright year. 2015-03-01 22:27:17 +07:00
Sebastian Pöll
40adf80fd2 Update AltoRouter.php 2014-12-19 16:20:11 +01:00
Sebastian Pöll
3de8ea70d5 Update AltoRouter.php
Added @return to getRoutes. Removed unnecessary whitespaces.
2014-12-19 16:13:03 +01:00
Sebastian Pöll
009f163c3d Update AltoRouter.php
Added function getRoutes(). Often it's nice to process routes or display them without extending AltoRouter.
2014-12-19 15:26:51 +01:00
Danny van Kooten
a7a6b99ac8 Merge branch 'master' of github.com:dannyvankooten/AltoRouter 2014-10-07 18:13:18 +02:00
Danny van Kooten
384d0ed35b Readme update, link usage sections to AltoRouter.com 2014-10-07 18:11:22 +02:00
Koen Punt
4acd26880c Fix incorrect example 2014-10-07 10:22:05 +02:00
Danny van Kooten
c0041d10e7 Merge pull request #79 from dannyvankooten/cleanup-readme
Update/Cleanup README
2014-10-07 09:24:30 +02:00
John Long
f468fe9c02 Merge pull request #88 from Nyholm/patch-1
Added PHP 5.6 and HHVM to travis.yml
2014-09-25 06:41:42 -05:00
Tobias Nyholm
fdadb3119d Added PHP 5.6 and HHVM to travis.yml 2014-09-25 08:21:10 +02:00
Koen Punt
768d3ea445 mention addRoutes 2014-05-10 17:52:49 +02:00
Koen Punt
4b1c205de4 Cleanup README 2014-05-10 17:44:34 +02:00
Koen Punt
cd145f993e Merge pull request #78 from koenpunt/escape-dash
escape dash to fix tests
2014-05-10 14:58:06 +02:00
Koen Punt
99f75c28e6 escape dash to fix tests 2014-05-10 14:56:15 +02:00
Danny van Kooten
aa0706284d Merge pull request #75 from dannyvankooten/add-patch-method-type
Add PATCH to comment as valid method.
2014-05-08 11:16:37 +02:00
Koen Punt
55da8fcbda Add PATCH to comment as valid method.
In frameworks like Rails the PATCH method is preferred over PUT. More on this: http://weblog.rubyonrails.org/2012/2/25/edge-rails-patch-is-the-new-primary-http-method-for-updates/
2014-05-07 13:39:15 +02:00
Danny van Kooten
353596ec4f Merge pull request #66 from MathB/master
Add matching documentation
2014-04-17 13:25:07 +02:00
Danny van Kooten
09d9d946c5 Merge pull request #69 from koenpunt/unicode-regex
Add support for unicode regular expressions
2014-04-16 11:44:40 +02:00
Danny van Kooten
b749633151 Merge pull request #68 from koenpunt/fix-coverage
Add addRoutes method for bulk adding routes, props @koenpunt
2014-04-16 11:43:40 +02:00
Koen Punt
b29440e117 Allow instance of iterator in addRoutes 2014-04-16 11:39:57 +02:00
Koen Punt
dbe416b9b9 Add addRoutes method for bulk adding routes 2014-04-16 11:17:42 +02:00
Koen Punt
fc6d743e0a Add support for unicode regular expressions 2014-04-16 11:09:59 +02:00
MathB
5cf275f1b5 Add matching documentation 2014-04-08 23:14:24 +02:00
Koen Punt
fb41766c3d Merge pull request #61 from frosso/patch-1
Typo in example
2014-03-11 19:30:45 +01:00
Francesco
1303c3a0af Typo in example
My IDE is going crazy about this :D
2014-03-11 14:36:05 +01:00
Danny van Kooten
4f8836e179 updated basic example to match new folder structure 2014-03-10 12:09:57 +01:00
niahoo osef
c3c166bd76 Merge pull request #59 from koenpunt/examples
Added examples directory and moved index.php example
2014-03-08 16:34:47 +01:00
Koen Punt
cace1a6f97 Moved example 2014-03-08 11:47:18 +01:00
10 changed files with 297 additions and 87 deletions

8
.gitattributes vendored Normal file
View File

@@ -0,0 +1,8 @@
/.gitattributes export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore
/examples export-ignore
/phpunit.xml.dist export-ignore
/tests export-ignore
/Gemfile export-ignore
/Gemfile.lock export-ignore

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
.idea
_site
Gemfile
Gemfile.lock
build
vendor
composer.lock
phpunit.xml

View File

@@ -3,5 +3,20 @@ php:
- 5.3
- 5.4
- 5.5
script: phpunit ./
- 5.6
- 7.0
- hhvm
install:
- composer update --prefer-source
script:
- ./vendor/bin/phpunit
after_script:
- ./vendor/bin/test-reporter
env:
global:
secure: XnXSc7nxJMIrm/EJ1KuwlN4f+sj2R/sR0IFHdOdbOfMKyZ/u6WEgZ3vNrdeAsisiC/QUJJ00DGku1pAl3t3Hzvam0N/SiHtXjB1ZLVbX00S1PEZ6Z+h9zoaUBXWoN6+0OdKN0Xjmj2lwvTpvUxUZXNabilOw0F9WS/+JasofqBQ=
sudo: false
cache:
directories:
- vendor
- $HOME/.composer/cache

View File

@@ -2,9 +2,24 @@
class AltoRouter {
/**
* @var array Array of all routes (incl. named routes).
*/
protected $routes = array();
/**
* @var array Array of all named routes.
*/
protected $namedRoutes = array();
/**
* @var string Can be used to ignore leading part of the Request URL (if main file lives in subdirectory of host)
*/
protected $basePath = '';
/**
* @var array Array of default match types (regex helpers)
*/
protected $matchTypes = array(
'i' => '[0-9]++',
'a' => '[0-9A-Za-z]++',
@@ -22,11 +37,38 @@ class AltoRouter {
* @param array $matchTypes
*/
public function __construct( $routes = array(), $basePath = '', $matchTypes = array() ) {
$this->addRoutes($routes);
$this->setBasePath($basePath);
$this->addMatchTypes($matchTypes);
}
/**
* Retrieves all routes.
* Useful if you want to process or display routes.
* @return array All routes.
*/
public function getRoutes() {
return $this->routes;
}
foreach( $routes as $route ) {
call_user_func_array(array($this,'map'),$route);
/**
* Add multiple routes at once from array in the following format:
*
* $routes = array(
* array($method, $route, $target, $name)
* );
*
* @param array $routes
* @return void
* @author Koen Punt
* @throws Exception
*/
public function addRoutes($routes){
if(!is_array($routes) && !$routes instanceof Traversable) {
throw new \Exception('Routes should be an array or an instance of Traversable');
}
foreach($routes as $route) {
call_user_func_array(array($this, 'map'), $route);
}
}
@@ -50,11 +92,11 @@ class AltoRouter {
/**
* Map a route to a target
*
* @param string $method One of 4 HTTP Methods, or a pipe-separated list of multiple HTTP Methods (GET|POST|PUT|DELETE)
* @param string $method One of 5 HTTP Methods, or a pipe-separated list of multiple HTTP Methods (GET|POST|PATCH|PUT|DELETE)
* @param string $route The route regex, custom regex must start with an @. You can use multiple pre-set regex filters, like [i:id]
* @param mixed $target The target where this route should point to. Can be anything.
* @param string $name Optional name of this route. Supply if you want to reverse route this url in your application.
*
* @throws Exception
*/
public function map($method, $route, $target, $name = null) {
@@ -80,6 +122,7 @@ class AltoRouter {
* @param string $routeName The name of the route.
* @param array @params Associative array of parameters to replace placeholders with.
* @return string The URL of the route with named parameters in place.
* @throws Exception
*/
public function generate($routeName, array $params = array()) {
@@ -145,10 +188,6 @@ class AltoRouter {
$requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
}
// Force request_order to be GP
// http://www.mail-archive.com/internals@lists.php.net/msg33119.html
$_REQUEST = array_merge($_GET, $_POST);
foreach($this->routes as $handler) {
list($method, $_route, $target, $name) = $handler;
@@ -170,7 +209,8 @@ class AltoRouter {
if ($_route === '*') {
$match = true;
} elseif (isset($_route[0]) && $_route[0] === '@') {
$match = preg_match('`' . substr($_route, 1) . '`', $requestUrl, $params);
$pattern = '`' . substr($_route, 1) . '`u';
$match = preg_match($pattern, $requestUrl, $params);
} else {
$route = null;
$regex = false;
@@ -226,7 +266,7 @@ class AltoRouter {
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
$matchTypes = $this->matchTypes;
foreach ($matches as $match) {
foreach($matches as $match) {
list($block, $pre, $type, $param, $optional) = $match;
if (isset($matchTypes[$type])) {
@@ -249,6 +289,6 @@ class AltoRouter {
}
}
return "`^$route$`";
return "`^$route$`u";
}
}

View File

@@ -1,77 +1,38 @@
# AltoRouter [![Build Status](https://api.travis-ci.org/dannyvankooten/AltoRouter.png)](http://travis-ci.org/dannyvankooten/AltoRouter)
# AltoRouter [![Build Status](https://api.travis-ci.org/dannyvankooten/AltoRouter.png)](http://travis-ci.org/dannyvankooten/AltoRouter) [![Latest Stable Version](https://poser.pugx.org/altorouter/altorouter/v/stable.svg)](https://packagist.org/packages/altorouter/altorouter) [![License](https://poser.pugx.org/altorouter/altorouter/license.svg)](https://packagist.org/packages/altorouter/altorouter) [![Code Climate](https://codeclimate.com/github/dannyvankooten/AltoRouter/badges/gpa.svg)](https://codeclimate.com/github/dannyvankooten/AltoRouter) [![Test Coverage](https://codeclimate.com/github/dannyvankooten/AltoRouter/badges/coverage.svg)](https://codeclimate.com/github/dannyvankooten/AltoRouter)
AltoRouter is a small but powerful routing class for PHP 5.3+, heavily inspired by [klein.php](https://github.com/chriso/klein.php/).
* Dynamic routing with named parameters
```php
$router = new AltoRouter();
// map homepage
$router->map( 'GET', '/', function() {
require __DIR__ . '/views/home.php';
});
// map users details page
$router->map( 'GET|POST', '/users/[i:id]/', function( $id ) {
$user = .....
require __DIR__ . '/views/user/details.php';
});
```
## Features
* Can be used with all HTTP Methods
* Dynamic routing with named route parameters
* Reversed routing
* Flexible regular expression routing (inspired by [Sinatra](http://www.sinatrarb.com/))
* Custom regexes
## Getting started
1. PHP 5.3.x is required
2. Install AltoRouter using Composer or manually
2. Setup URL rewriting so that all requests are handled by **index.php**
3. Create an instance of AltoRouter, map your routes and match a request.
4. Have a look at the example `index.php` file for a better understanding on how to use AltoRouter(index.php).
## Routing
```php
$router = new AltoRouter();
$router->setBasePath('/AltoRouter'); // (optional) the subdir AltoRouter lives in
// mapping routes
$router->map('GET|POST','/', 'home#index', 'home');
$router->map('GET','/users', array('c' => 'UserController', 'a' => 'ListAction'));
$router->map('GET','/users/[i:id]', 'users#show', 'users_show');
$router->map('POST','/users/[i:id]/[delete|update:action]', 'usersController#doAction', 'users_do');
// reversed routing
$router->generate('users_show', array('id' => 5));
```
**You can use the following limits on your named parameters. AltoRouter will create the correct regexes for you.**
```php
* // Match all request URIs
[i] // Match an integer
[i:id] // Match an integer as 'id'
[a:action] // Match alphanumeric characters as 'action'
[h:key] // Match hexadecimal characters as 'key'
[:action] // Match anything up to the next / or end of the URI as 'action'
[create|edit:action] // Match either 'create' or 'edit' as 'action'
[*] // Catch all (lazy, stops at the next trailing slash)
[*:trailing] // Catch all as 'trailing' (lazy)
[**:trailing] // Catch all (possessive - will match the rest of the URI)
.[:format]? // Match an optional parameter 'format' - a / or . before the block is also optional
```
**Some more complicated examples**
```php
@/(?[A-Za-z]{2}_[A-Za-z]{2})$ // custom regex, matches language codes like "en_us" etc.
/posts/[*:title][i:id] // Matches "/posts/this-is-a-title-123"
/output.[xml|json:format]? // Matches "/output", "output.xml", "output.json"
/[:controller]?/[:action]? // Matches the typical /controller/action format
```
**The character before the colon (the 'match type') is a shortcut for one of the following regular expressions**
```php
'i' => '[0-9]++'
'a' => '[0-9A-Za-z]++'
'h' => '[0-9A-Fa-f]++'
'*' => '.+?'
'**' => '.++'
'' => '[^/\.]++'
```
**New match types can be added using the `addMatchTypes()` method**
```php
$router->addMatchTypes(array('cId' => '[a-zA-Z]{2}[0-9](?:_[0-9]++)?'));
```
You need PHP >= 5.3 to use AltoRouter.
- [Install AltoRouter](http://altorouter.com/usage/install.html)
- [Rewrite all requests to AltoRouter](http://altorouter.com/usage/rewrite-requests.html)
- [Map your routes](http://altorouter.com/usage/mapping-routes.html)
- [Match requests](http://altorouter.com/usage/matching-requests.html)
- [Process the request your preferred way](http://altorouter.com/usage/processing-requests.html)
## Contributors
- [Danny van Kooten](https://github.com/dannyvankooten)
@@ -83,7 +44,7 @@ $router->addMatchTypes(array('cId' => '[a-zA-Z]{2}[0-9](?:_[0-9]++)?'));
(MIT License)
Copyright (c) 2012-2013 Danny van Kooten <hi@dannyvankooten.com>
Copyright (c) 2012-2015 Danny van Kooten <hi@dannyvankooten.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -22,7 +22,14 @@
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "4.5.*",
"codeclimate/php-test-reporter": "dev-master"
},
"autoload": {
"classmap": ["AltoRouter.php"]
},
"scripts": {
"test": "vendor/bin/phpunit"
}
}

View File

@@ -1,9 +1,9 @@
<?php
require 'AltoRouter.php';
require '../../AltoRouter.php';
$router = new AltoRouter();
$router->setBasePath('/AltoRouter');
$router->setBasePath('/AltoRouter/examples/basic');
$router->map('GET|POST','/', 'home#index', 'home');
$router->map('GET','/users/', array('c' => 'UserController', 'a' => 'ListAction'));
$router->map('GET','/users/[i:id]', 'users#show', 'users_show');
@@ -12,7 +12,7 @@ $router->map('POST','/users/[i:id]/[delete|update:action]', 'usersController#doA
// match current request
$match = $router->match();
?>
<h1>AltoRouter</h3>
<h1>AltoRouter</h1>
<h3>Current request: </h3>
<pre>

17
phpunit.xml.dist Normal file
View File

@@ -0,0 +1,17 @@
<phpunit
colors="true"
verbose="true">
<testsuites>
<testsuite name="altorouter">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<blacklist>
<directory>./vendor/</directory>
</blacklist>
</filter>
<logging>
<log type="coverage-clover" target="build/logs/clover.xml"/>
</logging>
</phpunit>

View File

@@ -7,17 +7,40 @@ class AltoRouterDebug extends AltoRouter{
public function getNamedRoutes(){
return $this->namedRoutes;
}
public function getRoutes(){
return $this->routes;
}
public function getBasePath(){
return $this->basePath;
}
}
class SimpleTraversable implements Iterator{
protected $_position = 0;
protected $_data = array(
array('GET', '/foo', 'foo_action', null),
array('POST', '/bar', 'bar_action', 'second_route')
);
public function current(){
return $this->_data[$this->_position];
}
public function key(){
return $this->_position;
}
public function next(){
++$this->_position;
}
public function rewind(){
$this->_position = 0;
}
public function valid(){
return isset($this->_data[$this->_position]);
}
}
/**
* Generated by PHPUnit_SkeletonGenerator 1.2.1 on 2013-07-14 at 17:47:46.
*/
@@ -45,6 +68,69 @@ class AltoRouterTest extends PHPUnit_Framework_TestCase
{
}
/**
* @covers AltoRouter::getRoutes
*/
public function testGetRoutes()
{
$method = 'POST';
$route = '/[:controller]/[:action]';
$target = function(){};
$this->assertInternalType('array', $this->router->getRoutes());
$this->router->map($method, $route, $target);
$this->assertEquals(array(array($method, $route, $target, null)), $this->router->getRoutes());
}
/**
* @covers AltoRouter::addRoutes
*/
public function testAddRoutes()
{
$method = 'POST';
$route = '/[:controller]/[:action]';
$target = function(){};
$this->router->addRoutes(array(
array($method, $route, $target),
array($method, $route, $target, 'second_route')
));
$routes = $this->router->getRoutes();
$this->assertEquals(array($method, $route, $target, null), $routes[0]);
$this->assertEquals(array($method, $route, $target, 'second_route'), $routes[1]);
}
/**
* @covers AltoRouter::addRoutes
*/
public function testAddRoutesAcceptsTraverable()
{
$traversable = new SimpleTraversable();
$this->router->addRoutes($traversable);
$traversable->rewind();
$first = $traversable->current();
$traversable->next();
$second = $traversable->current();
$routes = $this->router->getRoutes();
$this->assertEquals($first, $routes[0]);
$this->assertEquals($second, $routes[1]);
}
/**
* @covers AltoRouter::addRoutes
* @expectedException Exception
*/
public function testAddRoutesThrowsExceptionOnInvalidArgument()
{
$this->router->addRoutes(new stdClass);
}
/**
* @covers AltoRouter::setBasePath
*/
@@ -187,6 +273,20 @@ class AltoRouterTest extends PHPUnit_Framework_TestCase
), $this->router->match('/foo/test/do?param=value', 'GET'));
}
public function testMatchWithNonRegex() {
$this->router->map('GET','/about-us', 'PagesController#about', 'about_us');
$this->assertEquals(array(
'target' => 'PagesController#about',
'params' => array(),
'name' => 'about_us'
), $this->router->match('/about-us', 'GET'));
$this->assertFalse($this->router->match('/about-us', 'POST'));
$this->assertFalse($this->router->match('/about', 'GET'));
$this->assertFalse($this->router->match('/about-us-again', 'GET'));
}
public function testMatchWithFixedParamValues()
{
@@ -266,6 +366,36 @@ class AltoRouterTest extends PHPUnit_Framework_TestCase
}
public function testMatchWithUnicodeRegex()
{
$pattern = '/(?<path>[^';
// Arabic characters
$pattern .= '\x{0600}-\x{06FF}';
$pattern .= '\x{FB50}-\x{FDFD}';
$pattern .= '\x{FE70}-\x{FEFF}';
$pattern .= '\x{0750}-\x{077F}';
// Alphanumeric, /, _, - and space characters
$pattern .= 'a-zA-Z0-9\/_\-\s';
// 'ZERO WIDTH NON-JOINER'
$pattern .= '\x{200C}';
$pattern .= ']+)';
$this->router->map('GET', '@' . $pattern, 'unicode_action', 'unicode_route');
$this->assertEquals(array(
'target' => 'unicode_action',
'name' => 'unicode_route',
'params' => array(
'path' => '大家好'
)
), $this->router->match('/大家好', 'GET'));
$this->assertFalse($this->router->match('/﷽‎', 'GET'));
}
/**
* @covers AltoRouter::addMatchTypes
*/
public function testMatchWithCustomNamedRegex()
{
$this->router->addMatchTypes(array('cId' => '[a-zA-Z]{2}[0-9](?:_[0-9]++)?'));
@@ -290,4 +420,28 @@ class AltoRouterTest extends PHPUnit_Framework_TestCase
$this->assertFalse($this->router->match('/some-other-thing', 'GET'));
}
public function testMatchWithCustomNamedUnicodeRegex()
{
$pattern = '[^';
// Arabic characters
$pattern .= '\x{0600}-\x{06FF}';
$pattern .= '\x{FB50}-\x{FDFD}';
$pattern .= '\x{FE70}-\x{FEFF}';
$pattern .= '\x{0750}-\x{077F}';
$pattern .= ']+';
$this->router->addMatchTypes(array('nonArabic' => $pattern));
$this->router->map('GET', '/bar/[nonArabic:string]', 'non_arabic_action', 'non_arabic_route');
$this->assertEquals(array(
'target' => 'non_arabic_action',
'name' => 'non_arabic_route',
'params' => array(
'string' => 'some-path'
)
), $this->router->match('/bar/some-path', 'GET'));
$this->assertFalse($this->router->match('/﷽‎', 'GET'));
}
}