├── .gitignore ├── .idea ├── vcs.xml ├── .gitignore ├── phpspec.xml ├── modules.xml ├── inspectionProfiles │ └── Project_Default.xml ├── php.xml ├── php-router-benchmark.iml └── workspace (kristoffers-mbp.lan's conflicted copy 2025-01-31).xml ├── docker ├── caddy │ └── Caddyfile ├── fpm │ ├── Dockerfile │ └── php.ini └── cli │ ├── php.ini │ └── run.sh ├── src ├── Generators │ ├── ClosureTest.php │ ├── ClassTest.php │ ├── TestClassSimple.php │ ├── TestRouteBuilder.php │ └── TestGenerator.php ├── Routers │ ├── Klein │ │ ├── KleinSimpleClass.php │ │ ├── KleinRouterDefinition.php │ │ └── KleinRouterAdapter.php │ ├── Bramus │ │ ├── BramusClassSimple.php │ │ ├── BramusResponseClass.php │ │ ├── BramusRouterDefinition.php │ │ └── BramusRouterAdapter.php │ ├── AdapterInterface.php │ ├── Nette │ │ ├── NetteRouterDefinition.php │ │ └── NetteRouterAdapter.php │ ├── Jaunt │ │ ├── JauntRouterDefinition.php │ │ └── JauntRouterAdapter.php │ ├── Sharkk │ │ ├── SharkkRouterDefinition.php │ │ └── SharkkRouterAdapter.php │ ├── PHRoute │ │ ├── PHRouteRouteDefinition.php │ │ └── PHRouteRouteAdapter.php │ ├── Laravel │ │ ├── LaravelRouteDefinition.php │ │ └── LaravelRouteAdapter.php │ ├── FastRoute │ │ ├── FastRouteRouteDefinition.php │ │ └── FastRouteRouteAdapter.php │ ├── Symfony │ │ ├── SymfonyRouterDefinition.php │ │ └── SymfonyRouterAdapter.php │ ├── AltoRouter │ │ ├── AltoRouterDefinition.php │ │ └── AltoRouterAdapter.php │ └── Rammewerk │ │ ├── RammewerkRouterDefinition.php │ │ └── RammewerkRouterAdapter.php ├── TestCode │ ├── test_suite_1.php │ ├── test_suite_8.php │ ├── test_suite_3.php │ ├── test_suite_2.php │ ├── test_suite_7.php │ ├── test_suite_4.php │ ├── test_suite_9.php │ ├── test_suite_5.php │ └── test_suite_6.php ├── TestSuites │ ├── TestSuite1.php │ ├── TestSuite9.php │ ├── TestSuite7.php │ ├── TestSuite4.php │ ├── TestSuite2.php │ ├── TestSuite6.php │ ├── TestSuite5.php │ ├── TestSuite8.php │ └── TestSuite3.php ├── TestSuites.php ├── Packages.php └── TestData │ ├── test_2.json │ ├── test_4.json │ ├── test_3.json │ └── test_9.json ├── core ├── Setup │ ├── TestSuiteInterface.php │ ├── PackageDefinitionInterface.php │ ├── TestRunner.php │ └── Result.php ├── benchmark.php ├── Utilities │ ├── FileSaver.php │ └── Color.php └── Benchmark │ ├── Benchmark.php │ ├── BenchmarkResult.php │ └── ReadmeGenerator.php ├── phpstan.neon ├── public └── index.php ├── docker-compose.yml ├── composer.json ├── benchmark.sh ├── preload.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | .idea/ -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /docker/caddy/Caddyfile: -------------------------------------------------------------------------------- 1 | :80 { 2 | root * /code/public 3 | file_server 4 | 5 | # Forward PHP requests to the PHP-FPM service named 'fpm' 6 | php_fastcgi /* fpm:9000 7 | 8 | # Handle static files efficiently 9 | try_files {path} {path}/ /index.php?{query} 10 | } -------------------------------------------------------------------------------- /.idea/phpspec.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Generators/ClosureTest.php: -------------------------------------------------------------------------------- 1 | one . $request->two; 11 | } 12 | 13 | 14 | 15 | public static function twoAbout(Request $request): string { 16 | return $request->one . $request->two; 17 | } 18 | 19 | 20 | 21 | public static function twoContact(Request $request): string { 22 | return $request->one . $request->two; 23 | } 24 | 25 | 26 | } -------------------------------------------------------------------------------- /src/Routers/Bramus/BramusClassSimple.php: -------------------------------------------------------------------------------- 1 | setContent($one . $two); 9 | } 10 | 11 | 12 | 13 | public static function twoAbout(string $one, string $two): void { 14 | BramusResponseClass::getInstance()->setContent($one . $two); 15 | } 16 | 17 | 18 | 19 | public static function twoContact(string $one, string $two): void { 20 | BramusResponseClass::getInstance()->setContent($one . $two); 21 | } 22 | 23 | 24 | } -------------------------------------------------------------------------------- /src/TestCode/test_suite_1.php: -------------------------------------------------------------------------------- 1 | initialize(); 22 | } 23 | 24 | 25 | $timeEnd = hrtime(true); 26 | return Result::fromMeasurement($timeStart, $timeEnd, memory_get_peak_usage()); -------------------------------------------------------------------------------- /src/Routers/Bramus/BramusResponseClass.php: -------------------------------------------------------------------------------- 1 | content = $content; 23 | } 24 | 25 | 26 | 27 | public function getContent(): string { 28 | return $this->content; 29 | } 30 | 31 | 32 | } -------------------------------------------------------------------------------- /src/Routers/AdapterInterface.php: -------------------------------------------------------------------------------- 1 | getAllDefinitions(); 21 | 22 | // Load the output generator 23 | $outputGenerator = new ReadmeGenerator(ROOT_DIR . 'result/README.md'); 24 | 25 | // Run the benchmark 26 | $benchmark = new Benchmark(); 27 | $benchmark->run($testSuites, $packageDefinitions, $outputGenerator); 28 | 29 | Color::print("Benchmark completed. Results saved to readme file under result directory", [ 30 | Color::GREEN, 31 | Color::BOLD, 32 | ]); -------------------------------------------------------------------------------- /src/TestSuites/TestSuite7.php: -------------------------------------------------------------------------------- 1 | '; 25 | print_r($data); 26 | die; 27 | } 28 | 29 | $package = $_GET['package'] ?? null; 30 | $suite = $_GET['suite'] ?? null; 31 | 32 | if ($package === null) { 33 | Result::unsuccessful('Missing package parameter')->printJsonResult(); 34 | } 35 | if ($suite === null) { 36 | Result::unsuccessful('Missing suite parameter')->printJsonResult(); 37 | } 38 | 39 | try { 40 | new TestRunner()->run($package, (int)$suite)->printJsonResult(); 41 | } catch (\Throwable $exception) { 42 | Result::unsuccessful($exception->getMessage())->printJsonResult(); 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/TestSuites/TestSuite6.php: -------------------------------------------------------------------------------- 1 | definitions = new Packages(); 20 | } 21 | 22 | 23 | 24 | public function run(string $package_name, int $suite): Result { 25 | 26 | $definition = $this->definitions->getDefinition($package_name) 27 | ?? throw new LogicException("Package \"$package_name\" doesn't exist"); 28 | 29 | $testSuite = TestSuites::getTestSuite($suite) 30 | ?? throw new LogicException("Test suite \"$suite\" doesn't exist"); 31 | 32 | $adapter = $definition->getAdapter(); 33 | 34 | try { 35 | return require PROJECT_ROOT . "src/TestCode/{$testSuite->getFileName()}.php"; 36 | } catch (Throwable $exception) { 37 | return new Result(message: $exception->getMessage()); 38 | } 39 | 40 | 41 | } 42 | 43 | 44 | } -------------------------------------------------------------------------------- /src/TestSuites/TestSuite8.php: -------------------------------------------------------------------------------- 1 | router = new SegmentRouter(); 17 | } 18 | 19 | 20 | 21 | public function dispatch(string $path): string { 22 | $result = $this->router->lookup('GET', $path); 23 | return call_user_func_array($result['handler'], $result['params']); 24 | } 25 | 26 | 27 | 28 | public function registerClosureRoutes(array $tests): void { 29 | foreach ($tests as $test) { 30 | $this->router->add('GET', $test->registerPath, $test->closure); 31 | } 32 | } 33 | 34 | 35 | 36 | public function registerClassRoutes(array $tests): void { 37 | foreach ($tests as $test) { 38 | $this->router->add('GET', $test->registerPath, [new $test->class, $test->method]); 39 | } 40 | } 41 | 42 | 43 | 44 | public function wildcardReplacements(string $path): string { 45 | return TestGenerator::replaceWildcards($path, [':one', ':two', ':three']); 46 | } 47 | 48 | 49 | 50 | } -------------------------------------------------------------------------------- /src/Routers/Rammewerk/RammewerkRouterAdapter.php: -------------------------------------------------------------------------------- 1 | router = new Router(); 20 | } 21 | 22 | 23 | 24 | /** 25 | * @throws InvalidRoute 26 | */ 27 | public function dispatch(string $path): string { 28 | return $this->router->dispatch($path); 29 | } 30 | 31 | 32 | 33 | public function registerClosureRoutes(array $tests): void { 34 | foreach ($tests as $test) { 35 | $this->router->add($test->registerPath, $test->closure)->disableReflection(); 36 | } 37 | } 38 | 39 | 40 | 41 | public function registerClassRoutes(array $tests): void { 42 | foreach ($tests as $test) { 43 | $this->router->add($test->registerPath, $test->class)->classMethod($test->method); 44 | } 45 | } 46 | 47 | 48 | 49 | public function wildcardReplacements(string $path): string { 50 | return rtrim(TestGenerator::replaceWildcards($path, ['*', '*', '*']), '*/'); 51 | } 52 | 53 | 54 | } -------------------------------------------------------------------------------- /src/Routers/PHRoute/PHRouteRouteAdapter.php: -------------------------------------------------------------------------------- 1 | router = new RouteCollector(); 21 | 22 | } 23 | 24 | 25 | 26 | public function registerClosureRoutes(array $tests): void { 27 | foreach ($tests as $test) { 28 | $this->router->get($test->registerPath, $test->closure); 29 | } 30 | } 31 | 32 | 33 | 34 | public function registerClassRoutes(array $tests): void { 35 | foreach ($tests as $test) { 36 | $this->router->get($test->registerPath, [$test->class, $test->method]); 37 | } 38 | } 39 | 40 | 41 | 42 | public function dispatch(string $path): string { 43 | return new Dispatcher($this->router->getData())->dispatch('GET', $path); 44 | } 45 | 46 | 47 | 48 | public function wildcardReplacements(string $path): string { 49 | return TestGenerator::replaceWildcards($path, ['{one}', '{two}', '{three}']); 50 | } 51 | 52 | 53 | 54 | } -------------------------------------------------------------------------------- /src/Routers/Jaunt/JauntRouterAdapter.php: -------------------------------------------------------------------------------- 1 | router = new Router(); 16 | } 17 | 18 | 19 | 20 | public function registerClosureRoutes(array $tests): void { 21 | foreach ($tests as $test) { 22 | $this->router->get($test->registerPath, $test->closure); 23 | } 24 | } 25 | 26 | 27 | 28 | public function registerClassRoutes(array $tests): void { 29 | foreach ($tests as $test) { 30 | $this->router->get($test->registerPath, [$test->class, $test->method]); 31 | } 32 | } 33 | 34 | 35 | 36 | public function dispatch(string $path): string { 37 | $route = $this->router->match('GET', $path); 38 | if (isset($route['stack'][0]) && $route['stack'][0] instanceof Closure) { 39 | $handler = $route['stack'][0]; 40 | } else { 41 | $handler = [new $route['stack'][0](), $route['stack'][1]]; 42 | } 43 | return call_user_func_array($handler, array_values($route['params'])); 44 | } 45 | 46 | 47 | public function wildcardReplacements(string $path): string { 48 | return TestGenerator::replaceWildcards($path, [':one', ':two:', ':three']); 49 | } 50 | } -------------------------------------------------------------------------------- /src/Routers/Klein/KleinRouterAdapter.php: -------------------------------------------------------------------------------- 1 | router = new Klein(); 20 | $this->request = Request::createFromGlobals(); 21 | } 22 | 23 | 24 | 25 | public function registerClosureRoutes(array $tests): void { 26 | foreach ($tests as $test) { 27 | $this->router->respond('GET', $test->registerPath, $test->closure); 28 | } 29 | } 30 | 31 | 32 | 33 | public function registerClassRoutes(array $tests): void { 34 | foreach ($tests as $test) { 35 | $this->router->respond('GET', $test->registerPath, [KleinSimpleClass::class, $test->method]); 36 | } 37 | } 38 | 39 | 40 | 41 | public function dispatch(string $path): string { 42 | $this->request->server()->set('REQUEST_URI', $path); 43 | $this->router->dispatch($this->request, capture: Klein::DISPATCH_CAPTURE_AND_RETURN); 44 | return $this->router->response()->body(); 45 | } 46 | 47 | 48 | 49 | public function wildcardReplacements(string $path): string { 50 | return TestGenerator::replaceWildcards($path, ['[:one]', '[:two]', '[:three]']); 51 | } 52 | 53 | 54 | 55 | } -------------------------------------------------------------------------------- /src/Routers/AltoRouter/AltoRouterAdapter.php: -------------------------------------------------------------------------------- 1 | router = new \AltoRouter(); 16 | } 17 | 18 | 19 | 20 | public function registerClosureRoutes(array $tests): void { 21 | foreach ($tests as $test) { 22 | $this->router->map('GET', $test->registerPath, $test->closure); 23 | } 24 | } 25 | 26 | 27 | 28 | public function registerClassRoutes(array $tests): void { 29 | foreach ($tests as $test) { 30 | $this->router->map('GET', $test->registerPath, [$test->class, $test->method]); 31 | } 32 | } 33 | 34 | 35 | 36 | public function dispatch(string $path): string { 37 | 38 | $match = $this->router->match($path, 'GET'); 39 | 40 | if (is_array($match)) { 41 | if ($match['target'] instanceof \Closure) { 42 | return call_user_func_array($match['target'], $match['params']); 43 | } 44 | return call_user_func_array([new $match['target'][0], $match['target'][1]], $match['params']); 45 | } 46 | 47 | throw new \RuntimeException("No route was matched: $path"); 48 | 49 | } 50 | 51 | 52 | 53 | public function wildcardReplacements(string $path): string { 54 | return TestGenerator::replaceWildcards($path, ['[:one]', '[:two]', '[:three]']); 55 | } 56 | 57 | 58 | } -------------------------------------------------------------------------------- /src/TestSuites.php: -------------------------------------------------------------------------------- 1 | new TestSuite1(), 43 | 2 => new TestSuite2(), 44 | 3 => new TestSuite3(), 45 | 4 => new TestSuite4(), 46 | 5 => new TestSuite5(), 47 | 6 => new TestSuite6(), 48 | 7 => new TestSuite7(), 49 | 8 => new TestSuite8(), 50 | 9 => new TestSuite9(), 51 | default => null, 52 | }; 53 | } 54 | 55 | 56 | } -------------------------------------------------------------------------------- /src/Packages.php: -------------------------------------------------------------------------------- 1 | getAllDefinitions(), 44 | static fn($definition) => $definition->getName() === $name, 45 | ); 46 | } 47 | 48 | 49 | } -------------------------------------------------------------------------------- /benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | PROJECT_ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 5 | 6 | if [[ "$1" == "composer" ]]; then 7 | 8 | # Make sure a composer subcommand is provided 9 | if [[ -z "$2" ]]; then 10 | echo 'Please provide a composer subcommand (e.g., "require") as the second argument' 11 | exit 1 12 | fi 13 | 14 | # Run the composer command with additional arguments 15 | shift # Remove the "composer" argument 16 | docker run --rm --interactive --tty \ 17 | --volume $PROJECT_ROOT:/code \ 18 | --user $(id -u):$(id -g) \ 19 | composer "$@" --prefer-dist --no-interaction --working-dir=/code --ignore-platform-reqs 20 | 21 | docker run --rm --interactive --tty \ 22 | --volume $PROJECT_ROOT:/code \ 23 | --user $(id -u):$(id -g) \ 24 | composer dump-autoload --classmap-authoritative --no-interaction --working-dir=/code 25 | 26 | 27 | elif [[ "$1" == "run" ]]; then 28 | 29 | # Install composer dependencies 30 | docker run --rm --interactive --tty \ 31 | --volume $PROJECT_ROOT:/code \ 32 | --user $(id -u):$(id -g) \ 33 | composer install --prefer-dist --no-interaction --working-dir=/code --ignore-platform-reqs 34 | 35 | # Dump autoloader 36 | docker run --rm --interactive --tty \ 37 | --volume $PROJECT_ROOT:/code \ 38 | --user $(id -u):$(id -g) \ 39 | composer dump-autoload --classmap-authoritative --no-interaction --working-dir=/code 40 | 41 | # Run the benchmark 42 | docker compose up --build -d 43 | 44 | docker compose run --rm benchmark-cli php core/benchmark.php 45 | 46 | else 47 | 48 | echo 'Available options: "composer"' 49 | exit 1 50 | 51 | fi 52 | -------------------------------------------------------------------------------- /src/Routers/Bramus/BramusRouterAdapter.php: -------------------------------------------------------------------------------- 1 | router = new Router(); 17 | $this->router->set404(function () { 18 | header('HTTP/1.1 404 Not Found'); 19 | echo '404 Route Not Found'; 20 | die; 21 | }); 22 | } 23 | 24 | 25 | 26 | public function getResponseClass(): BramusResponseClass { 27 | return BramusResponseClass::getInstance(); 28 | } 29 | 30 | 31 | 32 | public function registerClosureRoutes(array $tests): void { 33 | foreach ($tests as $test) { 34 | $this->router->get($test->registerPath, $test->closure); 35 | } 36 | } 37 | 38 | 39 | 40 | public function registerClassRoutes(array $tests): void { 41 | foreach ($tests as $test) { 42 | $this->router->get($test->registerPath, BramusClassSimple::class . '@' . $test->method); 43 | } 44 | } 45 | 46 | 47 | 48 | public function dispatch(string $path): string { 49 | $_SERVER['REQUEST_METHOD'] = 'GET'; 50 | $_SERVER['REQUEST_URI'] = $path; 51 | $this->router->run(); 52 | return BramusResponseClass::getInstance()->getContent() 53 | ?: throw new \RuntimeException("No response set for path: $path"); 54 | } 55 | 56 | 57 | 58 | public function wildcardReplacements(string $path): string { 59 | return TestGenerator::replaceWildcards($path, ['(\w+)', '(\w+)', '(\w+)']); 60 | } 61 | 62 | 63 | 64 | } -------------------------------------------------------------------------------- /src/Routers/Symfony/SymfonyRouterAdapter.php: -------------------------------------------------------------------------------- 1 | routes = new RouteCollection(); 22 | $this->context = new RequestContext(); 23 | } 24 | 25 | 26 | 27 | public function registerClosureRoutes(array $tests): void { 28 | foreach ($tests as $test) { 29 | $this->routes->add($test->registerPath, new Route($test->registerPath, ['_controller' => $test->closure])); 30 | } 31 | $this->matcher = new UrlMatcher($this->routes, $this->context); 32 | } 33 | 34 | 35 | 36 | public function registerClassRoutes(array $tests): void { 37 | foreach ($tests as $test) { 38 | $this->routes->add($test->registerPath, new Route($test->registerPath, ['_controller' => [new $test->class, $test->method]])); 39 | } 40 | $this->matcher = new UrlMatcher($this->routes, $this->context); 41 | } 42 | 43 | 44 | 45 | public function dispatch(string $path): string { 46 | $parameters = $this->matcher->match($path); 47 | $callback = $parameters['_controller']; 48 | unset($parameters['_controller'], $parameters['_route']); 49 | return $callback(...array_values($parameters)); 50 | } 51 | 52 | 53 | 54 | public function wildcardReplacements(string $path): string { 55 | return TestGenerator::replaceWildcards($path, ['{one}', '{two}', '{three}']); 56 | } 57 | 58 | 59 | 60 | } -------------------------------------------------------------------------------- /preload.php: -------------------------------------------------------------------------------- 1 | getMessage()); 35 | } 36 | if (!is_file($filePath)) { 37 | throw new \RuntimeException(sprintf('File "%s" was not created', $filePath)); 38 | } 39 | } 40 | 41 | 42 | 43 | public static function load(string $fileName): array { 44 | $filePath = self::path($fileName); 45 | if (!is_file($filePath)) { 46 | throw new \RuntimeException(sprintf('File "%s" does not exist', $filePath)); 47 | } 48 | $data = file_get_contents($filePath); 49 | if ($data === false) { 50 | throw new \RuntimeException(sprintf('Could not read file "%s"', $filePath)); 51 | } 52 | try { 53 | return json_decode($data, true, 512, JSON_THROW_ON_ERROR); 54 | } catch (\JsonException $e) { 55 | throw new \RuntimeException('Could not load file: ' . $e->getMessage()); 56 | } 57 | } 58 | 59 | 60 | } -------------------------------------------------------------------------------- /src/Routers/Nette/NetteRouterAdapter.php: -------------------------------------------------------------------------------- 1 | router = new RouteList(); 22 | $this->request = new RequestFactory()->fromGlobals(); 23 | } 24 | 25 | 26 | 27 | public function registerClosureRoutes(array $tests): void { 28 | foreach ($tests as $test) { 29 | $this->router->addRoute($test->registerPath, [ 30 | 'controller' => $test->closure, 31 | ]); 32 | } 33 | } 34 | 35 | 36 | 37 | public function registerClassRoutes(array $tests): void { 38 | foreach ($tests as $test) { 39 | $this->router->addRoute($test->registerPath, [ 40 | 'controller' => $test->class, 41 | 'action' => $test->method, 42 | ]); 43 | } 44 | } 45 | 46 | 47 | 48 | public function dispatch(string $path): string { 49 | $this->request = $this->request->withUrl(new \Nette\Http\UrlScript($path)); 50 | $params = $this->router->match($this->request); 51 | 52 | $handler = $params['controller']; 53 | unset($params['controller']); 54 | 55 | if (isset($params['action'])) { 56 | $handler = [new $handler, $params['action']]; 57 | unset($params['action']); 58 | } 59 | 60 | return call_user_func_array($handler, $params); 61 | 62 | } 63 | 64 | 65 | 66 | public function wildcardReplacements(string $path): string { 67 | return TestGenerator::replaceWildcards($path, ['', '', '']); 68 | } 69 | 70 | 71 | } -------------------------------------------------------------------------------- /docker/fpm/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | ; Enables the display of error messages (helpful for debugging) 3 | display_errors = 1 4 | ; Enables the display of startup errors (helpful for debugging) 5 | display_startup_errors = 1 6 | ; Reports all errors except deprecated ones (avoids old warnings) 7 | error_reporting = E_ALL & ~E_DEPRECATED 8 | ; Sets a maximum execution time of 10 seconds for scripts (prevents infinite loops) 9 | max_execution_time = 10 10 | 11 | [OPCACHE] 12 | ; Loads the OPcache extension for PHP to improve performance by caching bytecode 13 | zend-extension = opcache.so 14 | ; Disables consistency checks (can improve performance) 15 | opcache.consistency_checks = 0 16 | ; Enables the OPcache extension (ensure this variable is set to a valid value like 1 or '1') 17 | opcache.enable = 1 18 | ; Allows overriding certain file-based configurations for OPcache 19 | opcache.enable_file_override = 1 20 | ; Enables fast shutdown for OPcache, improving the termination of PHP processes 21 | opcache.fast_shutdown = 1 22 | ; Sets the maximum number of files OPcache can cache to 10,000 23 | opcache.max_accelerated_files = 10000 24 | ; Defines the maximum amount of wasted memory allowed before OPcache is reset 25 | opcache.max_wasted_percentage = 10 26 | ; Allocates 256MB of memory for OPcache 27 | opcache.memory_consumption = 256 28 | ; Disables revalidation of file paths (improves performance, but might cause issues if files change frequently) 29 | opcache.revalidate_path = 0 30 | ; Saves comments in cached PHP files (useful for debugging, but can slightly impact performance) 31 | opcache.save_comments = 1 32 | ; Disables the use of the current working directory (can improve OPcache performance) 33 | opcache.use_cwd = 0 34 | ; Disables timestamp validation for cached files (can improve performance) 35 | opcache.validate_timestamps = 0 36 | ; Preloads specific PHP file(s) for faster execution (useful for large applications) 37 | opcache.preload = /code/preload.php 38 | ; Defines the user for preloading OPcache (usually set to the web server user) 39 | opcache.preload_user = www-data -------------------------------------------------------------------------------- /src/TestCode/test_suite_8.php: -------------------------------------------------------------------------------- 1 | $paths */ 24 | $tests = TestGenerator::generatePaths(100, 5, 2); 25 | FileSaver::save($cacheFile, $tests); 26 | } 27 | 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Generate handler 32 | |-------------------------------------------------------------------------- 33 | */ 34 | 35 | $tests = TestGenerator::generateClassTests( 36 | $tests, 37 | 2, 38 | $adapter, 39 | ); 40 | 41 | 42 | /* 43 | |-------------------------------------------------------------------------- 44 | | Run Benchmark 45 | |-------------------------------------------------------------------------- 46 | */ 47 | 48 | 49 | gc_collect_cycles(); 50 | memory_reset_peak_usage(); 51 | $timeStart = hrtime(true); 52 | 53 | 54 | $adapter->initialize(); 55 | $adapter->registerClassRoutes($tests); 56 | 57 | for ($i = 0; $i < 200; $i++) { 58 | foreach ($tests as $test) { 59 | $response = $adapter->dispatch($test->path); 60 | if ($response !== $test->result) { 61 | return Result::unsuccessful("Response mismatch. Expected: $test->result Got: $response"); 62 | } 63 | } 64 | } 65 | 66 | 67 | $timeEnd = hrtime(true); 68 | return Result::fromMeasurement($timeStart, $timeEnd, memory_get_peak_usage()); 69 | 70 | -------------------------------------------------------------------------------- /src/Routers/Laravel/LaravelRouteAdapter.php: -------------------------------------------------------------------------------- 1 | bind(CallableDispatcher::class, \Illuminate\Routing\CallableDispatcher::class); 27 | 28 | // Create a request from server variables, and bind it to the container; optional 29 | $this->request = Request::capture(); 30 | $container->instance(Request::class, $this->request); 31 | 32 | // Using Illuminate/Events/Dispatcher here (not required); any implementation of 33 | // Illuminate/Contracts/Event/Dispatcher is acceptable 34 | $events = new Dispatcher($container); 35 | 36 | // Create the router instance 37 | $this->router = new Router($events, $container); 38 | } 39 | 40 | 41 | 42 | public function registerClosureRoutes(array $tests): void { 43 | // Sort routes: Specific before Generic 44 | foreach ($tests as $test) { 45 | $this->router->get($test->registerPath, $test->closure); 46 | } 47 | } 48 | 49 | 50 | 51 | public function registerClassRoutes(array $tests): void { 52 | foreach ($tests as $test) { 53 | $this->router->get($test->registerPath, [$test->class, $test->method]); 54 | } 55 | } 56 | 57 | 58 | 59 | public function dispatch(string $path): string { 60 | $this->request = $this->request->duplicate(); 61 | $this->request->server->set('REQUEST_URI', $path); 62 | return $this->router->dispatch($this->request)->getContent() ?: throw new RuntimeException("Unable to get string response"); 63 | } 64 | 65 | 66 | 67 | public function wildcardReplacements(string $path): string { 68 | return TestGenerator::replaceWildcards($path, ['{one}', '{two}', '{three}']); 69 | } 70 | 71 | 72 | 73 | } -------------------------------------------------------------------------------- /src/Generators/TestRouteBuilder.php: -------------------------------------------------------------------------------- 1 | $path) { 32 | $result = $replacedPaths[$i] ?? $path; 33 | $routes[$path] = static function (ServerRequestInterface $request) use ($result): ResponseInterface { 34 | return new Response(200, [], $result)->withHeader('X-MiddlewareCount', $request->getAttribute('middlewareCount', 0)); 35 | }; 36 | } 37 | return $routes; 38 | } 39 | 40 | 41 | 42 | private static function buildMiddlewares(int $middlewareCount): array { 43 | $middlewares = []; 44 | for ($i = 0; $i < $middlewareCount; $i++) { 45 | $middlewares[] = new class() implements MiddlewareInterface { 46 | 47 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { 48 | $count = $request->getAttribute('middlewareCount', 0); 49 | return $handler->handle($request->withAttribute('middlewareCount', $count + 1)); 50 | } 51 | 52 | 53 | }; 54 | } 55 | return $middlewares; 56 | } 57 | 58 | 59 | 60 | private static function buildRequestPaths(array $paths): array { 61 | $requestPaths = []; 62 | foreach ($paths as $path) { 63 | $requestPaths[$path] = new ServerRequest('GET', $path); 64 | } 65 | return $requestPaths; 66 | } 67 | 68 | 69 | 70 | } -------------------------------------------------------------------------------- /src/Routers/FastRoute/FastRouteRouteAdapter.php: -------------------------------------------------------------------------------- 1 | routeCollector = new RouteCollector(new Std(), new GroupCountBased()); 25 | $this->dispatcher = new Dispatcher\GroupCountBased($this->routeCollector->getData()); 26 | } 27 | 28 | 29 | 30 | public function registerClosureRoutes(array $tests): void { 31 | foreach ($tests as $test) { 32 | $this->routeCollector->addRoute('GET', $test->registerPath, $test->closure); 33 | } 34 | } 35 | 36 | 37 | 38 | public function registerClassRoutes(array $tests): void { 39 | foreach ($tests as $test) { 40 | $this->routeCollector->addRoute('GET', $test->registerPath, [$test->class, $test->method]); 41 | } 42 | } 43 | 44 | 45 | 46 | public function dispatch(string $path): string { 47 | $this->dispatcher = new Dispatcher\GroupCountBased($this->routeCollector->getData()); 48 | $routeInfo = $this->dispatcher->dispatch('GET', $path); 49 | if (isset($routeInfo[1]) && $routeInfo[1] instanceof Closure) { 50 | $handler = $routeInfo[1]; 51 | } else { 52 | $handler = [new $routeInfo[1][0], $routeInfo[1][1]]; 53 | } 54 | switch ($routeInfo[0]) { 55 | case Dispatcher::NOT_FOUND: 56 | throw new RuntimeException("Not found: $path"); 57 | case Dispatcher::METHOD_NOT_ALLOWED: 58 | throw new RuntimeException('Method not allowed'); 59 | case Dispatcher::FOUND: 60 | return call_user_func_array($handler, $routeInfo[2]); 61 | } 62 | throw new RuntimeException('Unexpected result'); 63 | } 64 | 65 | 66 | 67 | public function wildcardReplacements(string $path): string { 68 | return TestGenerator::replaceWildcards($path, ['{one:\w+}', '{two:\w+}', '{three:\w+}']); 69 | } 70 | 71 | 72 | } -------------------------------------------------------------------------------- /src/TestCode/test_suite_3.php: -------------------------------------------------------------------------------- 1 | $paths */ 28 | $tests = TestGenerator::generatePaths(100, 5, 0); 29 | FileSaver::save($cacheFile, $tests); 30 | } 31 | 32 | /* 33 | |-------------------------------------------------------------------------- 34 | | Generate handler 35 | |-------------------------------------------------------------------------- 36 | */ 37 | 38 | if ($adapter instanceof BramusRouterAdapter) { 39 | $response = $adapter->getResponseClass(); 40 | $tests = array_map(static function (array $test) use ($response) { 41 | $path = $test['path']; 42 | $test['closure'] = static function () use ($response, $path): string { 43 | $response->setContent($path); 44 | return $path; 45 | }; 46 | return $test; 47 | }, $tests); 48 | } 49 | 50 | 51 | $tests = TestGenerator::generateClosureTests( 52 | $tests, 53 | $adapter, 54 | ); 55 | 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Run Benchmark 60 | |-------------------------------------------------------------------------- 61 | */ 62 | 63 | $randomRoute = $tests[4]; 64 | 65 | gc_collect_cycles(); 66 | memory_reset_peak_usage(); 67 | $timeStart = hrtime(true); 68 | 69 | $adapter->initialize(); 70 | $adapter->registerClosureRoutes($tests); 71 | 72 | $response = $adapter->dispatch($randomRoute->path); 73 | if ($response !== $randomRoute->path) { 74 | return Result::unsuccessful("Response mismatch. Expected: $randomRoute->path Got: $response"); 75 | } 76 | 77 | $timeEnd = hrtime(true); 78 | return Result::fromMeasurement($timeStart, $timeEnd, memory_get_peak_usage()); 79 | 80 | -------------------------------------------------------------------------------- /src/TestCode/test_suite_2.php: -------------------------------------------------------------------------------- 1 | $paths */ 27 | $tests = TestGenerator::generatePaths(50, 5, 0); 28 | FileSaver::save($cacheFile, $tests); 29 | } 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Generate handler 34 | |-------------------------------------------------------------------------- 35 | */ 36 | 37 | if ($adapter instanceof BramusRouterAdapter) { 38 | $response = $adapter->getResponseClass(); 39 | $tests = array_map(static function (array $test) use ($response) { 40 | $path = $test['path']; 41 | $test['closure'] = static function () use ($response, $path): string { 42 | $response->setContent($path); 43 | return $path; 44 | }; 45 | return $test; 46 | }, $tests); 47 | } 48 | 49 | 50 | $tests = TestGenerator::generateClosureTests( 51 | $tests, 52 | $adapter, 53 | ); 54 | 55 | 56 | 57 | 58 | /* 59 | |-------------------------------------------------------------------------- 60 | | Run Benchmark 61 | |-------------------------------------------------------------------------- 62 | */ 63 | 64 | $oneTest = $tests[array_rand($tests)]; 65 | 66 | 67 | gc_collect_cycles(); 68 | memory_reset_peak_usage(); 69 | $timeStart = hrtime(true); 70 | 71 | 72 | 73 | for ($i = 0; $i < 50; $i++) { 74 | $adapter->initialize(); 75 | $adapter->registerClosureRoutes($tests); 76 | } 77 | 78 | 79 | $timeEnd = hrtime(true); 80 | 81 | $result = $adapter->dispatch($oneTest->path); 82 | if ($result !== $oneTest->path) { 83 | return Result::unsuccessful("Response mismatch. Expected: $oneTest->path Got: $result"); 84 | } 85 | 86 | return Result::fromMeasurement($timeStart, $timeEnd, memory_get_peak_usage()); 87 | 88 | 89 | -------------------------------------------------------------------------------- /core/Benchmark/Benchmark.php: -------------------------------------------------------------------------------- 1 | runTestSuite($suite, $package, $result); 25 | } 26 | } 27 | 28 | Color::print("Generating results...", [Color::LIGHT_GREEN]); 29 | $outputGenerator->generate($testSuites, $packages, $result); 30 | Color::print("Benchmark finished successfully", [Color::LIGHT_GREEN]); 31 | } 32 | 33 | 34 | 35 | private function runTestSuite( 36 | TestSuiteInterface $testSuite, 37 | PackageDefinitionInterface $package, 38 | BenchmarkResult $benchmarkResult, 39 | ): void { 40 | $testSuiteNumber = $testSuite->getNumber(); 41 | $packageName = $package->getDisplayName(); 42 | 43 | Color::print("Running test $testSuiteNumber: $packageName", [ 44 | Color::BLUE, 45 | ]); 46 | 47 | $this->clearOpcache(); 48 | 49 | echo Color::DIM, Color::LIGHT_BLUE_ALT; 50 | 51 | for ($run = 0; $run < 20; $run++) { 52 | $container = $package->getName(); 53 | $result = $this->getResult($testSuiteNumber, $container); 54 | 55 | $benchmarkResult->addTestResult($testSuite, $package, $result); 56 | 57 | if ($result->isSuccessful() === false) { 58 | echo Color::RESET; 59 | Color::print("Test failed: " . $result->message(), [Color::RED]); 60 | return; 61 | } 62 | } 63 | echo Color::RESET; 64 | 65 | echo " (" . $benchmarkResult->getResult($testSuite, $packageName)->timeConsumptionInMilliSeconds() . " ms)\n"; 66 | } 67 | 68 | 69 | 70 | private function clearOpcache(): void { 71 | shell_exec("curl -s \"http://caddy/public/index.php?clear=1\""); 72 | } 73 | 74 | 75 | 76 | private function getResult(int $suite, string $package_name): Result { 77 | $pn = urlencode($package_name); 78 | echo '-'; 79 | $url = "http://caddy/public/index.php?package=$pn&suite=$suite"; 80 | $response = shell_exec("curl -s \"$url\""); 81 | return empty($response) 82 | ? Result::unsuccessful("Empty response from server running test $package_name on test suite $suite") 83 | : Result::fromJSON($response); 84 | } 85 | 86 | 87 | 88 | } -------------------------------------------------------------------------------- /src/TestCode/test_suite_7.php: -------------------------------------------------------------------------------- 1 | $paths */ 24 | $tests = TestGenerator::generatePaths(1500, 6, 2); 25 | FileSaver::save($cacheFile, $tests); 26 | } 27 | 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Generate handler 32 | |-------------------------------------------------------------------------- 33 | */ 34 | 35 | if ($adapter instanceof \Benchmark\Routers\Bramus\BramusRouterAdapter) { 36 | $response = $adapter->getResponseClass(); 37 | $tests = array_map(static function (array $test) use ($response) { 38 | $test['closure'] = static function (string $one, string $two) use ($response): string { 39 | $response->setContent($one . $two); 40 | return $one . $two; 41 | }; 42 | return $test; 43 | }, $tests); 44 | } 45 | 46 | if ($adapter instanceof \Benchmark\Routers\Klein\KleinRouterAdapter) { 47 | $tests = array_map(static function (array $test) { 48 | $test['closure'] = static function (\Klein\Request $request): string { 49 | /** @noinspection PhpUndefinedFieldInspection */ 50 | return $request->one . $request->two; 51 | }; 52 | return $test; 53 | }, $tests); 54 | } 55 | 56 | $tests = TestGenerator::generateClosureTests( 57 | $tests, 58 | $adapter, 59 | static fn(string $one, string $two): string => $one . $two, 60 | ); 61 | 62 | 63 | 64 | 65 | /* 66 | |-------------------------------------------------------------------------- 67 | | Run Benchmark 68 | |-------------------------------------------------------------------------- 69 | */ 70 | 71 | gc_collect_cycles(); 72 | memory_reset_peak_usage(); 73 | $timeStart = hrtime(true); 74 | 75 | $adapter->initialize(); 76 | $adapter->registerClosureRoutes($tests); 77 | 78 | foreach ($tests as $test) { 79 | $response = $adapter->dispatch($test->path); 80 | if (!$response || $response !== $test->result) { 81 | return Result::unsuccessful("Response mismatch. Expected: $test->result Got: $response"); 82 | } 83 | } 84 | 85 | $timeEnd = hrtime(true); 86 | return Result::fromMeasurement($timeStart, $timeEnd, memory_get_peak_usage()); 87 | 88 | -------------------------------------------------------------------------------- /src/TestCode/test_suite_4.php: -------------------------------------------------------------------------------- 1 | $paths */ 28 | $tests = TestGenerator::generatePaths(50, 4, 1); 29 | FileSaver::save($cacheFile, $tests); 30 | } 31 | 32 | /* 33 | |-------------------------------------------------------------------------- 34 | | Generate handler 35 | |-------------------------------------------------------------------------- 36 | */ 37 | 38 | if ($adapter instanceof BramusRouterAdapter) { 39 | $response = $adapter->getResponseClass(); 40 | $tests = array_map(static function (array $test) use ($response) { 41 | $test['closure'] = static function (string $one) use ($response): string { 42 | $response->setContent($one); 43 | return $one; 44 | }; 45 | return $test; 46 | }, $tests); 47 | } 48 | 49 | if ($adapter instanceof \Benchmark\Routers\Klein\KleinRouterAdapter) { 50 | $tests = array_map(static function (array $test) { 51 | $test['closure'] = static function (\Klein\Request $request): string { 52 | /** @noinspection PhpUndefinedFieldInspection */ 53 | return $request->one; 54 | }; 55 | return $test; 56 | }, $tests); 57 | } 58 | 59 | $tests = TestGenerator::generateClosureTests( 60 | $tests, 61 | $adapter, 62 | static fn(string $one): string => $one, 63 | ); 64 | 65 | 66 | 67 | /* 68 | |-------------------------------------------------------------------------- 69 | | Run Benchmark 70 | |-------------------------------------------------------------------------- 71 | */ 72 | 73 | gc_collect_cycles(); 74 | memory_reset_peak_usage(); 75 | $timeStart = hrtime(true); 76 | 77 | 78 | $adapter->initialize(); 79 | 80 | $adapter->registerClosureRoutes($tests); 81 | 82 | foreach ($tests as $test) { 83 | $response = $adapter->dispatch($test->path); 84 | if ($response !== $test->result) { 85 | return Result::unsuccessful("Response mismatch. Expected: $test->result Got: $response"); 86 | } 87 | } 88 | 89 | $timeEnd = hrtime(true); 90 | return Result::fromMeasurement($timeStart, $timeEnd, memory_get_peak_usage()); 91 | 92 | -------------------------------------------------------------------------------- /src/TestCode/test_suite_9.php: -------------------------------------------------------------------------------- 1 | $paths */ 28 | $tests = TestGenerator::generatePaths(100, 5, 1); 29 | FileSaver::save($cacheFile, $tests); 30 | } 31 | 32 | /* 33 | |-------------------------------------------------------------------------- 34 | | Generate handler 35 | |-------------------------------------------------------------------------- 36 | */ 37 | 38 | if ($adapter instanceof BramusRouterAdapter) { 39 | $response = $adapter->getResponseClass(); 40 | $tests = array_map(static function (array $test) use ($response) { 41 | $test['closure'] = static function (string $one) use ($response): string { 42 | $response->setContent($one); 43 | return $one; 44 | }; 45 | return $test; 46 | }, $tests); 47 | } 48 | 49 | if ($adapter instanceof \Benchmark\Routers\Klein\KleinRouterAdapter) { 50 | $tests = array_map(static function (array $test) { 51 | $test['closure'] = static function (\Klein\Request $request): string { 52 | /** @noinspection PhpUndefinedFieldInspection */ 53 | return $request->one; 54 | }; 55 | return $test; 56 | }, $tests); 57 | } 58 | 59 | $tests = TestGenerator::generateClosureTests( 60 | $tests, 61 | $adapter, 62 | static fn(string $one): string => $one, 63 | ); 64 | 65 | 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | Run Benchmark 69 | |-------------------------------------------------------------------------- 70 | */ 71 | 72 | # Get the last array element 73 | $randomRoute = $tests[50]; 74 | 75 | gc_collect_cycles(); 76 | memory_reset_peak_usage(); 77 | $timeStart = hrtime(true); 78 | 79 | $adapter->initialize(); 80 | $adapter->registerClosureRoutes($tests); 81 | 82 | $response = $adapter->dispatch($randomRoute->path); 83 | if ($response !== $randomRoute->result) { 84 | return Result::unsuccessful("Response mismatch. Expected: $randomRoute->result Got: $response"); 85 | } 86 | 87 | $timeEnd = hrtime(true); 88 | return Result::fromMeasurement($timeStart, $timeEnd, memory_get_peak_usage()); 89 | 90 | -------------------------------------------------------------------------------- /src/TestCode/test_suite_5.php: -------------------------------------------------------------------------------- 1 | $paths */ 25 | $tests = TestGenerator::generatePaths(100, 6, 2); 26 | FileSaver::save($cacheFile, $tests); 27 | } 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Generate handler 32 | |-------------------------------------------------------------------------- 33 | */ 34 | 35 | if ($adapter instanceof \Benchmark\Routers\Bramus\BramusRouterAdapter) { 36 | $response = $adapter->getResponseClass(); 37 | $tests = array_map(static function (array $test) use ($response) { 38 | $test['closure'] = static function (string $one, string $two) use ($response): string { 39 | $response->setContent($one . $two); 40 | return $one . $two; 41 | }; 42 | return $test; 43 | }, $tests); 44 | } 45 | 46 | if ($adapter instanceof \Benchmark\Routers\Klein\KleinRouterAdapter) { 47 | $tests = array_map(static function (array $test) { 48 | $test['closure'] = static function (\Klein\Request $request): string { 49 | /** @noinspection PhpUndefinedFieldInspection */ 50 | return $request->one . $request->two; 51 | }; 52 | return $test; 53 | }, $tests); 54 | } 55 | 56 | $tests = TestGenerator::generateClosureTests( 57 | $tests, 58 | $adapter, 59 | static fn(string $one, string $two): string => $one . $two, 60 | ); 61 | 62 | 63 | /* 64 | |-------------------------------------------------------------------------- 65 | | Run Benchmark 66 | |-------------------------------------------------------------------------- 67 | */ 68 | 69 | gc_collect_cycles(); 70 | memory_reset_peak_usage(); 71 | $timeStart = hrtime(true); 72 | 73 | $adapter->initialize(); 74 | 75 | $adapter->registerClosureRoutes($tests); 76 | 77 | for ($i = 0; $i < 50; $i++) { 78 | foreach ($tests as $test) { 79 | $response = $adapter->dispatch($test->path); 80 | if ($response !== $test->result) { 81 | return Result::unsuccessful("Response mismatch. Expected: $test->result Got: $response"); 82 | } 83 | } 84 | } 85 | 86 | $timeEnd = hrtime(true); 87 | return Result::fromMeasurement($timeStart, $timeEnd, memory_get_peak_usage()); 88 | 89 | -------------------------------------------------------------------------------- /core/Utilities/Color.php: -------------------------------------------------------------------------------- 1 | timeConsumption; 64 | } 65 | 66 | 67 | 68 | public function peakMemoryUsageInMegaBytes(): ?float { 69 | return $this->peakMemoryUsage; 70 | } 71 | 72 | 73 | 74 | public function message(): string { 75 | return $this->message; 76 | } 77 | 78 | 79 | 80 | public function isSuccessful(): bool { 81 | return $this->timeConsumption !== null && $this->peakMemoryUsage !== null; 82 | } 83 | 84 | 85 | 86 | public function toJson(): string { 87 | try { 88 | return json_encode( 89 | [ 90 | "time" => $this->timeConsumption, 91 | "memory" => $this->peakMemoryUsage, 92 | "message" => $this->message, 93 | ], 94 | JSON_THROW_ON_ERROR, 95 | ); 96 | } catch (JsonException) { 97 | 98 | } 99 | } 100 | 101 | 102 | 103 | public function printJsonResult(): never { 104 | http_response_code($this->isSuccessful() ? 200 : 500); 105 | header('Content-Type: application/json'); 106 | echo $this->toJson(); 107 | exit; 108 | } 109 | 110 | 111 | } -------------------------------------------------------------------------------- /src/TestCode/test_suite_6.php: -------------------------------------------------------------------------------- 1 | $paths */ 24 | $tests = TestGenerator::generatePaths(100, 6, 2); 25 | FileSaver::save($cacheFile, $tests); 26 | } 27 | 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Generate handler 32 | |-------------------------------------------------------------------------- 33 | */ 34 | 35 | if ($adapter instanceof \Benchmark\Routers\Bramus\BramusRouterAdapter) { 36 | $response = $adapter->getResponseClass(); 37 | $tests = array_map(static function (array $test) use ($response) { 38 | $test['closure'] = static function (string $one, string $two) use ($response): string { 39 | $response->setContent($one . $two); 40 | return $one . $two; 41 | }; 42 | return $test; 43 | }, $tests); 44 | } 45 | 46 | if ($adapter instanceof \Benchmark\Routers\Klein\KleinRouterAdapter) { 47 | $tests = array_map(static function (array $test) { 48 | $test['closure'] = static function (\Klein\Request $request): string { 49 | /** @noinspection PhpUndefinedFieldInspection */ 50 | return $request->one . $request->two; 51 | }; 52 | return $test; 53 | }, $tests); 54 | } 55 | 56 | 57 | $tests = TestGenerator::generateClosureTests( 58 | $tests, 59 | $adapter, 60 | static fn(string $one, string $two): string => $one . $two, 61 | ); 62 | 63 | 64 | /* 65 | |-------------------------------------------------------------------------- 66 | | Pre Benchmark 67 | |-------------------------------------------------------------------------- 68 | */ 69 | 70 | gc_collect_cycles(); 71 | memory_reset_peak_usage(); 72 | 73 | $adapter->initialize(); 74 | $adapter->registerClosureRoutes($tests); 75 | 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Run Benchmark 80 | |-------------------------------------------------------------------------- 81 | */ 82 | 83 | $timeStart = hrtime(true); 84 | 85 | 86 | for ($i = 0; $i < 200; $i++) { 87 | foreach ($tests as $test) { 88 | $response = $adapter->dispatch($test->path); 89 | if ($response !== $test->result) { 90 | return Result::unsuccessful("Response mismatch. Expected: $test->result Got: $response"); 91 | } 92 | } 93 | } 94 | 95 | 96 | $timeEnd = hrtime(true); 97 | return Result::fromMeasurement($timeStart, $timeEnd, memory_get_peak_usage()); 98 | 99 | -------------------------------------------------------------------------------- /core/Benchmark/BenchmarkResult.php: -------------------------------------------------------------------------------- 1 | getDisplayName(); 24 | $testSuiteNumber = $testSuite->getNumber(); 25 | 26 | $this->testResults[$testSuiteNumber][$containerName][] = $result; 27 | } 28 | 29 | 30 | 31 | public function getResult(TestSuiteInterface $testSuite, string $containerName): Result { 32 | $testSuiteNumber = $testSuite->getNumber(); 33 | 34 | if (isset($this->testResults[$testSuiteNumber][$containerName]) === false) { 35 | throw new InvalidArgumentException("No result with the given parameters exists"); 36 | } 37 | 38 | return $this->calculateResults($this->testResults[$testSuiteNumber][$containerName]); 39 | } 40 | 41 | 42 | 43 | /** 44 | * @return Result[] 45 | */ 46 | public function getResults(TestSuiteInterface $testSuite): array { 47 | $testSuiteNumber = $testSuite->getNumber(); 48 | 49 | if (isset($this->testResults[$testSuiteNumber]) === false) { 50 | return []; 51 | } 52 | 53 | $results = array_map(function ($containerResults) { 54 | return $this->calculateResults($containerResults); 55 | }, $this->testResults[$testSuiteNumber]); 56 | 57 | uasort($results, static function (Result $a, Result $b): int { 58 | if ($a->timeConsumptionInMilliSeconds() === null && $b->timeConsumptionInMilliSeconds() !== null) { 59 | return 1; 60 | } 61 | 62 | if ($a->timeConsumptionInMilliSeconds() !== null && $b->timeConsumptionInMilliSeconds() === null) { 63 | return -1; 64 | } 65 | 66 | return $a->timeConsumptionInMilliSeconds() <=> $b->timeConsumptionInMilliSeconds(); 67 | }); 68 | 69 | return $results; 70 | } 71 | 72 | 73 | 74 | /** 75 | * @param Result[] $containerResults 76 | */ 77 | private function calculateResults(array $containerResults): Result { 78 | $timeResults = []; 79 | $memoryResults = []; 80 | foreach ($containerResults as $item) { 81 | $timeResults[] = $item->timeConsumptionInMilliSeconds(); 82 | $memoryResults[] = $item->peakMemoryUsageInMegaBytes(); 83 | } 84 | 85 | return Result::fromValues( 86 | $this->getMedian($timeResults), 87 | $this->getMedian($memoryResults), 88 | ); 89 | } 90 | 91 | 92 | 93 | /** 94 | * @param array $results 95 | */ 96 | private function getMedian(array $results): ?float { 97 | if ($results === [] || $results[0] === null) { 98 | return null; 99 | } 100 | 101 | sort($results, SORT_NUMERIC); 102 | 103 | $count = count($results); 104 | $middleIndex = $count / 2; 105 | 106 | assert($results[$middleIndex] !== null); 107 | assert($results[$middleIndex + 1] !== null); 108 | 109 | if ($count % 2 === 0) { 110 | $median = ($results[$middleIndex] + $results[$middleIndex + 1]) / 2; 111 | } else { 112 | $median = $results[$middleIndex]; 113 | } 114 | 115 | return round($median, 5); 116 | } 117 | 118 | 119 | } -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 74 | -------------------------------------------------------------------------------- /src/Generators/TestGenerator.php: -------------------------------------------------------------------------------- 1 | substr(bin2hex(random_bytes(2)), 0, 4), range(1, $wildcardCount)); 56 | $newPath = self::replaceWildcards($wildcardPath, $replacements); 57 | 58 | // Ensure uniqueness 59 | if (!isset($unique[$wildcardPath])) { 60 | $results[] = [ 61 | 'path' => $newPath, 62 | 'wildcard_path' => $wildcardPath, 63 | 'result' => implode('', $replacements), 64 | ]; 65 | $unique[$wildcardPath] = true; 66 | } 67 | } 68 | 69 | return $results; 70 | } catch (RandomException $e) { 71 | throw new \RuntimeException($e->getMessage(), 0, $e); 72 | } 73 | } 74 | 75 | 76 | 77 | public static function replaceWildcards(string $wildcardPath, array $replacements): string { 78 | $segments = explode('/', trim($wildcardPath, '/')); 79 | $replacementIndex = 0; 80 | 81 | foreach ($segments as &$segment) { 82 | if ($segment === '**' && isset($replacements[$replacementIndex])) { 83 | $segment = $replacements[$replacementIndex]; 84 | $replacementIndex++; 85 | } 86 | } 87 | 88 | return '/' . implode('/', $segments); 89 | } 90 | 91 | 92 | 93 | private static function randomSegment(): string { 94 | /** @noinspection SpellCheckingInspection */ 95 | return substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'), 0, random_int(1, 12)); 96 | } 97 | 98 | 99 | 100 | public static function generateClosureTests(array $tests, AdapterInterface $adapter, ?\Closure $closure = null): array { 101 | return array_map(static function ($data) use ($adapter, $closure) { 102 | $path = $data['path']; 103 | $closure = $data['closure'] ?? $closure ?? static function () use ($path): string { 104 | return $path; 105 | }; 106 | return new ClosureTest( 107 | $data['path'], $data['result'], $adapter->wildcardReplacements($data['wildcard_path']), $closure, 108 | ); 109 | }, $tests); 110 | } 111 | 112 | 113 | 114 | public static function generateClassTests(array $tests, int $wildcardCount, AdapterInterface $adapter) { 115 | if ($wildcardCount === 2) { 116 | $methods = ['twoHome', 'twoAbout', 'twoContact']; 117 | } else { 118 | throw new \RuntimeException('Not implemented'); 119 | } 120 | return array_map(static function ($data) use ($adapter, $methods) { 121 | return new ClassTest( 122 | $data['path'], 123 | $data['result'], 124 | $adapter->wildcardReplacements($data['wildcard_path']), 125 | \Benchmark\Generators\TestClassSimple::class, 126 | $methods[array_rand($methods)], 127 | ); 128 | }, $tests); 129 | 130 | } 131 | 132 | 133 | 134 | } 135 | -------------------------------------------------------------------------------- /.idea/php.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 110 | -------------------------------------------------------------------------------- /.idea/php-router-benchmark.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/TestData/test_2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "\/czslr", 4 | "wildcard_path": "\/czslr", 5 | "result": "a3330829" 6 | }, 7 | { 8 | "path": "\/ptxka\/ne", 9 | "wildcard_path": "\/ptxka\/ne", 10 | "result": "8b569f7e" 11 | }, 12 | { 13 | "path": "\/czslr\/gzn", 14 | "wildcard_path": "\/czslr\/gzn", 15 | "result": "65508eb5" 16 | }, 17 | { 18 | "path": "\/vcudptoxjle\/s\/mpcrnvyl\/chzpsngmb", 19 | "wildcard_path": "\/vcudptoxjle\/s\/mpcrnvyl\/chzpsngmb", 20 | "result": "6800f983" 21 | }, 22 | { 23 | "path": "\/megwndl\/dcivksuwrhjl\/iatdw\/v", 24 | "wildcard_path": "\/megwndl\/dcivksuwrhjl\/iatdw\/v", 25 | "result": "ee448e42" 26 | }, 27 | { 28 | "path": "\/wjfus\/l\/hnx\/nqgpyr", 29 | "wildcard_path": "\/wjfus\/l\/hnx\/nqgpyr", 30 | "result": "ab66d63d" 31 | }, 32 | { 33 | "path": "\/czslr\/jcigdynoqrhu\/cufbpxrw", 34 | "wildcard_path": "\/czslr\/jcigdynoqrhu\/cufbpxrw", 35 | "result": "52d6363a" 36 | }, 37 | { 38 | "path": "\/ofdwqgi\/tv\/kisco\/tbaquwdmjscp", 39 | "wildcard_path": "\/ofdwqgi\/tv\/kisco\/tbaquwdmjscp", 40 | "result": "14a92f68" 41 | }, 42 | { 43 | "path": "\/w\/ad\/gdnysclh\/ahn", 44 | "wildcard_path": "\/w\/ad\/gdnysclh\/ahn", 45 | "result": "8047728d" 46 | }, 47 | { 48 | "path": "\/czslr\/nihpmq\/skvywo\/xejonsqirapm\/sqeaxlb", 49 | "wildcard_path": "\/czslr\/nihpmq\/skvywo\/xejonsqirapm\/sqeaxlb", 50 | "result": "bbc60627" 51 | }, 52 | { 53 | "path": "\/w\/zngcvtl\/emqyfrx\/pcyevk", 54 | "wildcard_path": "\/w\/zngcvtl\/emqyfrx\/pcyevk", 55 | "result": "24d3c6ab" 56 | }, 57 | { 58 | "path": "\/wly\/dmoykvczirt", 59 | "wildcard_path": "\/wly\/dmoykvczirt", 60 | "result": "f2a06c42" 61 | }, 62 | { 63 | "path": "\/ofdwqgi\/tv\/kisco\/tbaquwdmjscp\/wbpcxfkyiom", 64 | "wildcard_path": "\/ofdwqgi\/tv\/kisco\/tbaquwdmjscp\/wbpcxfkyiom", 65 | "result": "edabd2f3" 66 | }, 67 | { 68 | "path": "\/megwndl\/dcivksuwrhjl\/iatdw\/jq", 69 | "wildcard_path": "\/megwndl\/dcivksuwrhjl\/iatdw\/jq", 70 | "result": "2e6de666" 71 | }, 72 | { 73 | "path": "\/csabzfdoinvq\/yuk", 74 | "wildcard_path": "\/csabzfdoinvq\/yuk", 75 | "result": "82647a2c" 76 | }, 77 | { 78 | "path": "\/mujyezkxdvhg\/rhmnbldyti\/ovhsquwf\/repxvambodkj\/tbzuvhqxjces", 79 | "wildcard_path": "\/mujyezkxdvhg\/rhmnbldyti\/ovhsquwf\/repxvambodkj\/tbzuvhqxjces", 80 | "result": "ddef5b13" 81 | }, 82 | { 83 | "path": "\/druemcxal\/edfpav", 84 | "wildcard_path": "\/druemcxal\/edfpav", 85 | "result": "4e2549ba" 86 | }, 87 | { 88 | "path": "\/csabzfdoinvq", 89 | "wildcard_path": "\/csabzfdoinvq", 90 | "result": "09094378" 91 | }, 92 | { 93 | "path": "\/wnmzg\/qjmafz", 94 | "wildcard_path": "\/wnmzg\/qjmafz", 95 | "result": "df017a25" 96 | }, 97 | { 98 | "path": "\/dpuw\/kx\/mjthqczrk\/zjsgru\/xznlubtg", 99 | "wildcard_path": "\/dpuw\/kx\/mjthqczrk\/zjsgru\/xznlubtg", 100 | "result": "5335399e" 101 | }, 102 | { 103 | "path": "\/csabzfdoinvq\/ebkdtvialnms", 104 | "wildcard_path": "\/csabzfdoinvq\/ebkdtvialnms", 105 | "result": "4f140fae" 106 | }, 107 | { 108 | "path": "\/wjfus\/l\/hnx\/nqgpyr\/ecy", 109 | "wildcard_path": "\/wjfus\/l\/hnx\/nqgpyr\/ecy", 110 | "result": "b135bdf5" 111 | }, 112 | { 113 | "path": "\/ltpga\/ygjzho\/bel\/b", 114 | "wildcard_path": "\/ltpga\/ygjzho\/bel\/b", 115 | "result": "f6a61f1f" 116 | }, 117 | { 118 | "path": "\/agjrhvweoq\/lzh", 119 | "wildcard_path": "\/agjrhvweoq\/lzh", 120 | "result": "593d5dfb" 121 | }, 122 | { 123 | "path": "\/svj\/ndpzbugqsita\/vnyf", 124 | "wildcard_path": "\/svj\/ndpzbugqsita\/vnyf", 125 | "result": "5957e7bf" 126 | }, 127 | { 128 | "path": "\/wjfus\/l\/hnx\/nqgpyr\/m", 129 | "wildcard_path": "\/wjfus\/l\/hnx\/nqgpyr\/m", 130 | "result": "0d5adbed" 131 | }, 132 | { 133 | "path": "\/vcudptoxjle\/s\/mpcrnvyl\/chzpsngmb\/yarsgldxnzq", 134 | "wildcard_path": "\/vcudptoxjle\/s\/mpcrnvyl\/chzpsngmb\/yarsgldxnzq", 135 | "result": "b789f7a1" 136 | }, 137 | { 138 | "path": "\/iozgubxcp\/kyajup\/sdoghl", 139 | "wildcard_path": "\/iozgubxcp\/kyajup\/sdoghl", 140 | "result": "b4808786" 141 | }, 142 | { 143 | "path": "\/ltpga\/ygjzho\/bel\/b\/mbofcp", 144 | "wildcard_path": "\/ltpga\/ygjzho\/bel\/b\/mbofcp", 145 | "result": "bc288595" 146 | }, 147 | { 148 | "path": "\/agjrhvweoq", 149 | "wildcard_path": "\/agjrhvweoq", 150 | "result": "65972514" 151 | }, 152 | { 153 | "path": "\/evcdtyaumqn\/ktnhzpewl\/bant\/qivgjxswuly\/qhjiuk", 154 | "wildcard_path": "\/evcdtyaumqn\/ktnhzpewl\/bant\/qivgjxswuly\/qhjiuk", 155 | "result": "e4add3ba" 156 | }, 157 | { 158 | "path": "\/wnmzg\/qjmafz\/rubpdew\/iuoemp", 159 | "wildcard_path": "\/wnmzg\/qjmafz\/rubpdew\/iuoemp", 160 | "result": "12216787" 161 | }, 162 | { 163 | "path": "\/svj\/ndpzbugqsita", 164 | "wildcard_path": "\/svj\/ndpzbugqsita", 165 | "result": "2474c9b2" 166 | }, 167 | { 168 | "path": "\/n\/ux\/zuiblvcqsdxh\/qkxojltbhnsw\/wfzg", 169 | "wildcard_path": "\/n\/ux\/zuiblvcqsdxh\/qkxojltbhnsw\/wfzg", 170 | "result": "e1508d8e" 171 | }, 172 | { 173 | "path": "\/wly\/qeldwurzajb", 174 | "wildcard_path": "\/wly\/qeldwurzajb", 175 | "result": "ae6f6b86" 176 | }, 177 | { 178 | "path": "\/dpuw\/kx\/mjthqczrk\/zjsgru\/v", 179 | "wildcard_path": "\/dpuw\/kx\/mjthqczrk\/zjsgru\/v", 180 | "result": "be27d97a" 181 | }, 182 | { 183 | "path": "\/avdybfotmrzn\/j\/gzy\/qaf\/t", 184 | "wildcard_path": "\/avdybfotmrzn\/j\/gzy\/qaf\/t", 185 | "result": "ae5859e7" 186 | }, 187 | { 188 | "path": "\/avdybfotmrzn\/j\/gzy\/qaf\/y", 189 | "wildcard_path": "\/avdybfotmrzn\/j\/gzy\/qaf\/y", 190 | "result": "e1558294" 191 | }, 192 | { 193 | "path": "\/megwndl\/dcivksuwrhjl\/iatdw\/mvgltszwj\/wzp", 194 | "wildcard_path": "\/megwndl\/dcivksuwrhjl\/iatdw\/mvgltszwj\/wzp", 195 | "result": "5dbfbeec" 196 | }, 197 | { 198 | "path": "\/vtejfrbynu\/qivo\/wedolx\/rxm", 199 | "wildcard_path": "\/vtejfrbynu\/qivo\/wedolx\/rxm", 200 | "result": "caa409a2" 201 | }, 202 | { 203 | "path": "\/iozgubxcp\/kyajup\/sdoghl\/izq\/voicxn", 204 | "wildcard_path": "\/iozgubxcp\/kyajup\/sdoghl\/izq\/voicxn", 205 | "result": "d0ae1e92" 206 | }, 207 | { 208 | "path": "\/svj\/ndpzbugqsita\/bnivtpkarcf\/bklrch", 209 | "wildcard_path": "\/svj\/ndpzbugqsita\/bnivtpkarcf\/bklrch", 210 | "result": "1ed97aad" 211 | }, 212 | { 213 | "path": "\/avdybfotmrzn\/j\/gzy\/qaf\/moebwhsyczkd", 214 | "wildcard_path": "\/avdybfotmrzn\/j\/gzy\/qaf\/moebwhsyczkd", 215 | "result": "20d70b5b" 216 | }, 217 | { 218 | "path": "\/hun\/espnjogkvfi\/kagzblfpw", 219 | "wildcard_path": "\/hun\/espnjogkvfi\/kagzblfpw", 220 | "result": "855a1bce" 221 | }, 222 | { 223 | "path": "\/ofdwqgi\/tv\/kisco\/tbaquwdmjscp\/efh", 224 | "wildcard_path": "\/ofdwqgi\/tv\/kisco\/tbaquwdmjscp\/efh", 225 | "result": "b511e202" 226 | }, 227 | { 228 | "path": "\/mujyezkxdvhg\/rhmnbldyti\/ovhsquwf", 229 | "wildcard_path": "\/mujyezkxdvhg\/rhmnbldyti\/ovhsquwf", 230 | "result": "8e70ed87" 231 | }, 232 | { 233 | "path": "\/n\/ux\/zuiblvcqsdxh\/qkxojltbhnsw", 234 | "wildcard_path": "\/n\/ux\/zuiblvcqsdxh\/qkxojltbhnsw", 235 | "result": "3625dd80" 236 | }, 237 | { 238 | "path": "\/megwndl\/dcivksuwrhjl\/iatdw", 239 | "wildcard_path": "\/megwndl\/dcivksuwrhjl\/iatdw", 240 | "result": "cff591e1" 241 | }, 242 | { 243 | "path": "\/svj\/ndpzbugqsita\/mzoptuvq\/tdwjflh\/wgsbxv", 244 | "wildcard_path": "\/svj\/ndpzbugqsita\/mzoptuvq\/tdwjflh\/wgsbxv", 245 | "result": "966d8505" 246 | }, 247 | { 248 | "path": "\/wly\/sfuoeb", 249 | "wildcard_path": "\/wly\/sfuoeb", 250 | "result": "1f9e891e" 251 | } 252 | ] -------------------------------------------------------------------------------- /src/TestData/test_4.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "\/ogtczmjsnrd\/vrhst\/apfr\/41c4", 4 | "wildcard_path": "\/ogtczmjsnrd\/vrhst\/apfr\/**", 5 | "result": "41c4" 6 | }, 7 | { 8 | "path": "\/ymv\/zjbxhpsfd\/isuwztke\/rfkilgaeu\/8992", 9 | "wildcard_path": "\/ymv\/zjbxhpsfd\/isuwztke\/rfkilgaeu\/**", 10 | "result": "8992" 11 | }, 12 | { 13 | "path": "\/fyoptkniesdh\/uhbxiof\/fszwtie\/ece7", 14 | "wildcard_path": "\/fyoptkniesdh\/uhbxiof\/fszwtie\/**", 15 | "result": "ece7" 16 | }, 17 | { 18 | "path": "\/hkueawt\/msolngeybi\/551e", 19 | "wildcard_path": "\/hkueawt\/msolngeybi\/**", 20 | "result": "551e" 21 | }, 22 | { 23 | "path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/4307", 24 | "wildcard_path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/**", 25 | "result": "4307" 26 | }, 27 | { 28 | "path": "\/seq\/megpfzx\/d8bb\/sfgkraxel", 29 | "wildcard_path": "\/seq\/megpfzx\/**\/sfgkraxel", 30 | "result": "d8bb" 31 | }, 32 | { 33 | "path": "\/fvmjhsrnzdt\/f985", 34 | "wildcard_path": "\/fvmjhsrnzdt\/**", 35 | "result": "f985" 36 | }, 37 | { 38 | "path": "\/gzpimys\/cfbgxnjk\/jqxa\/iq\/f469", 39 | "wildcard_path": "\/gzpimys\/cfbgxnjk\/jqxa\/iq\/**", 40 | "result": "f469" 41 | }, 42 | { 43 | "path": "\/nbzxuvjpciy\/5e35\/jedn\/ospmbqzjr\/fdqaroctyng", 44 | "wildcard_path": "\/nbzxuvjpciy\/**\/jedn\/ospmbqzjr\/fdqaroctyng", 45 | "result": "5e35" 46 | }, 47 | { 48 | "path": "\/gzpimys\/cfbgxnjk\/jqxa\/e764", 49 | "wildcard_path": "\/gzpimys\/cfbgxnjk\/jqxa\/**", 50 | "result": "e764" 51 | }, 52 | { 53 | "path": "\/fvmjhsrnzdt\/o\/okaydtmzqsx\/kajuxfgslwh\/27b1", 54 | "wildcard_path": "\/fvmjhsrnzdt\/o\/okaydtmzqsx\/kajuxfgslwh\/**", 55 | "result": "27b1" 56 | }, 57 | { 58 | "path": "\/ymv\/ygkmrbxnla\/gyhdxrotuw\/aebf", 59 | "wildcard_path": "\/ymv\/ygkmrbxnla\/gyhdxrotuw\/**", 60 | "result": "aebf" 61 | }, 62 | { 63 | "path": "\/nbzxuvjpciy\/irltoq\/xrnkegc\/e29b", 64 | "wildcard_path": "\/nbzxuvjpciy\/irltoq\/xrnkegc\/**", 65 | "result": "e29b" 66 | }, 67 | { 68 | "path": "\/yeohd\/202b\/jpz", 69 | "wildcard_path": "\/yeohd\/**\/jpz", 70 | "result": "202b" 71 | }, 72 | { 73 | "path": "\/dkophsiyl\/uiaeyxshnc\/nstj\/uwgnrdbc\/dccf", 74 | "wildcard_path": "\/dkophsiyl\/uiaeyxshnc\/nstj\/uwgnrdbc\/**", 75 | "result": "dccf" 76 | }, 77 | { 78 | "path": "\/nbzxuvjpciy\/dxctgzuhoels\/a4bb", 79 | "wildcard_path": "\/nbzxuvjpciy\/dxctgzuhoels\/**", 80 | "result": "a4bb" 81 | }, 82 | { 83 | "path": "\/qxsyahvti\/zcifgpysq\/0d4b", 84 | "wildcard_path": "\/qxsyahvti\/zcifgpysq\/**", 85 | "result": "0d4b" 86 | }, 87 | { 88 | "path": "\/seq\/wi\/92fd\/somfzdpte\/bkpht", 89 | "wildcard_path": "\/seq\/wi\/**\/somfzdpte\/bkpht", 90 | "result": "92fd" 91 | }, 92 | { 93 | "path": "\/rogaymfc\/lqbvsdn\/v\/b68b\/ubca", 94 | "wildcard_path": "\/rogaymfc\/lqbvsdn\/v\/**\/ubca", 95 | "result": "b68b" 96 | }, 97 | { 98 | "path": "\/rzpludm\/hbirq\/tmdczkavxquo\/1058\/unhg", 99 | "wildcard_path": "\/rzpludm\/hbirq\/tmdczkavxquo\/**\/unhg", 100 | "result": "1058" 101 | }, 102 | { 103 | "path": "\/ymv\/twqebz\/durzfoby\/3d46", 104 | "wildcard_path": "\/ymv\/twqebz\/durzfoby\/**", 105 | "result": "3d46" 106 | }, 107 | { 108 | "path": "\/yeohd\/cizgsqtrexa\/f380", 109 | "wildcard_path": "\/yeohd\/cizgsqtrexa\/**", 110 | "result": "f380" 111 | }, 112 | { 113 | "path": "\/rzpludm\/hbirq\/tmdczkavxquo\/14b9", 114 | "wildcard_path": "\/rzpludm\/hbirq\/tmdczkavxquo\/**", 115 | "result": "14b9" 116 | }, 117 | { 118 | "path": "\/jnsohl\/igyfz\/izqou\/73b1\/qmhldorvwfze", 119 | "wildcard_path": "\/jnsohl\/igyfz\/izqou\/**\/qmhldorvwfze", 120 | "result": "73b1" 121 | }, 122 | { 123 | "path": "\/dkophsiyl\/uiaeyxshnc\/f136", 124 | "wildcard_path": "\/dkophsiyl\/uiaeyxshnc\/**", 125 | "result": "f136" 126 | }, 127 | { 128 | "path": "\/a\/fq\/da92", 129 | "wildcard_path": "\/a\/fq\/**", 130 | "result": "da92" 131 | }, 132 | { 133 | "path": "\/ulcrqspge\/kst\/bab3", 134 | "wildcard_path": "\/ulcrqspge\/kst\/**", 135 | "result": "bab3" 136 | }, 137 | { 138 | "path": "\/rouxgpbftd\/pbyonujfzdr\/02ef\/zbm", 139 | "wildcard_path": "\/rouxgpbftd\/pbyonujfzdr\/**\/zbm", 140 | "result": "02ef" 141 | }, 142 | { 143 | "path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/b99b\/dhrwn", 144 | "wildcard_path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/**\/dhrwn", 145 | "result": "b99b" 146 | }, 147 | { 148 | "path": "\/ogtczmjsnrd\/vrhst\/apfr\/yxb\/fb3e", 149 | "wildcard_path": "\/ogtczmjsnrd\/vrhst\/apfr\/yxb\/**", 150 | "result": "fb3e" 151 | }, 152 | { 153 | "path": "\/uk\/xt\/a57c", 154 | "wildcard_path": "\/uk\/xt\/**", 155 | "result": "a57c" 156 | }, 157 | { 158 | "path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/ab2b\/mrhoak", 159 | "wildcard_path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/**\/mrhoak", 160 | "result": "ab2b" 161 | }, 162 | { 163 | "path": "\/nbzxuvjpciy\/c2b1", 164 | "wildcard_path": "\/nbzxuvjpciy\/**", 165 | "result": "c2b1" 166 | }, 167 | { 168 | "path": "\/uipfzgkew\/6307", 169 | "wildcard_path": "\/uipfzgkew\/**", 170 | "result": "6307" 171 | }, 172 | { 173 | "path": "\/ymv\/3713", 174 | "wildcard_path": "\/ymv\/**", 175 | "result": "3713" 176 | }, 177 | { 178 | "path": "\/hkueawt\/msolngeybi\/4195\/hb", 179 | "wildcard_path": "\/hkueawt\/msolngeybi\/**\/hb", 180 | "result": "4195" 181 | }, 182 | { 183 | "path": "\/azerbltku\/aowvrf\/9bbd", 184 | "wildcard_path": "\/azerbltku\/aowvrf\/**", 185 | "result": "9bbd" 186 | }, 187 | { 188 | "path": "\/ulcrqspge\/szdb\/exzuodtpb\/5a77\/rdtfbgj", 189 | "wildcard_path": "\/ulcrqspge\/szdb\/exzuodtpb\/**\/rdtfbgj", 190 | "result": "5a77" 191 | }, 192 | { 193 | "path": "\/yeohd\/978f", 194 | "wildcard_path": "\/yeohd\/**", 195 | "result": "978f" 196 | }, 197 | { 198 | "path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/2556\/efbpjqxlz", 199 | "wildcard_path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/**\/efbpjqxlz", 200 | "result": "2556" 201 | }, 202 | { 203 | "path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/nsljvaxy\/b093", 204 | "wildcard_path": "\/itndfo\/xnhpwvjardkt\/mdvygu\/nsljvaxy\/**", 205 | "result": "b093" 206 | }, 207 | { 208 | "path": "\/azerbltku\/yx\/5750", 209 | "wildcard_path": "\/azerbltku\/yx\/**", 210 | "result": "5750" 211 | }, 212 | { 213 | "path": "\/gzpimys\/cfbgxnjk\/jqxa\/550e\/iklmsezcnp", 214 | "wildcard_path": "\/gzpimys\/cfbgxnjk\/jqxa\/**\/iklmsezcnp", 215 | "result": "550e" 216 | }, 217 | { 218 | "path": "\/nlyzqifu\/byuzfhqrmadc\/gto\/71ab", 219 | "wildcard_path": "\/nlyzqifu\/byuzfhqrmadc\/gto\/**", 220 | "result": "71ab" 221 | }, 222 | { 223 | "path": "\/qxsyahvti\/zcifgpysq\/mknjuhvdo\/cf43\/xgpsahyduljo", 224 | "wildcard_path": "\/qxsyahvti\/zcifgpysq\/mknjuhvdo\/**\/xgpsahyduljo", 225 | "result": "cf43" 226 | }, 227 | { 228 | "path": "\/uk\/xt\/acf7\/cipt\/hdpqsni", 229 | "wildcard_path": "\/uk\/xt\/**\/cipt\/hdpqsni", 230 | "result": "acf7" 231 | }, 232 | { 233 | "path": "\/hkueawt\/msolngeybi\/ypfejamgk\/5453", 234 | "wildcard_path": "\/hkueawt\/msolngeybi\/ypfejamgk\/**", 235 | "result": "5453" 236 | }, 237 | { 238 | "path": "\/uipfzgkew\/iazguwc\/8952\/rcbzyhm", 239 | "wildcard_path": "\/uipfzgkew\/iazguwc\/**\/rcbzyhm", 240 | "result": "8952" 241 | }, 242 | { 243 | "path": "\/fyoptkniesdh\/uhbxiof\/fszwtie\/5c9f\/bfxdraizc", 244 | "wildcard_path": "\/fyoptkniesdh\/uhbxiof\/fszwtie\/**\/bfxdraizc", 245 | "result": "5c9f" 246 | }, 247 | { 248 | "path": "\/a\/fq\/ka\/wvrxakitlszg\/6ca3", 249 | "wildcard_path": "\/a\/fq\/ka\/wvrxakitlszg\/**", 250 | "result": "6ca3" 251 | } 252 | ] -------------------------------------------------------------------------------- /core/Benchmark/ReadmeGenerator.php: -------------------------------------------------------------------------------- 1 | ensureResultsDirExists(dirname($this->readmeFilePath)); 40 | 41 | $runTime = date('Y-m-d H:i:s'); 42 | $phpVersion = PHP_VERSION; 43 | 44 | $readmeContent = <<getName(), 111 | $definition->getPackageName(), 112 | InstalledVersions::getPrettyVersion($definition->getPackageName()), 113 | $definition->getUrl(), 114 | ]; 115 | }, $packageDefinitions); 116 | 117 | foreach ($packages as [$name, $package, $version, $url]) { 118 | $readmeContent .= "| $name | [$package]($url) | $version |\n"; 119 | } 120 | 121 | $readmeContent .= "\n\n## Benchmark Results\n\n"; 122 | $readmeContent .= "These tests was run **{$runTime}** on PHP version: **{$phpVersion}**\n\n"; 123 | 124 | foreach ($testSuites as $i => $testSuite) { 125 | $testSuiteNumber = $testSuite->getNumber(); 126 | $testSuiteTitle = $testSuite->getTitle(); 127 | $testSuiteDescription = $testSuite->getDescription(); 128 | 129 | 130 | $readmeContent .= "\n\n### $testSuiteTitle \n\n"; 131 | $readmeContent .= $testSuiteDescription . "\n\n"; 132 | $readmeContent .= "`Test Suite #$testSuiteNumber:`\n\n"; 133 | 134 | $readmeContent .= "| Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) |\n"; 135 | $readmeContent .= "| --- | ------------- | ------ | ------- | ------ | ------ |\n"; 136 | 137 | $rank = 1; 138 | $timeBase = null; 139 | $memoryBase = null; 140 | 141 | foreach ($benchmarkResult->getResults($testSuite) as $containerName => $result) { 142 | if ($result->timeConsumptionInMilliSeconds() !== null) { 143 | $time = sprintf('%.3F', $result->timeConsumptionInMilliSeconds()); 144 | } else { 145 | $time = null; 146 | } 147 | 148 | $memory = $result->peakMemoryUsageInMegaBytes(); 149 | 150 | if ($rank === 1) { 151 | $timeBase = $time ?? null; 152 | $memoryBase = $memory ?? null; 153 | } 154 | 155 | $timeColumn = $time !== null ? round((float)$time, 3) : "N/A"; 156 | $epsilon = 1e-10; // A small threshold to treat near-zero values as zero 157 | $timeBaseFloat = (float)$timeBase; 158 | $timePercentColumn = ($time !== null && abs($timeBaseFloat) > $epsilon) ? round((float)$time / $timeBaseFloat * 100) . "%" : "N/A"; 159 | $memoryColumn = $memory !== null ? round($memory, 3) : "N/A"; 160 | $memoryPercentColumn = $memory !== null && $memoryBase !== null ? round($memory / $memoryBase * 100) . "%" : "N/A"; 161 | 162 | $readmeContent .= "| $rank | **$containerName** | $timeColumn | $timePercentColumn | $memoryColumn | $memoryPercentColumn |\n"; 163 | 164 | $rank++; 165 | 166 | } 167 | 168 | } 169 | 170 | $readmeContent .= <<readmeFilePath, $readmeContent); 242 | 243 | } 244 | 245 | 246 | } -------------------------------------------------------------------------------- /.idea/workspace (kristoffers-mbp.lan's conflicted copy 2025-01-31).xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 13 | 14 | $PROJECT_DIR$/composer.json 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | { 118 | "associatedIndex": 6 119 | } 120 | 121 | 122 | 125 | { 126 | "keyToString": { 127 | "RunOnceActivity.ShowReadmeOnStart": "true", 128 | "last_opened_file_path": "/Users/kristoffer/Dropbox/Web/Rammewerk/php-router-benchmark/src/TestCode", 129 | "node.js.detected.package.eslint": "true", 130 | "node.js.detected.package.tslint": "true", 131 | "node.js.selected.package.eslint": "(autodetect)", 132 | "node.js.selected.package.tslint": "(autodetect)", 133 | "nodejs_package_manager_path": "npm", 134 | "php.override.implement.member.chooser.php.doc": "NONE", 135 | "settings.editor.selected.configurable": "reference.webide.settings.project.settings.php" 136 | } 137 | } 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 1737615427076 182 | 222 | 223 | 224 | 225 | 227 | -------------------------------------------------------------------------------- /src/TestData/test_3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "\/wqovnajeiut\/cynsmtj\/f\/ypjguamc\/qsc", 4 | "wildcard_path": "\/wqovnajeiut\/cynsmtj\/f\/ypjguamc\/qsc", 5 | "result": "a735c2ba" 6 | }, 7 | { 8 | "path": "\/zbxj\/giykumvxzb\/opcwzeil", 9 | "wildcard_path": "\/zbxj\/giykumvxzb\/opcwzeil", 10 | "result": "9c444b78" 11 | }, 12 | { 13 | "path": "\/nkapezx", 14 | "wildcard_path": "\/nkapezx", 15 | "result": "1241a0c9" 16 | }, 17 | { 18 | "path": "\/evthqafbg\/qwlu\/kmcbnwodgpx\/wglmncez\/swdoj", 19 | "wildcard_path": "\/evthqafbg\/qwlu\/kmcbnwodgpx\/wglmncez\/swdoj", 20 | "result": "1ef66849" 21 | }, 22 | { 23 | "path": "\/lqwnshpe\/amrhwgkbtszj\/rilektoxdu\/ieydbhprl\/buzstvyaxmk", 24 | "wildcard_path": "\/lqwnshpe\/amrhwgkbtszj\/rilektoxdu\/ieydbhprl\/buzstvyaxmk", 25 | "result": "b4ab3453" 26 | }, 27 | { 28 | "path": "\/bknpsquyvo\/djigv\/hvptnbzywi\/em", 29 | "wildcard_path": "\/bknpsquyvo\/djigv\/hvptnbzywi\/em", 30 | "result": "7eea2471" 31 | }, 32 | { 33 | "path": "\/vbgyljcf\/sp\/lcjag\/i\/c", 34 | "wildcard_path": "\/vbgyljcf\/sp\/lcjag\/i\/c", 35 | "result": "99910040" 36 | }, 37 | { 38 | "path": "\/hzfgpqct\/poqrtsvyaw\/tc\/drlacfp\/e", 39 | "wildcard_path": "\/hzfgpqct\/poqrtsvyaw\/tc\/drlacfp\/e", 40 | "result": "e1ecfe2d" 41 | }, 42 | { 43 | "path": "\/zbxj\/giykumvxzb\/opcwzeil\/fpet\/gwtjei", 44 | "wildcard_path": "\/zbxj\/giykumvxzb\/opcwzeil\/fpet\/gwtjei", 45 | "result": "639fbffb" 46 | }, 47 | { 48 | "path": "\/j\/emnsdwo", 49 | "wildcard_path": "\/j\/emnsdwo", 50 | "result": "66f346a3" 51 | }, 52 | { 53 | "path": "\/fl\/jlfbeczusa\/iuoclhbnr\/jvslqtf", 54 | "wildcard_path": "\/fl\/jlfbeczusa\/iuoclhbnr\/jvslqtf", 55 | "result": "8d26dfa8" 56 | }, 57 | { 58 | "path": "\/uqe\/mldfotevpx\/qclbjd\/dal", 59 | "wildcard_path": "\/uqe\/mldfotevpx\/qclbjd\/dal", 60 | "result": "83b62610" 61 | }, 62 | { 63 | "path": "\/rngvuzeb\/eabjmfwki\/gtlxynoefmz\/zdxyilhops\/ukxjhtbqpg", 64 | "wildcard_path": "\/rngvuzeb\/eabjmfwki\/gtlxynoefmz\/zdxyilhops\/ukxjhtbqpg", 65 | "result": "3360279f" 66 | }, 67 | { 68 | "path": "\/hs\/fb\/vsbe\/frmsbwedjh", 69 | "wildcard_path": "\/hs\/fb\/vsbe\/frmsbwedjh", 70 | "result": "2457ba92" 71 | }, 72 | { 73 | "path": "\/hp\/mae\/xw\/teckrsb", 74 | "wildcard_path": "\/hp\/mae\/xw\/teckrsb", 75 | "result": "63429886" 76 | }, 77 | { 78 | "path": "\/bs\/hekpaxb\/vhsknxapwc", 79 | "wildcard_path": "\/bs\/hekpaxb\/vhsknxapwc", 80 | "result": "032811d1" 81 | }, 82 | { 83 | "path": "\/hzfgpqct\/poqrtsvyaw\/tc\/jtrlew\/erknxzmlbf", 84 | "wildcard_path": "\/hzfgpqct\/poqrtsvyaw\/tc\/jtrlew\/erknxzmlbf", 85 | "result": "c9ec9337" 86 | }, 87 | { 88 | "path": "\/dimjeonuk\/tvehbmok\/fgt", 89 | "wildcard_path": "\/dimjeonuk\/tvehbmok\/fgt", 90 | "result": "a11e1faa" 91 | }, 92 | { 93 | "path": "\/wevmpyg\/bxsljecyt\/sxuric\/yfrepub\/dzruabofw", 94 | "wildcard_path": "\/wevmpyg\/bxsljecyt\/sxuric\/yfrepub\/dzruabofw", 95 | "result": "71c7f4b6" 96 | }, 97 | { 98 | "path": "\/fwn\/mdultbfy\/cqhbekz\/ou\/owrnzelvsgu", 99 | "wildcard_path": "\/fwn\/mdultbfy\/cqhbekz\/ou\/owrnzelvsgu", 100 | "result": "26e4df19" 101 | }, 102 | { 103 | "path": "\/xneatk\/fxhrkc\/ovtwdruy\/eidqjncy\/j", 104 | "wildcard_path": "\/xneatk\/fxhrkc\/ovtwdruy\/eidqjncy\/j", 105 | "result": "4cf2f1c1" 106 | }, 107 | { 108 | "path": "\/lqwnshpe\/amrhwgkbtszj\/rilektoxdu\/ieydbhprl", 109 | "wildcard_path": "\/lqwnshpe\/amrhwgkbtszj\/rilektoxdu\/ieydbhprl", 110 | "result": "e2c68b06" 111 | }, 112 | { 113 | "path": "\/uqe\/mldfotevpx\/qclbjd", 114 | "wildcard_path": "\/uqe\/mldfotevpx\/qclbjd", 115 | "result": "6d14f130" 116 | }, 117 | { 118 | "path": "\/cofhguryq\/izurwmjstdp\/y\/ctinuklohd\/bsdukpitvm", 119 | "wildcard_path": "\/cofhguryq\/izurwmjstdp\/y\/ctinuklohd\/bsdukpitvm", 120 | "result": "c6b0804b" 121 | }, 122 | { 123 | "path": "\/bwqfejy\/sqvaixludtz\/qynozjlm\/kad\/chuowpmbsr", 124 | "wildcard_path": "\/bwqfejy\/sqvaixludtz\/qynozjlm\/kad\/chuowpmbsr", 125 | "result": "b0318e52" 126 | }, 127 | { 128 | "path": "\/plnjdeu\/wpetrdohmgq\/wbxkoivq\/jkwmztyegdoi", 129 | "wildcard_path": "\/plnjdeu\/wpetrdohmgq\/wbxkoivq\/jkwmztyegdoi", 130 | "result": "db70c7c5" 131 | }, 132 | { 133 | "path": "\/bs", 134 | "wildcard_path": "\/bs", 135 | "result": "48b40bb4" 136 | }, 137 | { 138 | "path": "\/cr\/jkvgbyehdo", 139 | "wildcard_path": "\/cr\/jkvgbyehdo", 140 | "result": "c3274fed" 141 | }, 142 | { 143 | "path": "\/rngvuzeb\/eabjmfwki\/nzbawyrd\/vloxyuzqewp\/zhdmvwsl", 144 | "wildcard_path": "\/rngvuzeb\/eabjmfwki\/nzbawyrd\/vloxyuzqewp\/zhdmvwsl", 145 | "result": "a08024f0" 146 | }, 147 | { 148 | "path": "\/jnefkqdg\/s", 149 | "wildcard_path": "\/jnefkqdg\/s", 150 | "result": "9d42eb51" 151 | }, 152 | { 153 | "path": "\/jnefkqdg\/amrsl", 154 | "wildcard_path": "\/jnefkqdg\/amrsl", 155 | "result": "ebcf9eda" 156 | }, 157 | { 158 | "path": "\/opqfnjl\/wzqgd\/tmzhc\/ivbwjranflyu\/zpwvjcsdx", 159 | "wildcard_path": "\/opqfnjl\/wzqgd\/tmzhc\/ivbwjranflyu\/zpwvjcsdx", 160 | "result": "cac2e679" 161 | }, 162 | { 163 | "path": "\/lqjyr\/bgaepqivlmn\/klsqyrmtvw\/jsdnyqwgi\/wvlnbmu", 164 | "wildcard_path": "\/lqjyr\/bgaepqivlmn\/klsqyrmtvw\/jsdnyqwgi\/wvlnbmu", 165 | "result": "f31f2a06" 166 | }, 167 | { 168 | "path": "\/opqfnjl\/wzqgd\/eytj", 169 | "wildcard_path": "\/opqfnjl\/wzqgd\/eytj", 170 | "result": "aeebd408" 171 | }, 172 | { 173 | "path": "\/x\/vlcimk\/zmvslbta\/efqorjlyiut", 174 | "wildcard_path": "\/x\/vlcimk\/zmvslbta\/efqorjlyiut", 175 | "result": "78a696f0" 176 | }, 177 | { 178 | "path": "\/bwqfejy\/sqvaixludtz\/uncxviwlabrq\/f\/ik", 179 | "wildcard_path": "\/bwqfejy\/sqvaixludtz\/uncxviwlabrq\/f\/ik", 180 | "result": "285678d3" 181 | }, 182 | { 183 | "path": "\/rehcizkyq\/lex", 184 | "wildcard_path": "\/rehcizkyq\/lex", 185 | "result": "9abd61f7" 186 | }, 187 | { 188 | "path": "\/wevmpyg\/bxsljecyt\/sxuric\/yfrepub\/e", 189 | "wildcard_path": "\/wevmpyg\/bxsljecyt\/sxuric\/yfrepub\/e", 190 | "result": "e2275cc2" 191 | }, 192 | { 193 | "path": "\/rngvuzeb\/eabjmfwki\/n", 194 | "wildcard_path": "\/rngvuzeb\/eabjmfwki\/n", 195 | "result": "694d3f30" 196 | }, 197 | { 198 | "path": "\/lqjyr\/bgaepqivlmn\/xyj\/lrcwom\/moidsrxefzqh", 199 | "wildcard_path": "\/lqjyr\/bgaepqivlmn\/xyj\/lrcwom\/moidsrxefzqh", 200 | "result": "b2d0ea6e" 201 | }, 202 | { 203 | "path": "\/wevmpyg\/bxsljecyt\/sxuric\/yfrepub\/wdpxuf", 204 | "wildcard_path": "\/wevmpyg\/bxsljecyt\/sxuric\/yfrepub\/wdpxuf", 205 | "result": "eca42765" 206 | }, 207 | { 208 | "path": "\/djeto\/rgchik\/dftb\/mfjdspcnwt", 209 | "wildcard_path": "\/djeto\/rgchik\/dftb\/mfjdspcnwt", 210 | "result": "5cd24ef5" 211 | }, 212 | { 213 | "path": "\/clhwxyr\/cfsa\/xjg\/z\/yumkvrobdps", 214 | "wildcard_path": "\/clhwxyr\/cfsa\/xjg\/z\/yumkvrobdps", 215 | "result": "09049d82" 216 | }, 217 | { 218 | "path": "\/evthqafbg\/qwlu\/kmcbnwodgpx", 219 | "wildcard_path": "\/evthqafbg\/qwlu\/kmcbnwodgpx", 220 | "result": "91250b6a" 221 | }, 222 | { 223 | "path": "\/fnmjbcuwtyx\/uligxrwstvoj\/atoqhjzpb\/gm\/wbxnq", 224 | "wildcard_path": "\/fnmjbcuwtyx\/uligxrwstvoj\/atoqhjzpb\/gm\/wbxnq", 225 | "result": "ba5870df" 226 | }, 227 | { 228 | "path": "\/wqovnajeiut\/cynsmtj\/f\/ypjguamc", 229 | "wildcard_path": "\/wqovnajeiut\/cynsmtj\/f\/ypjguamc", 230 | "result": "c330fd60" 231 | }, 232 | { 233 | "path": "\/xneatk\/fxhrkc\/ovtwdruy\/eidqjncy", 234 | "wildcard_path": "\/xneatk\/fxhrkc\/ovtwdruy\/eidqjncy", 235 | "result": "b36bf8ce" 236 | }, 237 | { 238 | "path": "\/uahwfoyrbjst\/rovxukp\/p\/gd", 239 | "wildcard_path": "\/uahwfoyrbjst\/rovxukp\/p\/gd", 240 | "result": "b3d5bb08" 241 | }, 242 | { 243 | "path": "\/mjxt\/bmgdpz\/veh\/zfeqktyox", 244 | "wildcard_path": "\/mjxt\/bmgdpz\/veh\/zfeqktyox", 245 | "result": "e38bddaf" 246 | }, 247 | { 248 | "path": "\/fnmjbcuwtyx\/uligxrwstvoj\/nkpwdxuylgzo", 249 | "wildcard_path": "\/fnmjbcuwtyx\/uligxrwstvoj\/nkpwdxuylgzo", 250 | "result": "1523b8e3" 251 | }, 252 | { 253 | "path": "\/fwn\/mdultbfy\/cqhbekz\/ou", 254 | "wildcard_path": "\/fwn\/mdultbfy\/cqhbekz\/ou", 255 | "result": "d9cc93b3" 256 | }, 257 | { 258 | "path": "\/bs\/ybarhpf\/nbkxqlyetcjr", 259 | "wildcard_path": "\/bs\/ybarhpf\/nbkxqlyetcjr", 260 | "result": "4e5edc8b" 261 | }, 262 | { 263 | "path": "\/cofhguryq", 264 | "wildcard_path": "\/cofhguryq", 265 | "result": "277ea52e" 266 | }, 267 | { 268 | "path": "\/uahwfoyrbjst\/rovxukp\/p\/lmvdcz", 269 | "wildcard_path": "\/uahwfoyrbjst\/rovxukp\/p\/lmvdcz", 270 | "result": "b803ed4d" 271 | }, 272 | { 273 | "path": "\/xneatk\/fxhrkc\/ovtwdruy\/eidqjncy\/nfdeyzja", 274 | "wildcard_path": "\/xneatk\/fxhrkc\/ovtwdruy\/eidqjncy\/nfdeyzja", 275 | "result": "06957f88" 276 | }, 277 | { 278 | "path": "\/nl\/sjia\/jruvcwexg\/ezsxcra", 279 | "wildcard_path": "\/nl\/sjia\/jruvcwexg\/ezsxcra", 280 | "result": "c3095e31" 281 | }, 282 | { 283 | "path": "\/iebr\/sbotu\/bntcvy", 284 | "wildcard_path": "\/iebr\/sbotu\/bntcvy", 285 | "result": "a5182090" 286 | }, 287 | { 288 | "path": "\/lqjyr\/bgaepqivlmn\/wtfk", 289 | "wildcard_path": "\/lqjyr\/bgaepqivlmn\/wtfk", 290 | "result": "99cc9e22" 291 | }, 292 | { 293 | "path": "\/hzfgpqct\/poqrtsvyaw\/tc", 294 | "wildcard_path": "\/hzfgpqct\/poqrtsvyaw\/tc", 295 | "result": "8d65ee2b" 296 | }, 297 | { 298 | "path": "\/cofhguryq\/ixmvabnoswu", 299 | "wildcard_path": "\/cofhguryq\/ixmvabnoswu", 300 | "result": "f6aa8fed" 301 | }, 302 | { 303 | "path": "\/uzxflery\/txpufhrdjazi\/piqlroaesfz\/ekyoailb\/xqjulsotmpa", 304 | "wildcard_path": "\/uzxflery\/txpufhrdjazi\/piqlroaesfz\/ekyoailb\/xqjulsotmpa", 305 | "result": "600284d7" 306 | }, 307 | { 308 | "path": "\/j\/emnsdwo\/hdotyx\/mdlcwzx", 309 | "wildcard_path": "\/j\/emnsdwo\/hdotyx\/mdlcwzx", 310 | "result": "314bedca" 311 | }, 312 | { 313 | "path": "\/jvmy\/v\/aytobghizp\/ylaoz", 314 | "wildcard_path": "\/jvmy\/v\/aytobghizp\/ylaoz", 315 | "result": "7dd6247e" 316 | }, 317 | { 318 | "path": "\/gxzikqvc\/wkifg\/dzbekqj", 319 | "wildcard_path": "\/gxzikqvc\/wkifg\/dzbekqj", 320 | "result": "d72ad3a3" 321 | }, 322 | { 323 | "path": "\/hzfgpqct\/poqrtsvyaw\/tc\/axqlmgnhdwe\/lqfzngwo", 324 | "wildcard_path": "\/hzfgpqct\/poqrtsvyaw\/tc\/axqlmgnhdwe\/lqfzngwo", 325 | "result": "41b233f4" 326 | }, 327 | { 328 | "path": "\/hs\/fb\/vsbe\/frmsbwedjh\/qskpj", 329 | "wildcard_path": "\/hs\/fb\/vsbe\/frmsbwedjh\/qskpj", 330 | "result": "eaa1c5b2" 331 | }, 332 | { 333 | "path": "\/tvjgcosz\/rvxnzsk\/qpumbt", 334 | "wildcard_path": "\/tvjgcosz\/rvxnzsk\/qpumbt", 335 | "result": "68171fbd" 336 | }, 337 | { 338 | "path": "\/rngvuzeb\/eabjmfwki", 339 | "wildcard_path": "\/rngvuzeb\/eabjmfwki", 340 | "result": "58524b1e" 341 | }, 342 | { 343 | "path": "\/djeto\/lhtvpczg", 344 | "wildcard_path": "\/djeto\/lhtvpczg", 345 | "result": "1ec47a1f" 346 | }, 347 | { 348 | "path": "\/opqfnjl\/wzqgd", 349 | "wildcard_path": "\/opqfnjl\/wzqgd", 350 | "result": "68671e06" 351 | }, 352 | { 353 | "path": "\/rehcizkyq\/lex\/hvgxjtkfb\/aqumlpej", 354 | "wildcard_path": "\/rehcizkyq\/lex\/hvgxjtkfb\/aqumlpej", 355 | "result": "b00a9904" 356 | }, 357 | { 358 | "path": "\/vbd\/hdcyoranq\/iaom\/tvbafywocgn", 359 | "wildcard_path": "\/vbd\/hdcyoranq\/iaom\/tvbafywocgn", 360 | "result": "367e69e7" 361 | }, 362 | { 363 | "path": "\/uzxflery\/txpufhrdjazi\/piqlroaesfz\/urdefxoq\/rhijsqxnvgoy", 364 | "wildcard_path": "\/uzxflery\/txpufhrdjazi\/piqlroaesfz\/urdefxoq\/rhijsqxnvgoy", 365 | "result": "788bc940" 366 | }, 367 | { 368 | "path": "\/fl\/jlfbeczusa", 369 | "wildcard_path": "\/fl\/jlfbeczusa", 370 | "result": "f50dea5d" 371 | }, 372 | { 373 | "path": "\/rngvuzeb\/eabjmfwki\/ktefbrji\/h", 374 | "wildcard_path": "\/rngvuzeb\/eabjmfwki\/ktefbrji\/h", 375 | "result": "562b4f29" 376 | }, 377 | { 378 | "path": "\/vbgyljcf\/sp\/lcjag\/i\/fhxw", 379 | "wildcard_path": "\/vbgyljcf\/sp\/lcjag\/i\/fhxw", 380 | "result": "de4af9cd" 381 | }, 382 | { 383 | "path": "\/lqwnshpe\/amrhwgkbtszj\/rilektoxdu\/ieydbhprl\/pdmzrhinbo", 384 | "wildcard_path": "\/lqwnshpe\/amrhwgkbtszj\/rilektoxdu\/ieydbhprl\/pdmzrhinbo", 385 | "result": "0ca6ec9f" 386 | }, 387 | { 388 | "path": "\/j", 389 | "wildcard_path": "\/j", 390 | "result": "97f8cca1" 391 | }, 392 | { 393 | "path": "\/dimjeonuk\/ivbkandq", 394 | "wildcard_path": "\/dimjeonuk\/ivbkandq", 395 | "result": "a5e9dcd4" 396 | }, 397 | { 398 | "path": "\/lqjyr\/bgaepqivlmn\/ndervujopscg", 399 | "wildcard_path": "\/lqjyr\/bgaepqivlmn\/ndervujopscg", 400 | "result": "deea8a6d" 401 | }, 402 | { 403 | "path": "\/cofhguryq\/ga\/mivwtn", 404 | "wildcard_path": "\/cofhguryq\/ga\/mivwtn", 405 | "result": "06c400c8" 406 | }, 407 | { 408 | "path": "\/djeto\/orvmial", 409 | "wildcard_path": "\/djeto\/orvmial", 410 | "result": "c62c473b" 411 | }, 412 | { 413 | "path": "\/wldnvtzsaqi\/qwyzfm\/wadsmnr", 414 | "wildcard_path": "\/wldnvtzsaqi\/qwyzfm\/wadsmnr", 415 | "result": "4771b431" 416 | }, 417 | { 418 | "path": "\/jnefkqdg\/qxyjrbzfhs\/n", 419 | "wildcard_path": "\/jnefkqdg\/qxyjrbzfhs\/n", 420 | "result": "11eb438f" 421 | }, 422 | { 423 | "path": "\/opqfnjl\/wzqgd\/qfxme\/mu\/nygzjctkwaf", 424 | "wildcard_path": "\/opqfnjl\/wzqgd\/qfxme\/mu\/nygzjctkwaf", 425 | "result": "684a1bef" 426 | }, 427 | { 428 | "path": "\/clhwxyr\/cfsa", 429 | "wildcard_path": "\/clhwxyr\/cfsa", 430 | "result": "d1389aa3" 431 | }, 432 | { 433 | "path": "\/hs\/fb\/vsbe\/frmsbwedjh\/lv", 434 | "wildcard_path": "\/hs\/fb\/vsbe\/frmsbwedjh\/lv", 435 | "result": "808b7cd8" 436 | }, 437 | { 438 | "path": "\/djeto\/chqf\/cpnkthr\/hufvposxj\/ni", 439 | "wildcard_path": "\/djeto\/chqf\/cpnkthr\/hufvposxj\/ni", 440 | "result": "77d2c97d" 441 | }, 442 | { 443 | "path": "\/zbxj\/giykumvxzb\/opcwzeil\/b\/islctgwz", 444 | "wildcard_path": "\/zbxj\/giykumvxzb\/opcwzeil\/b\/islctgwz", 445 | "result": "97e7af12" 446 | }, 447 | { 448 | "path": "\/vbd\/hdcyoranq\/iaom\/aexgif\/dxyanpsvugrb", 449 | "wildcard_path": "\/vbd\/hdcyoranq\/iaom\/aexgif\/dxyanpsvugrb", 450 | "result": "83bc29e0" 451 | }, 452 | { 453 | "path": "\/bknpsquyvo\/djigv\/hvptnbzywi\/em\/ughdcvj", 454 | "wildcard_path": "\/bknpsquyvo\/djigv\/hvptnbzywi\/em\/ughdcvj", 455 | "result": "5c89acb5" 456 | }, 457 | { 458 | "path": "\/gxzikqvc\/wkifg\/dzbekqj\/mjdfsny\/tavkilhywe", 459 | "wildcard_path": "\/gxzikqvc\/wkifg\/dzbekqj\/mjdfsny\/tavkilhywe", 460 | "result": "a9dc10b0" 461 | }, 462 | { 463 | "path": "\/opqfnjl\/wzqgd\/qkzpwvjay\/gewrj", 464 | "wildcard_path": "\/opqfnjl\/wzqgd\/qkzpwvjay\/gewrj", 465 | "result": "e1065e5a" 466 | }, 467 | { 468 | "path": "\/ovuzdnpycga\/kotcg\/rualwcshf\/mqwfoczjsye", 469 | "wildcard_path": "\/ovuzdnpycga\/kotcg\/rualwcshf\/mqwfoczjsye", 470 | "result": "83c1e5dd" 471 | }, 472 | { 473 | "path": "\/nkapezx\/pzt", 474 | "wildcard_path": "\/nkapezx\/pzt", 475 | "result": "414a54fe" 476 | }, 477 | { 478 | "path": "\/vbgyljcf\/sp\/lcjag\/i", 479 | "wildcard_path": "\/vbgyljcf\/sp\/lcjag\/i", 480 | "result": "8675e754" 481 | }, 482 | { 483 | "path": "\/wjkelquor\/bfqtywpiu\/vtrizugksbal\/coknftajg", 484 | "wildcard_path": "\/wjkelquor\/bfqtywpiu\/vtrizugksbal\/coknftajg", 485 | "result": "30512aa3" 486 | }, 487 | { 488 | "path": "\/nkapezx\/extgqui\/h", 489 | "wildcard_path": "\/nkapezx\/extgqui\/h", 490 | "result": "55adb4dc" 491 | }, 492 | { 493 | "path": "\/cpkfosea\/jiomyxabgne\/igjrd\/wk", 494 | "wildcard_path": "\/cpkfosea\/jiomyxabgne\/igjrd\/wk", 495 | "result": "84c73fb6" 496 | }, 497 | { 498 | "path": "\/bwqfejy\/sqvaixludtz", 499 | "wildcard_path": "\/bwqfejy\/sqvaixludtz", 500 | "result": "7d562e9e" 501 | } 502 | ] -------------------------------------------------------------------------------- /src/TestData/test_9.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "\/xchvlmwibsp\/muck\/gfelpnq\/b0cd", 4 | "wildcard_path": "\/xchvlmwibsp\/muck\/gfelpnq\/**", 5 | "result": "b0cd" 6 | }, 7 | { 8 | "path": "\/fdmbrexcqj\/rju\/wpglrthk\/6139\/ci", 9 | "wildcard_path": "\/fdmbrexcqj\/rju\/wpglrthk\/**\/ci", 10 | "result": "6139" 11 | }, 12 | { 13 | "path": "\/l\/tzegpwkocmbv\/qjcfyrzvu\/mtyraxgzvu\/ijyxbr\/26cd", 14 | "wildcard_path": "\/l\/tzegpwkocmbv\/qjcfyrzvu\/mtyraxgzvu\/ijyxbr\/**", 15 | "result": "26cd" 16 | }, 17 | { 18 | "path": "\/ofjwdmzbqnt\/c\/vnj\/e480\/wrmy\/mcavwjs", 19 | "wildcard_path": "\/ofjwdmzbqnt\/c\/vnj\/**\/wrmy\/mcavwjs", 20 | "result": "e480" 21 | }, 22 | { 23 | "path": "\/qwrcjbexovdp\/ysnkit\/ndtrzbejlw\/zila\/cb43", 24 | "wildcard_path": "\/qwrcjbexovdp\/ysnkit\/ndtrzbejlw\/zila\/**", 25 | "result": "cb43" 26 | }, 27 | { 28 | "path": "\/hrqcm\/skmqfhezali\/jcozr\/rl\/b640\/wogdh", 29 | "wildcard_path": "\/hrqcm\/skmqfhezali\/jcozr\/rl\/**\/wogdh", 30 | "result": "b640" 31 | }, 32 | { 33 | "path": "\/wvxk\/dkcgxsifatou\/hbdcusnfevpt\/o\/2212", 34 | "wildcard_path": "\/wvxk\/dkcgxsifatou\/hbdcusnfevpt\/o\/**", 35 | "result": "2212" 36 | }, 37 | { 38 | "path": "\/ifas\/jthy\/bfcoaqvyr\/wozpv\/f02b", 39 | "wildcard_path": "\/ifas\/jthy\/bfcoaqvyr\/wozpv\/**", 40 | "result": "f02b" 41 | }, 42 | { 43 | "path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/c1ae", 44 | "wildcard_path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/**", 45 | "result": "c1ae" 46 | }, 47 | { 48 | "path": "\/wvxk\/dkcgxsifatou\/hbdcusnfevpt\/o\/idovkramy\/ca91", 49 | "wildcard_path": "\/wvxk\/dkcgxsifatou\/hbdcusnfevpt\/o\/idovkramy\/**", 50 | "result": "ca91" 51 | }, 52 | { 53 | "path": "\/sarij\/zobd\/027d\/nv\/ksjvgp\/viyxupthdb", 54 | "wildcard_path": "\/sarij\/zobd\/**\/nv\/ksjvgp\/viyxupthdb", 55 | "result": "027d" 56 | }, 57 | { 58 | "path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/26e9\/heodnvc", 59 | "wildcard_path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/**\/heodnvc", 60 | "result": "26e9" 61 | }, 62 | { 63 | "path": "\/l\/tzegpwkocmbv\/299d\/qyzobv", 64 | "wildcard_path": "\/l\/tzegpwkocmbv\/**\/qyzobv", 65 | "result": "299d" 66 | }, 67 | { 68 | "path": "\/mz\/imyatfxhqb\/0f71\/ew\/jft", 69 | "wildcard_path": "\/mz\/imyatfxhqb\/**\/ew\/jft", 70 | "result": "0f71" 71 | }, 72 | { 73 | "path": "\/imentqdg\/ndfglucop\/yv\/rzqpejyowv\/ea2e", 74 | "wildcard_path": "\/imentqdg\/ndfglucop\/yv\/rzqpejyowv\/**", 75 | "result": "ea2e" 76 | }, 77 | { 78 | "path": "\/mhcjdrwg\/tpkdzybfleug\/mehoqfdgaj\/igehjcok\/362f", 79 | "wildcard_path": "\/mhcjdrwg\/tpkdzybfleug\/mehoqfdgaj\/igehjcok\/**", 80 | "result": "362f" 81 | }, 82 | { 83 | "path": "\/qwrcjbexovdp\/ysnkit\/ndtrzbejlw\/zila\/bawcmgevh\/4e9f", 84 | "wildcard_path": "\/qwrcjbexovdp\/ysnkit\/ndtrzbejlw\/zila\/bawcmgevh\/**", 85 | "result": "4e9f" 86 | }, 87 | { 88 | "path": "\/dvsnqmrfulgj\/b70c\/lme", 89 | "wildcard_path": "\/dvsnqmrfulgj\/**\/lme", 90 | "result": "b70c" 91 | }, 92 | { 93 | "path": "\/lpkhyrwsgjq\/p\/erc\/b7e7", 94 | "wildcard_path": "\/lpkhyrwsgjq\/p\/erc\/**", 95 | "result": "b7e7" 96 | }, 97 | { 98 | "path": "\/aevx\/aswtlbfhxpnm\/xhmynro\/gezw\/a4d5", 99 | "wildcard_path": "\/aevx\/aswtlbfhxpnm\/xhmynro\/gezw\/**", 100 | "result": "a4d5" 101 | }, 102 | { 103 | "path": "\/gp\/xjcdabthym\/ilmoxrgsput\/jnrmbokgw\/7eb9", 104 | "wildcard_path": "\/gp\/xjcdabthym\/ilmoxrgsput\/jnrmbokgw\/**", 105 | "result": "7eb9" 106 | }, 107 | { 108 | "path": "\/gjcszufvr\/b\/fdsmkoj\/62f2\/dyxauf\/bxjhwelavtg", 109 | "wildcard_path": "\/gjcszufvr\/b\/fdsmkoj\/**\/dyxauf\/bxjhwelavtg", 110 | "result": "62f2" 111 | }, 112 | { 113 | "path": "\/uqnbxlidejy\/y\/t\/rlgipmhcn\/064f", 114 | "wildcard_path": "\/uqnbxlidejy\/y\/t\/rlgipmhcn\/**", 115 | "result": "064f" 116 | }, 117 | { 118 | "path": "\/tdfljxwcykm\/tvjzlfqgyki\/f\/9092\/dbalmogpq\/sw", 119 | "wildcard_path": "\/tdfljxwcykm\/tvjzlfqgyki\/f\/**\/dbalmogpq\/sw", 120 | "result": "9092" 121 | }, 122 | { 123 | "path": "\/tdfljxwcykm\/tvjzlfqgyki\/f\/jxw\/ec9b", 124 | "wildcard_path": "\/tdfljxwcykm\/tvjzlfqgyki\/f\/jxw\/**", 125 | "result": "ec9b" 126 | }, 127 | { 128 | "path": "\/ndi\/adq\/soelxcutkpay\/a0d5", 129 | "wildcard_path": "\/ndi\/adq\/soelxcutkpay\/**", 130 | "result": "a0d5" 131 | }, 132 | { 133 | "path": "\/cjwzugiq\/ruhlp\/d\/almt\/lo\/ed09", 134 | "wildcard_path": "\/cjwzugiq\/ruhlp\/d\/almt\/lo\/**", 135 | "result": "ed09" 136 | }, 137 | { 138 | "path": "\/amkinvdhc\/hnjfs\/xutaj\/pqaybgtkvw\/cc53\/kiofjt", 139 | "wildcard_path": "\/amkinvdhc\/hnjfs\/xutaj\/pqaybgtkvw\/**\/kiofjt", 140 | "result": "cc53" 141 | }, 142 | { 143 | "path": "\/smbw\/w\/ir\/d089", 144 | "wildcard_path": "\/smbw\/w\/ir\/**", 145 | "result": "d089" 146 | }, 147 | { 148 | "path": "\/z\/ulef\/kdvhacxuboqs\/zlg\/9302", 149 | "wildcard_path": "\/z\/ulef\/kdvhacxuboqs\/zlg\/**", 150 | "result": "9302" 151 | }, 152 | { 153 | "path": "\/cjwzugiq\/ruhlp\/d\/almt\/f131", 154 | "wildcard_path": "\/cjwzugiq\/ruhlp\/d\/almt\/**", 155 | "result": "f131" 156 | }, 157 | { 158 | "path": "\/b\/p\/jqwgpveco\/e08d\/htoqwnipyx\/jxoywlpieu", 159 | "wildcard_path": "\/b\/p\/jqwgpveco\/**\/htoqwnipyx\/jxoywlpieu", 160 | "result": "e08d" 161 | }, 162 | { 163 | "path": "\/uvrqjox\/cisovqnzx\/dsgqavpz\/xqkndptluiha\/572d", 164 | "wildcard_path": "\/uvrqjox\/cisovqnzx\/dsgqavpz\/xqkndptluiha\/**", 165 | "result": "572d" 166 | }, 167 | { 168 | "path": "\/dck\/a5f3", 169 | "wildcard_path": "\/dck\/**", 170 | "result": "a5f3" 171 | }, 172 | { 173 | "path": "\/hrqcm\/skmqfhezali\/ef07", 174 | "wildcard_path": "\/hrqcm\/skmqfhezali\/**", 175 | "result": "ef07" 176 | }, 177 | { 178 | "path": "\/dvsnqmrfulgj\/vxdlniuc\/80d8\/znrq\/hawgkl\/ictpx", 179 | "wildcard_path": "\/dvsnqmrfulgj\/vxdlniuc\/**\/znrq\/hawgkl\/ictpx", 180 | "result": "80d8" 181 | }, 182 | { 183 | "path": "\/ndi\/kn\/a379", 184 | "wildcard_path": "\/ndi\/kn\/**", 185 | "result": "a379" 186 | }, 187 | { 188 | "path": "\/lrs\/tdkesvr\/7dd6", 189 | "wildcard_path": "\/lrs\/tdkesvr\/**", 190 | "result": "7dd6" 191 | }, 192 | { 193 | "path": "\/bd\/qksga\/ulq\/txrwelmodz\/a4f3\/iqhrtzuk", 194 | "wildcard_path": "\/bd\/qksga\/ulq\/txrwelmodz\/**\/iqhrtzuk", 195 | "result": "a4f3" 196 | }, 197 | { 198 | "path": "\/dck\/r\/793f\/h", 199 | "wildcard_path": "\/dck\/r\/**\/h", 200 | "result": "793f" 201 | }, 202 | { 203 | "path": "\/dck\/ateko\/92fb\/zevtjw", 204 | "wildcard_path": "\/dck\/ateko\/**\/zevtjw", 205 | "result": "92fb" 206 | }, 207 | { 208 | "path": "\/aqhiwsvepok\/twafodmvjnp\/mrgqizuvh\/afowmiv\/f5a9", 209 | "wildcard_path": "\/aqhiwsvepok\/twafodmvjnp\/mrgqizuvh\/afowmiv\/**", 210 | "result": "f5a9" 211 | }, 212 | { 213 | "path": "\/bd\/qksga\/ulq\/zf\/t\/c9f7", 214 | "wildcard_path": "\/bd\/qksga\/ulq\/zf\/t\/**", 215 | "result": "c9f7" 216 | }, 217 | { 218 | "path": "\/gp\/xjcdabthym\/ilmoxrgsput\/656c", 219 | "wildcard_path": "\/gp\/xjcdabthym\/ilmoxrgsput\/**", 220 | "result": "656c" 221 | }, 222 | { 223 | "path": "\/uvrqjox\/cisovqnzx\/dsgqavpz\/xqkndptluiha\/svdxglouwjma\/2805", 224 | "wildcard_path": "\/uvrqjox\/cisovqnzx\/dsgqavpz\/xqkndptluiha\/svdxglouwjma\/**", 225 | "result": "2805" 226 | }, 227 | { 228 | "path": "\/bqk\/fbdyu\/qym\/t\/wvrqcz\/b66c", 229 | "wildcard_path": "\/bqk\/fbdyu\/qym\/t\/wvrqcz\/**", 230 | "result": "b66c" 231 | }, 232 | { 233 | "path": "\/yvngkeubsahw\/pltog\/c\/5c40\/ubow\/e", 234 | "wildcard_path": "\/yvngkeubsahw\/pltog\/c\/**\/ubow\/e", 235 | "result": "5c40" 236 | }, 237 | { 238 | "path": "\/lpkhyrwsgjq\/p\/erc\/c3a9\/a", 239 | "wildcard_path": "\/lpkhyrwsgjq\/p\/erc\/**\/a", 240 | "result": "c3a9" 241 | }, 242 | { 243 | "path": "\/z\/ulef\/kdvhacxuboqs\/1981\/yvlpc", 244 | "wildcard_path": "\/z\/ulef\/kdvhacxuboqs\/**\/yvlpc", 245 | "result": "1981" 246 | }, 247 | { 248 | "path": "\/imentqdg\/ndfglucop\/yv\/k\/2230", 249 | "wildcard_path": "\/imentqdg\/ndfglucop\/yv\/k\/**", 250 | "result": "2230" 251 | }, 252 | { 253 | "path": "\/b\/p\/jqwgpveco\/adyhxivu\/af86", 254 | "wildcard_path": "\/b\/p\/jqwgpveco\/adyhxivu\/**", 255 | "result": "af86" 256 | }, 257 | { 258 | "path": "\/hrqcm\/skmqfhezali\/tzcvij\/3aeb", 259 | "wildcard_path": "\/hrqcm\/skmqfhezali\/tzcvij\/**", 260 | "result": "3aeb" 261 | }, 262 | { 263 | "path": "\/fdmbrexcqj\/rju\/wpglrthk\/bf\/e985", 264 | "wildcard_path": "\/fdmbrexcqj\/rju\/wpglrthk\/bf\/**", 265 | "result": "e985" 266 | }, 267 | { 268 | "path": "\/pwcfjz\/fvtsywzdgi\/5ca9", 269 | "wildcard_path": "\/pwcfjz\/fvtsywzdgi\/**", 270 | "result": "5ca9" 271 | }, 272 | { 273 | "path": "\/zgctv\/kjtsdb\/cjwxp\/niyxp\/4697", 274 | "wildcard_path": "\/zgctv\/kjtsdb\/cjwxp\/niyxp\/**", 275 | "result": "4697" 276 | }, 277 | { 278 | "path": "\/nmfq\/xlkqmbsrzeh\/eac1\/u\/uhzsejvqfx", 279 | "wildcard_path": "\/nmfq\/xlkqmbsrzeh\/**\/u\/uhzsejvqfx", 280 | "result": "eac1" 281 | }, 282 | { 283 | "path": "\/hrqcm\/skmqfhezali\/o\/eag\/8234\/tsi", 284 | "wildcard_path": "\/hrqcm\/skmqfhezali\/o\/eag\/**\/tsi", 285 | "result": "8234" 286 | }, 287 | { 288 | "path": "\/bqk\/fbdyu\/qym\/t\/dd00", 289 | "wildcard_path": "\/bqk\/fbdyu\/qym\/t\/**", 290 | "result": "dd00" 291 | }, 292 | { 293 | "path": "\/dszopnrkvca\/lxogq\/tmjhcrioxaf\/uigzhtfbm\/1034", 294 | "wildcard_path": "\/dszopnrkvca\/lxogq\/tmjhcrioxaf\/uigzhtfbm\/**", 295 | "result": "1034" 296 | }, 297 | { 298 | "path": "\/ofjwdmzbqnt\/c\/vnj\/4837", 299 | "wildcard_path": "\/ofjwdmzbqnt\/c\/vnj\/**", 300 | "result": "4837" 301 | }, 302 | { 303 | "path": "\/aqhiwsvepok\/twafodmvjnp\/mrgqizuvh\/afowmiv\/h\/e4d8", 304 | "wildcard_path": "\/aqhiwsvepok\/twafodmvjnp\/mrgqizuvh\/afowmiv\/h\/**", 305 | "result": "e4d8" 306 | }, 307 | { 308 | "path": "\/tdfljxwcykm\/tvjzlfqgyki\/f\/dxbpi\/02ba\/yfjlkbu", 309 | "wildcard_path": "\/tdfljxwcykm\/tvjzlfqgyki\/f\/dxbpi\/**\/yfjlkbu", 310 | "result": "02ba" 311 | }, 312 | { 313 | "path": "\/ndi\/3b97", 314 | "wildcard_path": "\/ndi\/**", 315 | "result": "3b97" 316 | }, 317 | { 318 | "path": "\/fxsed\/ruxizhyteqa\/037c", 319 | "wildcard_path": "\/fxsed\/ruxizhyteqa\/**", 320 | "result": "037c" 321 | }, 322 | { 323 | "path": "\/l\/tzegpwkocmbv\/3107", 324 | "wildcard_path": "\/l\/tzegpwkocmbv\/**", 325 | "result": "3107" 326 | }, 327 | { 328 | "path": "\/dvsnqmrfulgj\/3734\/tqsbpwm", 329 | "wildcard_path": "\/dvsnqmrfulgj\/**\/tqsbpwm", 330 | "result": "3734" 331 | }, 332 | { 333 | "path": "\/bd\/qksga\/ulq\/pnbiyvclsawq\/8601", 334 | "wildcard_path": "\/bd\/qksga\/ulq\/pnbiyvclsawq\/**", 335 | "result": "8601" 336 | }, 337 | { 338 | "path": "\/ph\/vhilf\/qwcue\/b329", 339 | "wildcard_path": "\/ph\/vhilf\/qwcue\/**", 340 | "result": "b329" 341 | }, 342 | { 343 | "path": "\/aqhiwsvepok\/twafodmvjnp\/mrgqizuvh\/afowmiv\/859d\/gbfukmynzhl", 344 | "wildcard_path": "\/aqhiwsvepok\/twafodmvjnp\/mrgqizuvh\/afowmiv\/**\/gbfukmynzhl", 345 | "result": "859d" 346 | }, 347 | { 348 | "path": "\/pwcfjz\/dd52\/if", 349 | "wildcard_path": "\/pwcfjz\/**\/if", 350 | "result": "dd52" 351 | }, 352 | { 353 | "path": "\/imentqdg\/ndfglucop\/yv\/77b0\/ilbvjhz", 354 | "wildcard_path": "\/imentqdg\/ndfglucop\/yv\/**\/ilbvjhz", 355 | "result": "77b0" 356 | }, 357 | { 358 | "path": "\/amkinvdhc\/ce97", 359 | "wildcard_path": "\/amkinvdhc\/**", 360 | "result": "ce97" 361 | }, 362 | { 363 | "path": "\/cjwzugiq\/ruhlp\/d\/almt\/a79b\/wlmajdbpfcxu", 364 | "wildcard_path": "\/cjwzugiq\/ruhlp\/d\/almt\/**\/wlmajdbpfcxu", 365 | "result": "a79b" 366 | }, 367 | { 368 | "path": "\/ph\/vhilf\/qwcue\/emwskoclh\/y\/069f", 369 | "wildcard_path": "\/ph\/vhilf\/qwcue\/emwskoclh\/y\/**", 370 | "result": "069f" 371 | }, 372 | { 373 | "path": "\/hrqcm\/skmqfhezali\/vebmjcq\/shgz\/3c60\/afxz", 374 | "wildcard_path": "\/hrqcm\/skmqfhezali\/vebmjcq\/shgz\/**\/afxz", 375 | "result": "3c60" 376 | }, 377 | { 378 | "path": "\/dvsnqmrfulgj\/1396\/pm\/xyezvothakur", 379 | "wildcard_path": "\/dvsnqmrfulgj\/**\/pm\/xyezvothakur", 380 | "result": "1396" 381 | }, 382 | { 383 | "path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/840a\/qvntezcr", 384 | "wildcard_path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/**\/qvntezcr", 385 | "result": "840a" 386 | }, 387 | { 388 | "path": "\/av\/efucajvlpn\/zwfhbpgrdxto\/tqgvisfwozhr\/154f", 389 | "wildcard_path": "\/av\/efucajvlpn\/zwfhbpgrdxto\/tqgvisfwozhr\/**", 390 | "result": "154f" 391 | }, 392 | { 393 | "path": "\/fmobpjkadh\/ca29", 394 | "wildcard_path": "\/fmobpjkadh\/**", 395 | "result": "ca29" 396 | }, 397 | { 398 | "path": "\/i\/cvroyh\/p\/ym\/856b", 399 | "wildcard_path": "\/i\/cvroyh\/p\/ym\/**", 400 | "result": "856b" 401 | }, 402 | { 403 | "path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/otsk\/24fb", 404 | "wildcard_path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/otsk\/**", 405 | "result": "24fb" 406 | }, 407 | { 408 | "path": "\/drpnhomjs\/lqgresnihojd\/pxdk\/a55f\/kptwm", 409 | "wildcard_path": "\/drpnhomjs\/lqgresnihojd\/pxdk\/**\/kptwm", 410 | "result": "a55f" 411 | }, 412 | { 413 | "path": "\/nmfq\/29ca\/qcm", 414 | "wildcard_path": "\/nmfq\/**\/qcm", 415 | "result": "29ca" 416 | }, 417 | { 418 | "path": "\/sarij\/zobd\/d60e", 419 | "wildcard_path": "\/sarij\/zobd\/**", 420 | "result": "d60e" 421 | }, 422 | { 423 | "path": "\/av\/mtdv\/avw\/osvculfewp\/a05d\/ucqwhirxdga", 424 | "wildcard_path": "\/av\/mtdv\/avw\/osvculfewp\/**\/ucqwhirxdga", 425 | "result": "a05d" 426 | }, 427 | { 428 | "path": "\/imentqdg\/ndfglucop\/yv\/9298", 429 | "wildcard_path": "\/imentqdg\/ndfglucop\/yv\/**", 430 | "result": "9298" 431 | }, 432 | { 433 | "path": "\/ndi\/vgis\/p\/0cc9", 434 | "wildcard_path": "\/ndi\/vgis\/p\/**", 435 | "result": "0cc9" 436 | }, 437 | { 438 | "path": "\/tdfljxwcykm\/tvjzlfqgyki\/f\/d3fa", 439 | "wildcard_path": "\/tdfljxwcykm\/tvjzlfqgyki\/f\/**", 440 | "result": "d3fa" 441 | }, 442 | { 443 | "path": "\/gp\/xjcdabthym\/ilmoxrgsput\/3f24\/pticdjeksl", 444 | "wildcard_path": "\/gp\/xjcdabthym\/ilmoxrgsput\/**\/pticdjeksl", 445 | "result": "3f24" 446 | }, 447 | { 448 | "path": "\/aevx\/aswtlbfhxpnm\/xhmynro\/gezw\/lyorz\/ed6a", 449 | "wildcard_path": "\/aevx\/aswtlbfhxpnm\/xhmynro\/gezw\/lyorz\/**", 450 | "result": "ed6a" 451 | }, 452 | { 453 | "path": "\/drpnhomjs\/lqgresnihojd\/dacc", 454 | "wildcard_path": "\/drpnhomjs\/lqgresnihojd\/**", 455 | "result": "dacc" 456 | }, 457 | { 458 | "path": "\/mhcjdrwg\/tpkdzybfleug\/mehoqfdgaj\/igehjcok\/ckq\/a65d", 459 | "wildcard_path": "\/mhcjdrwg\/tpkdzybfleug\/mehoqfdgaj\/igehjcok\/ckq\/**", 460 | "result": "a65d" 461 | }, 462 | { 463 | "path": "\/rjq\/vwlm\/8900\/bdcnawqzt\/ds", 464 | "wildcard_path": "\/rjq\/vwlm\/**\/bdcnawqzt\/ds", 465 | "result": "8900" 466 | }, 467 | { 468 | "path": "\/z\/ulef\/kdvhacxuboqs\/9fd3", 469 | "wildcard_path": "\/z\/ulef\/kdvhacxuboqs\/**", 470 | "result": "9fd3" 471 | }, 472 | { 473 | "path": "\/pwcfjz\/oijnr\/f347", 474 | "wildcard_path": "\/pwcfjz\/oijnr\/**", 475 | "result": "f347" 476 | }, 477 | { 478 | "path": "\/rjq\/db4b\/nfi\/bmwy\/xiost", 479 | "wildcard_path": "\/rjq\/**\/nfi\/bmwy\/xiost", 480 | "result": "db4b" 481 | }, 482 | { 483 | "path": "\/z\/ulef\/kdvhacxuboqs\/2a92\/i\/bhmpcyvw", 484 | "wildcard_path": "\/z\/ulef\/kdvhacxuboqs\/**\/i\/bhmpcyvw", 485 | "result": "2a92" 486 | }, 487 | { 488 | "path": "\/fmobpjkadh\/hgujeqtnc\/baeoljrxnv\/ht\/498c", 489 | "wildcard_path": "\/fmobpjkadh\/hgujeqtnc\/baeoljrxnv\/ht\/**", 490 | "result": "498c" 491 | }, 492 | { 493 | "path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/d56e\/saxhonmub", 494 | "wildcard_path": "\/uxv\/uakshdrimtc\/jitoqandpurv\/fsmge\/**\/saxhonmub", 495 | "result": "d56e" 496 | }, 497 | { 498 | "path": "\/sarij\/zobd\/bghfujpndtwe\/stwhgznl\/9914", 499 | "wildcard_path": "\/sarij\/zobd\/bghfujpndtwe\/stwhgznl\/**", 500 | "result": "9914" 501 | } 502 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Router Benchmark Tests 2 | 3 | This benchmark suite evaluates the performance of various PHP router packages by testing their initialization, route 4 | registration, and dispatching efficiency under different conditions. The tests simulate real-world scenarios, 5 | measuring execution time and memory consumption. 6 | 7 | ## How the Benchmark Works 8 | 9 | 1. **Test Execution** 10 | - Each router is tested across multiple predefined scenarios. 11 | - Tests include both static and dynamic routes, with and without wildcards. 12 | 13 | 2. **Performance Measurement** 14 | - Each test is executed **20 times per router** to ensure consistent results. 15 | - The **median execution time** is used to reduce the impact of outliers. 16 | - Peak memory usage is recorded during execution. 17 | 18 | 3. **Results and Ranking** 19 | - The fastest router is ranked **#1** based on median execution time. 20 | - A percentage comparison shows how each router performs relative to the fastest one. 21 | - Memory usage is also measured and ranked. 22 | 23 | ## Test Structure 24 | 25 | Each test follows a structured process: 26 | 27 | - **Router Initialization** – The router is initialized before each test. 28 | - **Route Registration** – A predefined set of routes is registered. 29 | - **Request Dispatching** – The router processes test requests and returns responses. 30 | - **Result Validation** – The response is compared to the expected output to ensure correctness. 31 | 32 | ## Output Format 33 | 34 | The benchmark results are presented in a table with the following columns: 35 | 36 | | Rank | Router | Time (ms) | Time (%) | Peak Memory (MB) | Memory (%) | 37 | |------|--------|----------|---------|-----------------|-----------| 38 | | 1 | FastRouter | 10.2 | 100% | 1.2 | 100% | 39 | | 2 | AnotherRouter | 12.5 | 122% | 1.4 | 116% | 40 | 41 | - **Time (%)** compares execution time to the fastest router. 42 | - **Memory (%)** compares peak memory usage. 43 | 44 | ## Why This Matters 45 | 46 | Efficient routing is essential for high-performance web applications. By running standardized tests across different 47 | routers and using the median execution time, this benchmark provides a clear and objective comparison of their efficiency. 48 | 49 | ## Ease of Use & Implementation 50 | 51 | While these benchmarks highlight the performance of each tested router, another crucial factor is **how easy they are 52 | to use in a project**. If you check the **`src/Routers`** directory, you’ll find the adapter files that show how each 53 | router is set up. The complexity of implementation varies significantly—some routers require minimal setup, while 54 | others need more configuration to get started. Additionally, some routers include extra features that are **not 55 | covered in this benchmark** but may be valuable depending on your needs. Be sure to explore these details when 56 | choosing a router! 57 | ## Packages 58 | 59 | | Name | Package | Version | 60 | |-----------|-------------|-----------| 61 | | Laravel | [illuminate/routing](https://github.com/illuminate/routing) | v11.41.3 | 62 | | Rammewerk | [rammewerk/router](https://github.com/rammewerk/router) | 0.9.82 | 63 | | Bramus | [bramus/router](https://github.com/bramus/router) | 1.6.1 | 64 | | AltoRouter | [altorouter/altorouter](https://github.com/dannyvankooten/AltoRouter) | 2.0.3 | 65 | | Symfony | [symfony/routing](https://symfony.com/doc/current/routing.html) | v7.2.3 | 66 | | Klein | [klein/klein](https://github.com/klein/klein) | v2.1.2 | 67 | | FastRoute | [nikic/fast-route](https://github.com/nikic/FastRoute) | v1.3.0 | 68 | | PHRoute | [phroute/phroute](https://github.com/mrjgreen/phroute) | v2.2.0 | 69 | | Nette | [nette/routing](https://github.com/nette/routing) | v3.1.1 | 70 | | Sharkk | [sharkk/router](https://git.sharkk.net/PHP/Router) | dev-master | 71 | | Jaunt | [davenusbaum/jaunt](https://github.com/davenusbaum/jaunt) | v0.0.1 | 72 | 73 | 74 | ## Benchmark Results 75 | 76 | These tests was run **2025-02-05 12:36:59** on PHP version: **8.4.3** 77 | 78 | 79 | 80 | ### Router Initialization Performance Test 81 | 82 | This test measures **how quickly the router initializes** when called **1000 times**. It helps determine the overhead of 83 | setting up the router repeatedly. A slower result here could indicate an expensive initialization process. 84 | 85 | `Test Suite #1:` 86 | 87 | | Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) | 88 | | --- | ------------- | ------ | ------- | ------ | ------ | 89 | | 1 | **Sharkk** | 0.026 | 100% | 0.347 | 100% | 90 | | 2 | **Jaunt** | 0.039 | 150% | 0.347 | 100% | 91 | | 3 | **PHRoute** | 0.07 | 269% | 0.347 | 100% | 92 | | 4 | **Bramus** | 0.091 | 350% | 0.347 | 100% | 93 | | 5 | **AltoRouter** | 0.096 | 369% | 0.346 | 100% | 94 | | 6 | **Rammewerk Router** | 0.111 | 427% | 0.347 | 100% | 95 | | 7 | **FastRoute** | 0.163 | 627% | 0.354 | 102% | 96 | | 8 | **Symfony Router** | 0.298 | 1146% | 0.347 | 100% | 97 | | 9 | **Nette** | 4.053 | 15588% | 0.357 | 103% | 98 | | 10 | **Klein** | 5.422 | 20854% | 0.356 | 103% | 99 | | 11 | **Laravel** | 7.016 | 26985% | 3.579 | 1032% | 100 | 101 | 102 | ### Router Initialization and Route Registration Performance Test 103 | 104 | This test measures **how efficiently the router initializes and registers routes**. It generates **50 static routes** with up to **5 segments** each 105 | and registers them as closure-based routes. The benchmark runs **50 iterations**, where the router is initialized and routes are registered in each iteration. 106 | The total time reflects how fast the router can complete this process **50 times**. 107 | 108 | `Test Suite #2:` 109 | 110 | | Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) | 111 | | --- | ------------- | ------ | ------- | ------ | ------ | 112 | | 1 | **AltoRouter** | 0.139 | 100% | 0.41 | 100% | 113 | | 2 | **Bramus** | 0.329 | 237% | 0.424 | 104% | 114 | | 3 | **FastRoute** | 0.913 | 657% | 0.409 | 100% | 115 | | 4 | **Sharkk** | 1.05 | 755% | 0.445 | 109% | 116 | | 5 | **PHRoute** | 1.149 | 827% | 0.439 | 107% | 117 | | 6 | **Jaunt** | 1.216 | 875% | 0.467 | 114% | 118 | | 7 | **Symfony Router** | 1.279 | 920% | 0.463 | 113% | 119 | | 8 | **Rammewerk Router** | 1.437 | 1034% | 0.446 | 109% | 120 | | 9 | **Klein** | 1.644 | 1183% | 0.44 | 108% | 121 | | 10 | **Nette** | 2.12 | 1525% | 0.477 | 117% | 122 | | 11 | **Laravel** | 3.928 | 2826% | 3.243 | 792% | 123 | 124 | 125 | ### Router Dispatch Performance Test (Static Routes) 126 | 127 | This test measures how efficiently the router initializes, registers, and dispatches routes. It generates **100 static routes** 128 | with up to **5 segments** each and registers them as closure-based routes. However, **only a single predefined route is 129 | dispatched**, and this same route is used for all routers to ensure consistent results. The benchmark reflects the time 130 | taken for the **entire process**, including initializing the router, registering all routes, and dispatching 131 | **one specific route**. 132 | 133 | `Test Suite #3:` 134 | 135 | | Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) | 136 | | --- | ------------- | ------ | ------- | ------ | ------ | 137 | | 1 | **AltoRouter** | 0.016 | 100% | 0.472 | 100% | 138 | | 2 | **Bramus** | 0.032 | 200% | 0.497 | 105% | 139 | | 3 | **FastRoute** | 0.054 | 338% | 0.465 | 99% | 140 | | 4 | **Sharkk** | 0.056 | 350% | 0.553 | 117% | 141 | | 5 | **Jaunt** | 0.066 | 413% | 0.595 | 126% | 142 | | 6 | **PHRoute** | 0.073 | 456% | 0.527 | 112% | 143 | | 7 | **Symfony Router** | 0.091 | 569% | 0.535 | 113% | 144 | | 8 | **Rammewerk Router** | 0.094 | 588% | 0.542 | 115% | 145 | | 9 | **Nette** | 0.143 | 894% | 0.599 | 127% | 146 | | 10 | **Klein** | 0.178 | 1113% | 0.513 | 108% | 147 | | 11 | **Laravel** | 0.415 | 2594% | 0.591 | 125% | 148 | 149 | 150 | ### Router Dispatch Performance Test (Dynamic Routes) 151 | 152 | This test is similar to **test suite 3**, but it features a path with **one dynamic segments**. The test registers multiple routes, 153 | but only dispatches **a single dynamic route** across all routers to ensure consistency. This setup helps gauge how well 154 | routers handle dynamic parameters during dispatch. 155 | 156 | `Test Suite #9:` 157 | 158 | | Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) | 159 | | --- | ------------- | ------ | ------- | ------ | ------ | 160 | | 1 | **Bramus** | 0.044 | 100% | 0.499 | 100% | 161 | | 2 | **AltoRouter** | 0.06 | 136% | 0.4 | 80% | 162 | | 3 | **Sharkk** | 0.074 | 168% | 0.51 | 102% | 163 | | 4 | **Jaunt** | 0.089 | 202% | 0.554 | 111% | 164 | | 5 | **Rammewerk Router** | 0.095 | 216% | 0.49 | 98% | 165 | | 6 | **FastRoute** | 0.138 | 314% | 0.487 | 98% | 166 | | 7 | **Klein** | 0.182 | 414% | 0.478 | 96% | 167 | | 8 | **Symfony Router** | 0.204 | 464% | 0.525 | 105% | 168 | | 9 | **Nette** | 0.269 | 611% | 0.615 | 123% | 169 | | 10 | **PHRoute** | 0.29 | 659% | 0.547 | 110% | 170 | | 11 | **Laravel** | 0.607 | 1380% | 0.577 | 116% | 171 | 172 | 173 | ### Router Dispatch Performance Test (Dynamic Routes) 174 | 175 | This test measures **how efficiently the router initializes, registers, and dispatches dynamic routes**. It generates **50 routes** 176 | with up to **4 segments**, including **one dynamic/wildcard segment**. After registration, each route is dispatched and its response 177 | is validated. The benchmark reflects the total time taken for the **entire process**, from initialization to handling dynamic routes. 178 | 179 | `Test Suite #4:` 180 | 181 | | Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) | 182 | | --- | ------------- | ------ | ------- | ------ | ------ | 183 | | 1 | **Sharkk** | 0.054 | 100% | 0.419 | 100% | 184 | | 2 | **Jaunt** | 0.075 | 139% | 0.442 | 105% | 185 | | 3 | **Rammewerk Router** | 0.083 | 154% | 0.414 | 99% | 186 | | 4 | **Bramus** | 0.283 | 524% | 0.425 | 101% | 187 | | 5 | **Symfony Router** | 0.373 | 691% | 0.468 | 111% | 188 | | 6 | **FastRoute** | 0.433 | 802% | 0.436 | 104% | 189 | | 7 | **PHRoute** | 0.629 | 1165% | 0.448 | 107% | 190 | | 8 | **AltoRouter** | 0.796 | 1474% | 0.374 | 89% | 191 | | 9 | **Nette** | 0.877 | 1624% | 0.485 | 116% | 192 | | 10 | **Klein** | 1.785 | 3306% | 0.443 | 105% | 193 | | 11 | **Laravel** | 2.607 | 4828% | 0.529 | 126% | 194 | 195 | 196 | ### Router Repeated Dispatch Performance Test (Dynamic Routes) 197 | 198 | This test measures **how efficiently the router handles repeated dispatches of dynamic routes**. It generates **100 routes** 199 | with up to **6 segments**, including **2 dynamic/wildcard segments**. After initialization and registration, **each route** is dispatched 200 | **50 times** to simulate repeated access patterns. The benchmark reflects the total time taken for the **entire process**, 201 | focusing on the cost of repeated dynamic route handling. 202 | 203 | `Test Suite #5:` 204 | 205 | | Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) | 206 | | --- | ------------- | ------ | ------- | ------ | ------ | 207 | | 1 | **Sharkk** | 2.194 | 100% | 0.56 | 100% | 208 | | 2 | **Jaunt** | 4.205 | 192% | 0.608 | 108% | 209 | | 3 | **Rammewerk Router** | 4.359 | 199% | 0.521 | 93% | 210 | | 4 | **Symfony Router** | 27.259 | 1242% | 0.65 | 116% | 211 | | 5 | **Bramus** | 45.241 | 2062% | 0.503 | 90% | 212 | | 6 | **FastRoute** | 61.55 | 2805% | 0.53 | 95% | 213 | | 7 | **PHRoute** | 86.215 | 3930% | 0.556 | 99% | 214 | | 8 | **Nette** | 115.708 | 5274% | 0.667 | 119% | 215 | | 9 | **AltoRouter** | 207.779 | 9470% | 0.403 | 72% | 216 | | 10 | **Laravel** | 217.4 | 9909% | 0.781 | 139% | 217 | | 11 | **Klein** | 815.596 | 37174% | 2.384 | 425% | 218 | 219 | 220 | ### Router High-Load Dispatch Performance Test (Dynamic Routes) 221 | 222 | This test measures **how efficiently the router handles a high number of dispatches for dynamic routes**. It generates **100 routes** 223 | with up to **6 segments**, including **2 dynamic/wildcard segments**. After initialization and registration, each route is dispatched 224 | **200 times** to simulate extreme load conditions. The benchmark reflects the router’s ability to handle repeated requests efficiently 225 | under heavy usage. 226 | 227 | `Test Suite #6:` 228 | 229 | | Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) | 230 | | --- | ------------- | ------ | ------- | ------ | ------ | 231 | | 1 | **Sharkk** | 9.153 | 100% | 0.572 | 100% | 232 | | 2 | **Rammewerk Router** | 17.681 | 193% | 0.538 | 94% | 233 | | 3 | **Jaunt** | 18.973 | 207% | 0.619 | 108% | 234 | | 4 | **Symfony Router** | 96.964 | 1059% | 0.654 | 114% | 235 | | 5 | **Bramus** | 184.247 | 2013% | 0.505 | 88% | 236 | | 6 | **FastRoute** | 292.94 | 3200% | 0.533 | 93% | 237 | | 7 | **PHRoute** | 376.227 | 4110% | 0.557 | 97% | 238 | | 8 | **Nette** | 570.937 | 6238% | 0.669 | 117% | 239 | | 9 | **AltoRouter** | 857.782 | 9372% | 0.404 | 71% | 240 | | 10 | **Laravel** | 871.901 | 9526% | 0.786 | 138% | 241 | | 11 | **Klein** | N/A | N/A | N/A | N/A | 242 | 243 | 244 | ### Router Large-Scale Route Handling Performance Test 245 | 246 | This test measures **how efficiently the router handles a large number of registered routes**. It generates **1,500 routes** 247 | with up to **6 segments**, including **2 dynamic/wildcard segments**. After initialization and registration, each route is dispatched 248 | once to validate its response. The benchmark reflects the router’s performance in handling a high volume of routes efficiently. 249 | 250 | `Test Suite #7:` 251 | 252 | | Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) | 253 | | --- | ------------- | ------ | ------- | ------ | ------ | 254 | | 1 | **Sharkk** | 1.977 | 100% | 3.675 | 100% | 255 | | 2 | **Rammewerk Router** | 2.675 | 135% | 3.075 | 84% | 256 | | 3 | **Jaunt** | 2.823 | 143% | 4.372 | 119% | 257 | | 4 | **Symfony Router** | 131.148 | 6634% | 4.772 | 130% | 258 | | 5 | **Bramus** | 234.448 | 11859% | 2.644 | 72% | 259 | | 6 | **FastRoute** | 327.376 | 16559% | 2.99 | 81% | 260 | | 7 | **PHRoute** | 422.9 | 21391% | 3.367 | 92% | 261 | | 8 | **Laravel** | 501.73 | 25378% | 6.48 | 176% | 262 | | 9 | **Nette** | 629.895 | 31861% | 5.033 | 137% | 263 | | 10 | **AltoRouter** | 1081.887 | 54724% | 1.177 | 32% | 264 | | 11 | **Klein** | 1156.678 | 58507% | 2.791 | 76% | 265 | 266 | 267 | ### Router Class-Based Dispatch Performance Test (Dynamic Routes) 268 | 269 | This test measures **how efficiently the router handles class-based route dispatching**. It generates **100 routes** with up to **5 segments**, 270 | including **2 dynamic/wildcard segments**, and maps them to methods within a predefined class. After initialization and registration, 271 | each route is dispatched **200 times** to simulate repeated requests. The benchmark reflects the router’s performance in handling 272 | class-based route resolution under load. 273 | 274 | `Test Suite #8:` 275 | 276 | | Rank | Container | Time (ms) | Time (%) | Peak Memory (MB) | Peak Memory (%) | 277 | | --- | ------------- | ------ | ------- | ------ | ------ | 278 | | 1 | **Sharkk** | 9.105 | 100% | 0.586 | 100% | 279 | | 2 | **Jaunt** | 20.471 | 225% | 0.63 | 107% | 280 | | 3 | **Rammewerk Router** | 25.671 | 282% | 0.896 | 153% | 281 | | 4 | **Symfony Router** | 123.89 | 1361% | 0.674 | 115% | 282 | | 5 | **Bramus** | 209.907 | 2305% | 0.438 | 75% | 283 | | 6 | **FastRoute** | 291.371 | 3200% | 0.552 | 94% | 284 | | 7 | **PHRoute** | 378.437 | 4156% | 0.577 | 99% | 285 | | 8 | **Nette** | 566.517 | 6222% | 0.703 | 120% | 286 | | 9 | **Laravel** | 811.709 | 8915% | 0.8 | 137% | 287 | | 10 | **AltoRouter** | 905.135 | 9941% | 0.424 | 72% | 288 | | 11 | **Klein** | N/A | N/A | N/A | N/A | 289 | ## How to Run Benchmarks 290 | 291 | ### 1. Prerequisites 292 | Make sure you have the following installed: 293 | - **Docker** 294 | - **Docker Compose** 295 | 296 | ### 2. Clone the Repository 297 | Clone or download this repository to your local machine: 298 | ```bash 299 | git clone https://github.com/follestad/php-router-benchmark.git 300 | cd php-router-benchmark 301 | ``` 302 | 303 | ### 3. Start the Benchmark Environment 304 | Use **Docker Compose** to start the PHP-FPM container: 305 | ```bash 306 | docker compose up -d 307 | ``` 308 | This will set up the environment needed for testing. You can check public/index.php for details on how the benchmark 309 | runs or visit http://localhost for additional instructions. 310 | 311 | ### 4. Install or Update Dependencies 312 | Before running the benchmark, install or update dependencies: 313 | ```bash 314 | sh benchmark.sh composer install # For first-time setup 315 | sh benchmark.sh composer update # To update dependencies 316 | sh benchmark.sh composer require .../... # To add new packages 317 | ``` 318 | ### 5. Run the Benchmark 319 | Execute the following command to run the benchmark: 320 | ```bash 321 | sh benchmark.sh run 322 | ``` 323 | ### 6. Viewing Results 324 | After running the benchmark, results will be **saved to /result/README.md**. 325 | If the benchmark completes successfully, you can copy this file to replace the main README. 326 | This will update the project documentation with the latest benchmark results. 327 | 328 | ## Understanding Router Implementations 329 | If you’re curious about how different routers are implemented, check out the `src/Routers` directory. 330 | Some routers require minimal setup, while others need more extensive configuration. For example: 331 | - **Rammewerk** Router has a compact and simple setup. 332 | - **Symfony and Laravel** require more extensive configuration. 333 | - **Bramus** Router needed additional adjustments to properly validate results, see `src/TestCode/...` files for examples. 334 | 335 | Exploring these implementations can give you insight into the trade-offs between simplicity and flexibility in different routing solutions 336 | 337 | ## Contributing & Disclaimer 338 | Want to contribute? Feel free to fork the repository, add new router packages, or improve existing implementations 339 | by submitting a pull request! 340 | 341 | You can find each package’s implementation under the `src/Routers` directory. Keep in mind that the test setups are 342 | based on official documentation and guides, but they may not always represent the absolute best or most optimized 343 | configuration for every router. Some routers required small adjustments to fit the test structure, but these should 344 | have minimal impact on performance. 345 | 346 | Additionally, some routers offer caching or compilation features to improve speed, but these haven’t been tested 347 | yet—hopefully, a future test will cover this! 348 | 349 | 350 | ## Credits 351 | - [Kristoffer Follestad](https://github.com/follestad) 352 | - [Máté Kocsis](https://github.com/kocsismate) 353 | 354 | A huge thanks to [Máté Kocsis](https://github.com/kocsismate) for the inspiration behind this project. Many parts of 355 | the implementation are based on his excellent work in [php-di-container-benchmarks](https://github.com/kocsismate/php-di-container-benchmarks). 356 | --------------------------------------------------------------------------------