├── .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 |
7 |
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 |
4 |
5 |
70 |
71 |
72 |
73 |
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 |
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 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
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 |
123 |
124 |
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 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 | 1737615427076
182 |
183 |
184 | 1737615427076
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
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 |
--------------------------------------------------------------------------------