├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── files └── .gitkeep ├── first-route-tests.php ├── run-tests.php └── worst-case-tests.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/* 2 | composer.lock 3 | /files/* 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Tyler Sommer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP Router Benchmark 2 | ==================== 3 | 4 | The intent here is to benchmark different PHP routing solutions. This is a micro-optimization, done purely out of 5 | dumb curiosity. 6 | 7 | 8 | Installation 9 | ------------ 10 | 11 | Clone the repo, run `composer install`, run `php run-tests.php`. 12 | 13 | You can install the [Pux](https://github.com/c9s/pux) extension to test that as well. If the extension is not 14 | installed, the tests will fallback to the pure PHP implementation of Pux. 15 | 16 | To test the [R3 library](https://github.com/c9s/php-r3), you also need to install that extension. If the extension is 17 | not installed, the tests for R3 will be skipped. 18 | 19 | Currently 20 | --------- 21 | 22 | The current test creates 1000 routes, each with a randomized prefix and postfix, with 9 parameters each. 23 | 24 | It was run with the [Pux](https://github.com/c9s/pux) and [R3](https://github.com/c9s/php-r3) extensions enabled. 25 | 26 | An example route: `/9b37eef21e/{arg1}/{arg2}/{arg3}/{arg4}/{arg5}/{arg6}/{arg7}/{arg8}/{arg9}/bda37e9f9b` 27 | 28 | ## Worst-case matching 29 | This benchmark matches the last route and unknown route. It generates a randomly prefixed and suffixed route in an attempt to thwart any optimization. 1,000 routes each with 9 arguments. 30 | 31 | This benchmark consists of 12 tests. Each test is executed 1,000 times, the results pruned, and then averaged. Values that fall outside of 3 standard deviations of the mean are discarded. 32 | 33 | 34 | Test Name | Results | Time | + Interval | Change 35 | --------- | ------- | ---- | ---------- | ------ 36 | r3 - unknown route (1000 routes) | 987 | 0.0000111161 | +0.0000000000 | baseline 37 | r3 - last route (1000 routes) | 994 | 0.0000135476 | +0.0000024316 | 22% slower 38 | FastRoute - unknown route (1000 routes) | 982 | 0.0003966292 | +0.0003855132 | 3468% slower 39 | FastRoute - last route (1000 routes) | 999 | 0.0004029198 | +0.0003918037 | 3525% slower 40 | Pux ext - unknown route (1000 routes) | 984 | 0.0008801297 | +0.0008690136 | 7818% slower 41 | Symfony2 Dumped - unknown route (1000 routes) | 981 | 0.0009883075 | +0.0009771914 | 8791% slower 42 | Pux ext - last route (1000 routes) | 999 | 0.0009942575 | +0.0009831414 | 8844% slower 43 | Symfony2 Dumped - last route (1000 routes) | 978 | 0.0010521817 | +0.0010410656 | 9365% slower 44 | Symfony2 - unknown route (1000 routes) | 989 | 0.0061380323 | +0.0061269163 | 55118% slower 45 | Symfony2 - last route (1000 routes) | 999 | 0.0061715401 | +0.0061604240 | 55419% slower 46 | Aura v2 - last route (1000 routes) | 982 | 0.1814854888 | +0.1814743727 | 1632542% slower 47 | Aura v2 - unknown route (1000 routes) | 977 | 0.1871979547 | +0.1871868386 | 1683932% slower 48 | 49 | 50 | ## First route matching 51 | This benchmark tests how quickly each router can match the first route. 1,000 routes each with 9 arguments. 52 | 53 | This benchmark consists of 6 tests. Each test is executed 1,000 times, the results pruned, and then averaged. Values that fall outside of 3 standard deviations of the mean are discarded. 54 | 55 | 56 | Test Name | Results | Time | + Interval | Change 57 | --------- | ------- | ---- | ---------- | ------ 58 | php-r3 - first route | 989 | 0.0000097053 | +0.0000000000 | baseline 59 | Pux ext - first route | 985 | 0.0000209825 | +0.0000112773 | 116% slower 60 | FastRoute - first route | 999 | 0.0000382496 | +0.0000285443 | 294% slower 61 | Symfony2 Dumped - first route | 991 | 0.0000611150 | +0.0000514098 | 530% slower 62 | Symfony2 - first route | 978 | 0.0002531449 | +0.0002434397 | 2508% slower 63 | Aura v2 - first route | 985 | 0.0003834265 | +0.0003737212 | 3851% slower 64 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "nice/bench": "~1.0", 4 | "nikic/fast-route": "dev-master", 5 | "corneltek/pux": "dev-master", 6 | "symfony/routing": "dev-master", 7 | "aura/router": "2.x-dev" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /files/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veonik/php-router-benchmark/c32077c74a268973dfe8fafd764f06016ff367c8/files/.gitkeep -------------------------------------------------------------------------------- /first-route-tests.php: -------------------------------------------------------------------------------- 1 | setDescription(sprintf( 23 | 'This benchmark tests how quickly each router can match the first route. %s routes each with %s arguments.', 24 | number_format($numRoutes), 25 | $numArgs 26 | )); 27 | 28 | setupAura2($benchmark, $numRoutes, $numArgs); 29 | setupFastRoute($benchmark, $numRoutes, $numArgs); 30 | if (extension_loaded('r3')) { 31 | setupR3($benchmark, $numRoutes, $numArgs); 32 | } else { 33 | echo "R3 extension is not loaded. Skipping initialization for \"First route matching\" test using R3.\n"; 34 | } 35 | 36 | setupSymfony2($benchmark, $numRoutes, $numArgs); 37 | setupSymfony2Optimized($benchmark, $numRoutes, $numArgs); 38 | setupPux($benchmark, $numRoutes, $numArgs); 39 | 40 | return $benchmark; 41 | } 42 | 43 | function getRandomParts() 44 | { 45 | $rand = md5(uniqid(mt_rand(), true)); 46 | 47 | return array( 48 | substr($rand, 0, 10), 49 | substr($rand, -10), 50 | ); 51 | } 52 | 53 | 54 | /** 55 | * Sets up R3 tests 56 | */ 57 | function setupR3(Benchmark $benchmark, $routes, $args) 58 | { 59 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 60 | $str = $firstStr = $lastStr = ''; 61 | $router = r3_tree_create_persist("app", 10); 62 | if (!r3_tree_is_compiled($router)) { 63 | for ($i = 0; $i < $routes; $i++) { 64 | list ($pre, $post) = getRandomParts(); 65 | $str = '/' . $pre . '/' . $argString . '/' . $post; 66 | if (0 === $i) { 67 | $firstStr = str_replace(array('{', '}'), '', $str); 68 | } 69 | $lastStr = str_replace(array('{', '}'), '', $str); 70 | r3_tree_insert($router, $str, "handler" . $i); 71 | } 72 | r3_tree_compile($router); 73 | } 74 | 75 | $benchmark->register(sprintf('php-r3 - first route', $routes), function () use ($router, $firstStr) { 76 | $data = r3_tree_match($router, $firstStr); 77 | }); 78 | } 79 | 80 | 81 | 82 | 83 | /** 84 | * Sets up FastRoute tests 85 | */ 86 | function setupFastRoute(Benchmark $benchmark, $routes, $args) 87 | { 88 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 89 | $str = $firstStr = $lastStr = ''; 90 | $router = \FastRoute\simpleDispatcher(function ($router) use ($routes, $argString, &$lastStr, &$firstStr) { 91 | for ($i = 0; $i < $routes; $i++) { 92 | list ($pre, $post) = getRandomParts(); 93 | $str = '/' . $pre . '/' . $argString . '/' . $post; 94 | 95 | if (0 === $i) { 96 | $firstStr = str_replace(array('{', '}'), '', $str); 97 | } 98 | $lastStr = str_replace(array('{', '}'), '', $str); 99 | 100 | $router->addRoute('GET', $str, 'handler' . $i); 101 | } 102 | }); 103 | 104 | $benchmark->register(sprintf('FastRoute - first route', $routes), function () use ($router, $firstStr) { 105 | $route = $router->dispatch('GET', $firstStr); 106 | }); 107 | } 108 | 109 | /** 110 | * Sets up Pux tests 111 | */ 112 | function setupPux(Benchmark $benchmark, $routes, $args) 113 | { 114 | $name = extension_loaded('pux') ? 'Pux ext' : 'Pux PHP'; 115 | $argString = implode('/', array_map(function ($i) { return ':arg' . $i; }, range(1, $args))); 116 | $str = $firstStr = $lastStr = ''; 117 | $router = new \Pux\Mux; 118 | for ($i = 0; $i < $routes; $i++) { 119 | list ($pre, $post) = getRandomParts(); 120 | $str = '/' . $pre . '/' . $argString . '/' . $post; 121 | 122 | if (0 === $i) { 123 | $firstStr = str_replace(':', '', $str); 124 | } 125 | $lastStr = str_replace(':', '', $str); 126 | 127 | $router->add($str, 'handler' . $i); 128 | } 129 | 130 | $benchmark->register(sprintf('%s - first route', $name), function () use ($router, $firstStr) { 131 | $route = $router->match($firstStr); 132 | }); 133 | } 134 | 135 | /** 136 | * Sets up Symfony 2 tests 137 | */ 138 | function setupSymfony2(Benchmark $benchmark, $routes, $args) 139 | { 140 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 141 | $str = $firstStr = $lastStr = ''; 142 | $sfRoutes = new \Symfony\Component\Routing\RouteCollection(); 143 | $router = new \Symfony\Component\Routing\Matcher\UrlMatcher($sfRoutes, new \Symfony\Component\Routing\RequestContext()); 144 | for ($i = 0, $str = 'a'; $i < $routes; $i++, $str++) { 145 | list ($pre, $post) = getRandomParts(); 146 | $str = '/' . $pre . '/' . $argString . '/' . $post; 147 | 148 | if (0 === $i) { 149 | $firstStr = str_replace(array('{', '}'), '', $str); 150 | } 151 | $lastStr = str_replace(array('{', '}'), '', $str); 152 | 153 | $sfRoutes->add($str, new \Symfony\Component\Routing\Route($str, array('controller' => 'handler' . $i))); 154 | } 155 | 156 | $benchmark->register('Symfony2 - first route', function () use ($router, $firstStr) { 157 | $route = $router->match($firstStr); 158 | }); 159 | } 160 | 161 | /** 162 | * Sets up Symfony2 optimized tests 163 | */ 164 | function setupSymfony2Optimized(Benchmark $benchmark, $routes, $args) 165 | { 166 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 167 | $str = $firstStr = $lastStr = ''; 168 | $sfRoutes = new \Symfony\Component\Routing\RouteCollection(); 169 | for ($i = 0, $str = 'a'; $i < $routes; $i++, $str++) { 170 | list ($pre, $post) = getRandomParts(); 171 | $str = '/' . $pre . '/' . $argString . '/' . $post; 172 | 173 | if (0 === $i) { 174 | $firstStr = str_replace(array('{', '}'), '', $str); 175 | } 176 | $lastStr = str_replace(array('{', '}'), '', $str); 177 | 178 | $sfRoutes->add($str, new \Symfony\Component\Routing\Route($str, array('controller' => 'handler' . $i))); 179 | } 180 | $dumper = new \Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper($sfRoutes); 181 | file_put_contents(__DIR__ . '/files/first-route-sf2.php', $dumper->dump(array( 182 | 'class' => 'FirstRouteSf2UrlMatcher' 183 | ))); 184 | require_once __DIR__ . '/files/first-route-sf2.php'; 185 | 186 | $router = new \FirstRouteSf2UrlMatcher(new \Symfony\Component\Routing\RequestContext()); 187 | 188 | $benchmark->register('Symfony2 Dumped - first route', function () use ($router, $firstStr) { 189 | $route = $router->match($firstStr); 190 | }); 191 | } 192 | 193 | /** 194 | * Sets up Aura v2 tests 195 | * 196 | * https://github.com/auraphp/Aura.Router 197 | */ 198 | function setupAura2(Benchmark $benchmark, $routes, $args) 199 | { 200 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 201 | $lastStr = ''; 202 | $router = new \Aura\Router\Router( 203 | new \Aura\Router\RouteCollection( 204 | new \Aura\Router\RouteFactory() 205 | ), 206 | new \Aura\Router\Generator() 207 | ); 208 | for ($i = 0, $str = 'a'; $i < $routes; $i++, $str++) { 209 | list ($pre, $post) = getRandomParts(); 210 | $str = '/' . $pre . '/' . $argString . '/' . $post; 211 | 212 | if (0 === $i) { 213 | $firstStr = str_replace(array('{', '}'), '', $str); 214 | } 215 | $lastStr = str_replace(array('{', '}'), '', $str); 216 | 217 | $router->add($str, $str) 218 | ->addValues(array( 219 | 'controller' => 'handler' . $i 220 | )); 221 | } 222 | 223 | $benchmark->register('Aura v2 - first route', function () use ($router, $firstStr) { 224 | $route = $router->match($firstStr); 225 | }); 226 | } 227 | -------------------------------------------------------------------------------- /run-tests.php: -------------------------------------------------------------------------------- 1 | addBenchmark($worstCaseBenchmark); 21 | $collection->addBenchmark($firstRouteBenchmark); 22 | $collection->execute(); 23 | -------------------------------------------------------------------------------- /worst-case-tests.php: -------------------------------------------------------------------------------- 1 | setDescription(sprintf( 24 | 'This benchmark matches the last route and unknown route. It generates a randomly prefixed and suffixed route in an attempt to thwart any optimization. %s routes each with %s arguments.', 25 | number_format($numRoutes), 26 | $numArgs 27 | )); 28 | 29 | setupAura2($benchmark, $numRoutes, $numArgs); 30 | setupFastRoute($benchmark, $numRoutes, $numArgs); 31 | if (extension_loaded('r3')) { 32 | setupR3($benchmark, $numRoutes, $numArgs); 33 | } else { 34 | echo "R3 extension is not loaded. Skipping initialization for \"Worst-case matching\" test using R3.\n"; 35 | } 36 | 37 | setupSymfony2($benchmark, $numRoutes, $numArgs); 38 | setupSymfony2Optimized($benchmark, $numRoutes, $numArgs); 39 | setupPux($benchmark, $numRoutes, $numArgs); 40 | 41 | return $benchmark; 42 | } 43 | 44 | function getRandomParts() 45 | { 46 | $rand = md5(uniqid(mt_rand(), true)); 47 | 48 | return array( 49 | substr($rand, 0, 10), 50 | substr($rand, -10), 51 | ); 52 | } 53 | 54 | function setupR3(Benchmark $benchmark, $routes, $args) 55 | { 56 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 57 | $str = $firstStr = $lastStr = ''; 58 | $router = r3_tree_create_persist("app", 10); 59 | if (!r3_tree_is_compiled($router)) { 60 | for ($i = 0; $i < $routes; $i++) { 61 | list ($pre, $post) = getRandomParts(); 62 | $str = '/' . $pre . '/' . $argString . '/' . $post; 63 | if (0 === $i) { 64 | $firstStr = str_replace(array('{', '}'), '', $str); 65 | } 66 | $lastStr = str_replace(array('{', '}'), '', $str); 67 | r3_tree_insert($router, $str, "handler" . $i); 68 | } 69 | r3_tree_compile($router); 70 | } 71 | 72 | $benchmark->register(sprintf('r3 - last route (%s routes)', $routes), function () use ($router, $lastStr) { 73 | $data = r3_tree_match($router, $lastStr); 74 | }); 75 | 76 | $benchmark->register(sprintf('r3 - unknown route (%s routes)', $routes), function () use ($router) { 77 | $data = r3_tree_match($router, "/not-even-real"); 78 | }); 79 | 80 | 81 | } 82 | 83 | /** 84 | * Sets up FastRoute tests 85 | */ 86 | function setupFastRoute(Benchmark $benchmark, $routes, $args) 87 | { 88 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 89 | $str = $firstStr = $lastStr = ''; 90 | $router = \FastRoute\simpleDispatcher(function ($router) use ($routes, $argString, &$lastStr) { 91 | for ($i = 0; $i < $routes; $i++) { 92 | list ($pre, $post) = getRandomParts(); 93 | $str = '/' . $pre . '/' . $argString . '/' . $post; 94 | 95 | if (0 === $i) { 96 | $firstStr = str_replace(array('{', '}'), '', $str); 97 | } 98 | $lastStr = str_replace(array('{', '}'), '', $str); 99 | 100 | $router->addRoute('GET', $str, 'handler' . $i); 101 | } 102 | }); 103 | 104 | $benchmark->register(sprintf('FastRoute - last route (%s routes)', $routes), function () use ($router, $lastStr) { 105 | $route = $router->dispatch('GET', $lastStr); 106 | }); 107 | 108 | $benchmark->register(sprintf('FastRoute - unknown route (%s routes)', $routes), function () use ($router) { 109 | $route = $router->dispatch('GET', '/not-even-real'); 110 | }); 111 | } 112 | 113 | /** 114 | * Sets up Pux tests 115 | */ 116 | function setupPux(Benchmark $benchmark, $routes, $args) 117 | { 118 | $name = extension_loaded('pux') ? 'Pux ext' : 'Pux PHP'; 119 | 120 | $argString = implode('/', array_map(function ($i) { return ':arg' . $i; }, range(1, $args))); 121 | $str = $firstStr = $lastStr = ''; 122 | $router = new \Pux\Mux; 123 | for ($i = 0; $i < $routes; $i++) { 124 | list ($pre, $post) = getRandomParts(); 125 | $str = '/' . $pre . '/' . $argString . '/' . $post; 126 | 127 | if (0 === $i) { 128 | $firstStr = str_replace(':', '', $str); 129 | } 130 | $lastStr = str_replace(':', '', $str); 131 | 132 | $router->add($str, 'handler' . $i); 133 | } 134 | 135 | $benchmark->register(sprintf('%s - last route (%s routes)', $name, $routes), function () use ($router, $lastStr) { 136 | $route = $router->match($lastStr); 137 | }); 138 | 139 | $benchmark->register(sprintf('%s - unknown route (%s routes)', $name, $routes), function () use ($router) { 140 | $route = $router->match('/not-even-real'); 141 | }); 142 | } 143 | 144 | /** 145 | * Sets up Symfony 2 tests 146 | */ 147 | function setupSymfony2(Benchmark $benchmark, $routes, $args) 148 | { 149 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 150 | $str = $firstStr = $lastStr = ''; 151 | $sfRoutes = new \Symfony\Component\Routing\RouteCollection(); 152 | $router = new \Symfony\Component\Routing\Matcher\UrlMatcher($sfRoutes, new \Symfony\Component\Routing\RequestContext()); 153 | for ($i = 0, $str = 'a'; $i < $routes; $i++, $str++) { 154 | list ($pre, $post) = getRandomParts(); 155 | $str = '/' . $pre . '/' . $argString . '/' . $post; 156 | 157 | if (0 === $i) { 158 | $firstStr = str_replace(array('{', '}'), '', $str); 159 | } 160 | $lastStr = str_replace(array('{', '}'), '', $str); 161 | 162 | $sfRoutes->add($str, new \Symfony\Component\Routing\Route($str, array('controller' => 'handler' . $i))); 163 | } 164 | 165 | $benchmark->register(sprintf('Symfony2 - last route (%s routes)', $routes), function () use ($router, $lastStr) { 166 | $route = $router->match($lastStr); 167 | }); 168 | 169 | $benchmark->register(sprintf('Symfony2 - unknown route (%s routes)', $routes), function () use ($router) { 170 | try { 171 | $route = $router->match('/not-even-real'); 172 | } catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { } 173 | }); 174 | } 175 | 176 | /** 177 | * Sets up Symfony2 optimized tests 178 | */ 179 | function setupSymfony2Optimized(Benchmark $benchmark, $routes, $args) 180 | { 181 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 182 | $str = $firstStr = $lastStr = ''; 183 | $sfRoutes = new \Symfony\Component\Routing\RouteCollection(); 184 | for ($i = 0, $str = 'a'; $i < $routes; $i++, $str++) { 185 | list ($pre, $post) = getRandomParts(); 186 | $str = '/' . $pre . '/' . $argString . '/' . $post; 187 | 188 | if (0 === $i) { 189 | $firstStr = str_replace(array('{', '}'), '', $str); 190 | } 191 | $lastStr = str_replace(array('{', '}'), '', $str); 192 | 193 | $sfRoutes->add($str, new \Symfony\Component\Routing\Route($str, array('controller' => 'handler' . $i))); 194 | } 195 | $dumper = new \Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper($sfRoutes); 196 | file_put_contents(__DIR__ . '/files/worst-case-sf2.php', $dumper->dump(array( 197 | 'class' => 'WorstCaseSf2UrlMatcher' 198 | ))); 199 | require_once __DIR__ . '/files/worst-case-sf2.php'; 200 | 201 | $router = new \WorstCaseSf2UrlMatcher(new \Symfony\Component\Routing\RequestContext()); 202 | 203 | $benchmark->register(sprintf('Symfony2 Dumped - last route (%s routes)', $routes), function () use ($router, $lastStr) { 204 | $route = $router->match($lastStr); 205 | }); 206 | 207 | $benchmark->register(sprintf('Symfony2 Dumped - unknown route (%s routes)', $routes), function () use ($router) { 208 | try { 209 | $route = $router->match('/not-even-real'); 210 | } catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { } 211 | }); 212 | } 213 | 214 | /** 215 | * Sets up Aura v2 tests 216 | * 217 | * https://github.com/auraphp/Aura.Router 218 | */ 219 | function setupAura2(Benchmark $benchmark, $routes, $args) 220 | { 221 | $argString = implode('/', array_map(function ($i) { return "{arg$i}"; }, range(1, $args))); 222 | $lastStr = ''; 223 | $router = new \Aura\Router\Router( 224 | new \Aura\Router\RouteCollection( 225 | new \Aura\Router\RouteFactory() 226 | ), 227 | new \Aura\Router\Generator() 228 | ); 229 | for ($i = 0, $str = 'a'; $i < $routes; $i++, $str++) { 230 | list ($pre, $post) = getRandomParts(); 231 | $str = '/' . $pre . '/' . $argString . '/' . $post; 232 | 233 | if (0 === $i) { 234 | $firstStr = str_replace(array('{', '}'), '', $str); 235 | } 236 | $lastStr = str_replace(array('{', '}'), '', $str); 237 | 238 | $router->add($str, $str) 239 | ->addValues(array( 240 | 'controller' => 'handler' . $i 241 | )); 242 | } 243 | 244 | $benchmark->register(sprintf('Aura v2 - last route (%s routes)', $routes), function () use ($router, $lastStr) { 245 | $route = $router->match($lastStr, $_SERVER); 246 | }); 247 | 248 | $benchmark->register(sprintf('Aura v2 - unknown route (%s routes)', $routes), function () use ($router) { 249 | $route = $router->match('/not-even-real', $_SERVER); 250 | }); 251 | } 252 | --------------------------------------------------------------------------------