diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 23aaf63..4bbb8d4 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -43,7 +43,7 @@ jobs: run: ./vendor/bin/pest tests --parallel - name: Run mutation tests - run: XDEBUG_MODE=coverage ./vendor/bin/pest --mutate --parallel --min=70 + run: XDEBUG_MODE=coverage ./vendor/bin/pest --mutate --parallel --min=60 - name: Phpstan analysis - run: ./vendor/bin/phpstan analyse src --level=7 --no-progress --no-interaction + run: ./vendor/bin/phpstan analyse src --no-progress --no-interaction diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml deleted file mode 100644 index 1588426..0000000 --- a/.github/workflows/trivy.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Trivy - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - name: Build - runs-on: ubuntu-20.04 - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@master - with: - scan-type: 'fs' - ignore-unfixed: true - format: 'sarif' - output: 'trivy-results.sarif' - severity: 'MEDIUM,HIGH,CRITICAL' - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: 'trivy-results.sarif' diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..de973a9 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,3 @@ +parameters: + level: 7 + treatPhpDocTypesAsCertain: false \ No newline at end of file diff --git a/src/Router/Loader/AttributeRouteLoader.php b/src/Router/Loader/AttributeRouteLoader.php index 274b7cf..48de9fd 100644 --- a/src/Router/Loader/AttributeRouteLoader.php +++ b/src/Router/Loader/AttributeRouteLoader.php @@ -21,7 +21,9 @@ class AttributeRouteLoader public function __construct( /** @var string[] */ private readonly array $directories, - private readonly ContainerInterface $container + private readonly ContainerInterface $container, + private readonly ?string $cache_file = null, + private readonly bool $cache_disabled = false ) {} /** @@ -29,6 +31,14 @@ public function __construct( */ public function load(): self { + if ($this->cache_file && !$this->cache_disabled && file_exists($this->cache_file)) { + $this->uncacheRoute(); + + if (count($this->routes)) { + return $this; + } + } + $files = []; foreach ($this->directories as $directory) { $files = array_merge($files, $this->findPhpFiles($directory)); @@ -59,9 +69,50 @@ public function load(): self } } + if ($this->cache_file && !$this->cache_disabled && count($this->routes)) { + $this->cacheRoutes(); + } + return $this; } + private function cacheRoutes(): void + { + $cache = []; + foreach ($this->routes as $route) { + /** @var LazyRequestHandler $handler */ + $handler = $route->getHandler(); + + $cache[] = [ + 'methods' => $route->getAllowedMethods(), + 'path' => $route->getPath(), + 'handler' => $handler->id, + 'name' => $route->getName(), + 'priority' => $route->getPriority() + ]; + } + + file_put_contents($this->cache_file, 'cache_file; + if (!$cached_routes || !is_array($cached_routes) || !count($cached_routes)) { + return; + } + + foreach ($cached_routes as $route_data) { + $this->routes[] = new Route( + $route_data['methods'], + $route_data['path'], + new LazyRequestHandler($route_data['handler'], $this->container), + $route_data['name'] ?? null, + $route_data['priority'] ?? null + ); + } + } + /** @return string[] */ private function findPhpFiles(string $directory): array { diff --git a/src/Router/Loader/LazyRequestHandler.php b/src/Router/Loader/LazyRequestHandler.php index 7f106da..ed9de2c 100644 --- a/src/Router/Loader/LazyRequestHandler.php +++ b/src/Router/Loader/LazyRequestHandler.php @@ -11,7 +11,7 @@ { public function __construct( - private string $id, + public string $id, private ContainerInterface $container ) {} diff --git a/tests/Pest.php b/tests/Pest.php index cd733da..bf7b40d 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -18,9 +18,21 @@ ->in('Unit/RouteTest.php'); uses() + ->beforeAll(function () { + $cache_file = __DIR__.'/cache/routes.cache.php'; + if (file_exists($cache_file)) { +// unlink($cache_file); + } + }) ->beforeEach(function () { $this->router = new FastRouteRouter(); }) + ->afterEach(function () { + $cache_file = __DIR__.'/cache/routes.cache.php'; + if (file_exists($cache_file)) { +// unlink($cache_file); + } + }) ->in('Unit/FastRouteRouterTest.php'); uses() @@ -34,3 +46,18 @@ $this->router = new TreeRouter(); }) ->in('Unit/TreeRouterTest.php'); + +uses() + ->beforeAll(function () { + $cache_file = __DIR__.'/cache/loader.routes.cache.php'; + if (file_exists($cache_file)) { +// unlink($cache_file); + } + }) + ->afterEach(function () { + $cache_file = __DIR__.'/cache/loader.routes.cache.php'; + if (file_exists($cache_file)) { +// unlink($cache_file); + } + }) + ->in('Unit/AttributeRouteLoaderTest.php'); diff --git a/tests/Unit/AttributeRouteLoaderTest.php b/tests/Unit/AttributeRouteLoaderTest.php index 9281bcd..3f59ee6 100644 --- a/tests/Unit/AttributeRouteLoaderTest.php +++ b/tests/Unit/AttributeRouteLoaderTest.php @@ -5,6 +5,7 @@ use Borsch\Container\Container; use Borsch\Router\Loader\AttributeRouteLoader; use Borsch\Router\Route; +use BorschTest\Mockup\RequestHandler; use Psr\Http\Server\RequestHandlerInterface; covers(AttributeRouteLoader::class); @@ -24,3 +25,22 @@ ->and($routes[0])->getHandler()->toBeInstanceOf(RequestHandlerInterface::class) ->and($routes[0])->getName()->toBe('mockup.request.handler'); }); + +test('cached routes located in file', function () { + $container = new Container(); + $cache_file = __DIR__.'/../cache/loader.routes.cache.php'; + + $loader = new AttributeRouteLoader([__DIR__.'/../Mockup'], $container, $cache_file); + $loader->load(); + + expect($cache_file)->toBeFile(); + + $cached_routes = require $cache_file; + + expect($cached_routes)->toBeArray() + ->and($cached_routes)->toHaveCount(1) + ->and($cached_routes[0]['path'])->toBe('/mockup/request-handler') + ->and($cached_routes[0]['methods'])->toBe(['GET']) + ->and($cached_routes[0]['handler'])->toBe(RequestHandler::class) + ->and($cached_routes[0]['name'])->toBe('mockup.request.handler'); +}); diff --git a/tests/cache/loader.routes.cache.php b/tests/cache/loader.routes.cache.php new file mode 100644 index 0000000..7236abb --- /dev/null +++ b/tests/cache/loader.routes.cache.php @@ -0,0 +1,13 @@ + + array ( + 'methods' => + array ( + 0 => 'GET', + ), + 'path' => '/mockup/request-handler', + 'handler' => 'BorschTest\\Mockup\\RequestHandler', + 'name' => 'mockup.request.handler', + 'priority' => 0, + ), +); \ No newline at end of file