From 6cce0ba9f066e2fefb3d39cb7f34b53f978b6d94 Mon Sep 17 00:00:00 2001 From: Volodymyr Stelmakh Date: Fri, 27 Feb 2026 00:35:40 +0100 Subject: [PATCH 1/4] add override attributes --- src/Console/Command/GenerateCommand.php | 5 +++++ src/Console/Command/ServerCommand.php | 7 +++++++ src/Console/CommandLoader.php | 3 +++ src/EventDispatcher/RouterConfig/RouterConfigEvent.php | 1 + src/EventDispatcher/RouterReady/RouterReadyEvent.php | 1 + src/Generator/Distribution/FilesystemDistribution.php | 5 +++++ src/Generator/SiteGeneratorVisitor.php | 3 +++ src/Router/Compiler/CompiledRouteCollection.php | 1 + src/Router/Compiler/Resource/ControllerResource.php | 1 + src/Router/Compiler/Resource/FileResource.php | 1 + src/Router/Compiler/RouteCompilerVisitor.php | 3 +++ src/Router/Route/Asset.php | 1 + src/Router/Route/Group.php | 1 + src/Router/Route/Route.php | 1 + src/Router/Source/RouteSourceCollection.php | 1 + src/ServiceLocator/NoContainer.php | 2 ++ 16 files changed, 37 insertions(+) diff --git a/src/Console/Command/GenerateCommand.php b/src/Console/Command/GenerateCommand.php index 0b2a5ba..ef8cc0d 100644 --- a/src/Console/Command/GenerateCommand.php +++ b/src/Console/Command/GenerateCommand.php @@ -26,16 +26,19 @@ class GenerateCommand extends Command implements CommandFactoryInterface private const string DESCRIPTION = 'Generate static site from specified routes'; private const string OPTION_SYMLINK = 'symlink'; + #[\Override] public static function name(): string { return self::NAME; } + #[\Override] public static function description(): string { return self::DESCRIPTION; } + #[\Override] public static function create(Kernel $kernel): self { $container = $kernel->container(); @@ -66,6 +69,7 @@ public function __construct( parent::__construct(self::NAME); } + #[\Override] protected function configure(): void { $this @@ -75,6 +79,7 @@ protected function configure(): void ; } + #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { $symlink = $input->getOption(self::OPTION_SYMLINK); diff --git a/src/Console/Command/ServerCommand.php b/src/Console/Command/ServerCommand.php index b6b688e..ff4aab4 100644 --- a/src/Console/Command/ServerCommand.php +++ b/src/Console/Command/ServerCommand.php @@ -29,16 +29,19 @@ class ServerCommand extends Command implements CommandFactoryInterface private ?Server $server = null; + #[\Override] public static function name(): string { return self::NAME; } + #[\Override] public static function description(): string { return self::DESCRIPTION; } + #[\Override] public static function create(Kernel $kernel): self { $distribution = $kernel->distribution(); @@ -54,17 +57,20 @@ public function __construct( } /** @return array */ + #[\Override] public function getSubscribedSignals(): array { return [2, 15]; // SIGINT, SIGTERM } + #[\Override] public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false { $this->server?->stop(); return false; } + #[\Override] protected function configure(): void { $this @@ -75,6 +81,7 @@ protected function configure(): void ; } + #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { if (!$this->distribution instanceof LocalDistributionInterface) { diff --git a/src/Console/CommandLoader.php b/src/Console/CommandLoader.php index a2ade4c..7733925 100644 --- a/src/Console/CommandLoader.php +++ b/src/Console/CommandLoader.php @@ -29,6 +29,7 @@ public function __construct( } } + #[\Override] public function get(string $name): Command { $factory = $this->map[$name] ?? null; @@ -39,11 +40,13 @@ public function get(string $name): Command return $this->createLazyCommand($factory); } + #[\Override] public function has(string $name): bool { return isset($this->map[$name]); } + #[\Override] public function getNames(): array { return array_keys($this->map); diff --git a/src/EventDispatcher/RouterConfig/RouterConfigEvent.php b/src/EventDispatcher/RouterConfig/RouterConfigEvent.php index 276c7e6..1ae8b22 100644 --- a/src/EventDispatcher/RouterConfig/RouterConfigEvent.php +++ b/src/EventDispatcher/RouterConfig/RouterConfigEvent.php @@ -17,6 +17,7 @@ public function __construct( private readonly RouteSourceCollection $sources, ) {} + #[\Override] public function accept(ListenerInterface $listener): bool { if (!$listener instanceof RouterConfigListenerInterface) { diff --git a/src/EventDispatcher/RouterReady/RouterReadyEvent.php b/src/EventDispatcher/RouterReady/RouterReadyEvent.php index f6251c5..b99484c 100644 --- a/src/EventDispatcher/RouterReady/RouterReadyEvent.php +++ b/src/EventDispatcher/RouterReady/RouterReadyEvent.php @@ -20,6 +20,7 @@ public function __construct( private readonly iterable $routes, ) {} + #[\Override] public function accept(ListenerInterface $listener): bool { if (!$listener instanceof RouterReadyListenerInterface) { diff --git a/src/Generator/Distribution/FilesystemDistribution.php b/src/Generator/Distribution/FilesystemDistribution.php index c9f4fff..6b1c89c 100644 --- a/src/Generator/Distribution/FilesystemDistribution.php +++ b/src/Generator/Distribution/FilesystemDistribution.php @@ -23,11 +23,13 @@ public function __construct(string $distPath, Filesystem $filesystem = new Files $this->filesystem = $filesystem; } + #[\Override] public function path(): string { return $this->basePath; } + #[\Override] public function clear(): void { try { @@ -48,6 +50,7 @@ public function clear(): void } } + #[\Override] public function write(string $path, $content): void { $fullPath = $this->getFullPath($path); @@ -61,6 +64,7 @@ public function write(string $path, $content): void } } + #[\Override] public function copy(string $sourcePath, string $destinationPath): void { if (!file_exists($sourcePath)) { @@ -85,6 +89,7 @@ public function copy(string $sourcePath, string $destinationPath): void } } + #[\Override] public function link(string $sourcePath, string $destinationPath): void { if (!file_exists($sourcePath)) { diff --git a/src/Generator/SiteGeneratorVisitor.php b/src/Generator/SiteGeneratorVisitor.php index f2d2029..ed5340a 100644 --- a/src/Generator/SiteGeneratorVisitor.php +++ b/src/Generator/SiteGeneratorVisitor.php @@ -32,6 +32,7 @@ public function __construct( } } + #[\Override] public function visitController(ControllerResource $resource): void { $controller = $this->getController($resource->reference); @@ -40,6 +41,7 @@ public function visitController(ControllerResource $resource): void $this->distribution->write($path, $content); } + #[\Override] public function visitFile(FileResource $resource): void { if ($this->symlinkFiles) { @@ -64,6 +66,7 @@ private function getController(ControllerInterface|string|\Closure $reference): return new class ($reference) implements ControllerInterface { public function __construct(private \Closure $closure) {} + #[\Override] public function render(Router $router, array $parameters) { return ($this->closure)($router, $parameters); diff --git a/src/Router/Compiler/CompiledRouteCollection.php b/src/Router/Compiler/CompiledRouteCollection.php index fc3da74..f8c62cb 100644 --- a/src/Router/Compiler/CompiledRouteCollection.php +++ b/src/Router/Compiler/CompiledRouteCollection.php @@ -43,6 +43,7 @@ public function add(CompiledRoute $route): self /** * @return \Generator */ + #[\Override] public function getIterator(): \Traversable { return $this->all(); diff --git a/src/Router/Compiler/Resource/ControllerResource.php b/src/Router/Compiler/Resource/ControllerResource.php index 3d97c37..b0d5cf5 100644 --- a/src/Router/Compiler/Resource/ControllerResource.php +++ b/src/Router/Compiler/Resource/ControllerResource.php @@ -14,6 +14,7 @@ public function __construct( public array $parameters = [], ) {} + #[\Override] public function accept(ResourceVisitorInterface $visitor): void { $visitor->visitController($this); diff --git a/src/Router/Compiler/Resource/FileResource.php b/src/Router/Compiler/Resource/FileResource.php index dd4ed56..4f5a067 100644 --- a/src/Router/Compiler/Resource/FileResource.php +++ b/src/Router/Compiler/Resource/FileResource.php @@ -10,6 +10,7 @@ public function __construct( public string $source, ) {} + #[\Override] public function accept(ResourceVisitorInterface $visitor): void { $visitor->visitFile($this); diff --git a/src/Router/Compiler/RouteCompilerVisitor.php b/src/Router/Compiler/RouteCompilerVisitor.php index 85acedd..80df13b 100644 --- a/src/Router/Compiler/RouteCompilerVisitor.php +++ b/src/Router/Compiler/RouteCompilerVisitor.php @@ -26,6 +26,7 @@ public function __construct( public readonly CompiledRouteCollection $routes = new CompiledRouteCollection(), ) {} + #[\Override] public function visitRoute(Route $route): void { $canonicalPath = $this->getCanonicalPath($route->path); @@ -38,6 +39,7 @@ public function visitRoute(Route $route): void $this->routes->add($compiledRoute); } + #[\Override] public function visitGroup(Group $group): void { $path = $this->getCanonicalPath($group->path); @@ -49,6 +51,7 @@ public function visitGroup(Group $group): void } } + #[\Override] public function visitAsset(Asset $asset): void { $canonicalPath = $this->getCanonicalPath($asset->path); diff --git a/src/Router/Route/Asset.php b/src/Router/Route/Asset.php index 0da54aa..2b8c483 100644 --- a/src/Router/Route/Asset.php +++ b/src/Router/Route/Asset.php @@ -19,6 +19,7 @@ public function __construct( public ?string $name = null, ) {} + #[\Override] public function accept(RouteVisitorInterface $visitor): void { $visitor->visitAsset($this); diff --git a/src/Router/Route/Group.php b/src/Router/Route/Group.php index 9a100f3..6349c25 100644 --- a/src/Router/Route/Group.php +++ b/src/Router/Route/Group.php @@ -17,6 +17,7 @@ public function __construct( public iterable|RouteProviderInterface|string $routes = [], ) {} + #[\Override] public function accept(RouteVisitorInterface $visitor): void { $visitor->visitGroup($this); diff --git a/src/Router/Route/Route.php b/src/Router/Route/Route.php index 7dcb97d..59b751c 100644 --- a/src/Router/Route/Route.php +++ b/src/Router/Route/Route.php @@ -24,6 +24,7 @@ public function __construct( public array $parameters = [], ) {} + #[\Override] public function accept(RouteVisitorInterface $visitor): void { $visitor->visitRoute($this); diff --git a/src/Router/Source/RouteSourceCollection.php b/src/Router/Source/RouteSourceCollection.php index 372e8ad..ca5dbdb 100644 --- a/src/Router/Source/RouteSourceCollection.php +++ b/src/Router/Source/RouteSourceCollection.php @@ -29,6 +29,7 @@ public function add(RouteSource $source): void /** * @return \Generator */ + #[\Override] public function getIterator(): \Generator { foreach ($this->sources as $source) { diff --git a/src/ServiceLocator/NoContainer.php b/src/ServiceLocator/NoContainer.php index f73e02d..090aad8 100644 --- a/src/ServiceLocator/NoContainer.php +++ b/src/ServiceLocator/NoContainer.php @@ -11,6 +11,7 @@ class NoContainer implements ContainerInterface { + #[\Override] public function get(string $id): never { throw new class ($id) extends LogicException implements NotFoundExceptionInterface { @@ -27,6 +28,7 @@ public function __construct(string $id) }; } + #[\Override] public function has(string $id): bool { return false; From d5d2196e4ce0909d32dbdf5902a93531797dbc72 Mon Sep 17 00:00:00 2001 From: Volodymyr Stelmakh Date: Sun, 22 Mar 2026 21:14:54 +0100 Subject: [PATCH 2/4] enable additional phpstan checks for stricter static analysis --- phpstan.neon | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/phpstan.neon b/phpstan.neon index f1c72f5..db07dd8 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,6 +6,11 @@ parameters: - tests reportUnmatchedIgnoredErrors: true + checkDynamicProperties: true + reportNonIntStringArrayKey: true + checkMissingOverrideMethodAttribute: true + checkStrictPrintfPlaceholderTypes: true + ignoreErrors: - identifier: missingType.iterableValue path: tests/*Test.php From a73e551f2ed2ca715fd627f5edeac5009cbe0252 Mon Sep 17 00:00:00 2001 From: Volodymyr Stelmakh Date: Sun, 22 Mar 2026 21:25:34 +0100 Subject: [PATCH 3/4] add override attributes to tests --- tests/Doubles/Console/StubACommand.php | 3 +++ tests/Doubles/Console/StubBCommand.php | 3 +++ tests/Doubles/EventDispatcher/AbstractMockEvent.php | 1 + tests/Doubles/EventDispatcher/MockEventA.php | 1 + tests/Doubles/EventDispatcher/MockEventB.php | 1 + tests/Doubles/Generator/MockDistribution.php | 5 +++++ tests/Doubles/ServiceLocator/FakeContainer.php | 2 ++ tests/Functional/Generate/GenerateTest.php | 1 + tests/Functional/Generate/source/CurrentTimeController.php | 1 + tests/Functional/Generate/source/config.php | 4 ++++ tests/Functional/Server/ServerTest.php | 2 ++ tests/Functional/Server/config.php | 4 ++++ tests/Unit/Config/fake_configs/abstract_config.php | 4 ++++ tests/Unit/Console/Command/GenerateCommandTest.php | 1 + tests/Unit/Console/Command/ServerCommandTest.php | 1 + tests/Unit/Console/CommandLoaderTest.php | 1 + tests/Unit/EventDispatcher/EventDispatcherTest.php | 1 + .../EventDispatcher/RouterConfig/RouterConfigEventTest.php | 5 +++++ .../EventDispatcher/RouterReady/RouterReadyEventTest.php | 5 +++++ tests/Unit/Extension/ExtensionLoaderTest.php | 1 + .../Generator/Distribution/FilesystemDistributionTest.php | 1 + tests/Unit/Generator/SiteGeneratorTest.php | 1 + tests/Unit/Generator/SiteGeneratorVisitorTest.php | 4 ++++ tests/Unit/Router/Compiler/RouteCompilerTest.php | 1 + tests/Unit/Router/Compiler/RouteCompilerVisitorTest.php | 3 +++ tests/Unit/Router/RouterTest.php | 1 + tests/Unit/ServiceLocator/ServiceLocatorTest.php | 1 + 27 files changed, 59 insertions(+) diff --git a/tests/Doubles/Console/StubACommand.php b/tests/Doubles/Console/StubACommand.php index b4a42bf..91cd172 100644 --- a/tests/Doubles/Console/StubACommand.php +++ b/tests/Doubles/Console/StubACommand.php @@ -12,16 +12,19 @@ class StubACommand extends Command implements CommandFactoryInterface { private const string NAME = 'test:a'; + #[\Override] public static function name(): string { return self::NAME; } + #[\Override] public static function description(): string { return 'Test command A'; } + #[\Override] public static function create(Kernel $kernel): Command { return new self(self::NAME); diff --git a/tests/Doubles/Console/StubBCommand.php b/tests/Doubles/Console/StubBCommand.php index 2b152d3..5604c9d 100644 --- a/tests/Doubles/Console/StubBCommand.php +++ b/tests/Doubles/Console/StubBCommand.php @@ -12,16 +12,19 @@ class StubBCommand extends Command implements CommandFactoryInterface { private const string NAME = 'test:b'; + #[\Override] public static function name(): string { return self::NAME; } + #[\Override] public static function description(): string { return 'Test command B'; } + #[\Override] public static function create(Kernel $kernel): Command { return new self(self::NAME); diff --git a/tests/Doubles/EventDispatcher/AbstractMockEvent.php b/tests/Doubles/EventDispatcher/AbstractMockEvent.php index 65957c3..3f68987 100644 --- a/tests/Doubles/EventDispatcher/AbstractMockEvent.php +++ b/tests/Doubles/EventDispatcher/AbstractMockEvent.php @@ -15,6 +15,7 @@ abstract class AbstractMockEvent implements EventInterface /** @return class-string */ abstract public function listenerClass(): string; + #[\Override] public function accept(ListenerInterface $listener): bool { $this->acceptedWith[] = $listener::class; diff --git a/tests/Doubles/EventDispatcher/MockEventA.php b/tests/Doubles/EventDispatcher/MockEventA.php index 0f80718..8251298 100644 --- a/tests/Doubles/EventDispatcher/MockEventA.php +++ b/tests/Doubles/EventDispatcher/MockEventA.php @@ -6,6 +6,7 @@ final class MockEventA extends AbstractMockEvent { + #[\Override] public function listenerClass(): string { return MockListenerA::class; diff --git a/tests/Doubles/EventDispatcher/MockEventB.php b/tests/Doubles/EventDispatcher/MockEventB.php index bf4868d..5315b5a 100644 --- a/tests/Doubles/EventDispatcher/MockEventB.php +++ b/tests/Doubles/EventDispatcher/MockEventB.php @@ -6,6 +6,7 @@ final class MockEventB extends AbstractMockEvent { + #[\Override] public function listenerClass(): string { return MockListenerB::class; diff --git a/tests/Doubles/Generator/MockDistribution.php b/tests/Doubles/Generator/MockDistribution.php index 4745328..753db2d 100644 --- a/tests/Doubles/Generator/MockDistribution.php +++ b/tests/Doubles/Generator/MockDistribution.php @@ -21,11 +21,13 @@ class MockDistribution implements DistributionInterface, LocalDistributionInterf /** @var array */ public private(set) array $links = []; + #[\Override] public function clear(): void { $this->cleared++; } + #[\Override] public function write(string $path, $content): void { $content = is_resource($content) ? stream_get_contents($content) : $content; @@ -33,16 +35,19 @@ public function write(string $path, $content): void $this->writes[] = ['path' => $path, 'content' => $content]; } + #[\Override] public function copy(string $sourcePath, string $destinationPath): void { $this->copies[] = ['source' => $sourcePath, 'dest' => $destinationPath]; } + #[\Override] public function path(): string { return '/tmp/stasis/test-distribution'; } + #[\Override] public function link(string $sourcePath, string $destinationPath): void { $this->links[] = ['source' => $sourcePath, 'dest' => $destinationPath]; diff --git a/tests/Doubles/ServiceLocator/FakeContainer.php b/tests/Doubles/ServiceLocator/FakeContainer.php index c53dcc5..0ea7e14 100644 --- a/tests/Doubles/ServiceLocator/FakeContainer.php +++ b/tests/Doubles/ServiceLocator/FakeContainer.php @@ -17,6 +17,7 @@ public function __construct( private array $services = [], ) {} + #[\Override] public function get(string $id): mixed { if (isset($this->services[$id])) { @@ -31,6 +32,7 @@ public function __construct(string $id) }; } + #[\Override] public function has(string $id): bool { return isset($this->services[$id]); diff --git a/tests/Functional/Generate/GenerateTest.php b/tests/Functional/Generate/GenerateTest.php index 1444f6e..da3a4ee 100644 --- a/tests/Functional/Generate/GenerateTest.php +++ b/tests/Functional/Generate/GenerateTest.php @@ -14,6 +14,7 @@ class GenerateTest extends TestCase private Filesystem $filesystem; private Process $generate; + #[\Override] public function setUp(): void { $this->filesystem = new Filesystem(); diff --git a/tests/Functional/Generate/source/CurrentTimeController.php b/tests/Functional/Generate/source/CurrentTimeController.php index 3c2d505..ecbb796 100644 --- a/tests/Functional/Generate/source/CurrentTimeController.php +++ b/tests/Functional/Generate/source/CurrentTimeController.php @@ -9,6 +9,7 @@ class CurrentTimeController implements ControllerInterface { + #[\Override] public function render(Router $router, array $parameters): string { $output = []; diff --git a/tests/Functional/Generate/source/config.php b/tests/Functional/Generate/source/config.php index 94af713..d7f9efd 100644 --- a/tests/Functional/Generate/source/config.php +++ b/tests/Functional/Generate/source/config.php @@ -13,6 +13,7 @@ use Stasis\Tests\Functional\Generate\source\CurrentTimeController; return new class implements ConfigInterface { + #[\Override] public function routes(): iterable { return [ @@ -28,6 +29,7 @@ public function routes(): iterable ]; } + #[\Override] public function container(): ContainerInterface { return new FakeContainer([ @@ -35,11 +37,13 @@ public function container(): ContainerInterface ]); } + #[\Override] public function distribution(): DistributionInterface { return new FilesystemDistribution(__DIR__ . '/../dist'); } + #[\Override] public function extensions(): iterable { return []; diff --git a/tests/Functional/Server/ServerTest.php b/tests/Functional/Server/ServerTest.php index ce411ce..5e25671 100644 --- a/tests/Functional/Server/ServerTest.php +++ b/tests/Functional/Server/ServerTest.php @@ -12,6 +12,7 @@ class ServerTest extends TestCase { private Process $server; + #[\Override] public function setUp(): void { $this->server = StasisProcessFactory::create([ @@ -25,6 +26,7 @@ public function setUp(): void usleep(200_000); // wait for the server to start } + #[\Override] public function tearDown(): void { $this->server->stop(); diff --git a/tests/Functional/Server/config.php b/tests/Functional/Server/config.php index c4cb32f..87c7ce6 100644 --- a/tests/Functional/Server/config.php +++ b/tests/Functional/Server/config.php @@ -9,21 +9,25 @@ use Stasis\ServiceLocator\NoContainer; return new class implements ConfigInterface { + #[\Override] public function routes(): iterable { return []; } + #[\Override] public function container(): ContainerInterface { return new NoContainer(); } + #[\Override] public function distribution(): DistributionInterface { return new FilesystemDistribution(__DIR__ . '/fake_dist'); } + #[\Override] public function extensions(): iterable { return []; diff --git a/tests/Unit/Config/fake_configs/abstract_config.php b/tests/Unit/Config/fake_configs/abstract_config.php index f34ed77..559a690 100644 --- a/tests/Unit/Config/fake_configs/abstract_config.php +++ b/tests/Unit/Config/fake_configs/abstract_config.php @@ -9,21 +9,25 @@ use Stasis\Tests\Doubles\Generator\MockDistribution; return new class implements ConfigInterface { + #[\Override] public function routes(): iterable { return []; } + #[\Override] public function container(): ContainerInterface { return new NoContainer(); } + #[\Override] public function distribution(): DistributionInterface { return new MockDistribution(); } + #[\Override] public function extensions(): iterable { return []; diff --git a/tests/Unit/Console/Command/GenerateCommandTest.php b/tests/Unit/Console/Command/GenerateCommandTest.php index a31fc77..3daf684 100644 --- a/tests/Unit/Console/Command/GenerateCommandTest.php +++ b/tests/Unit/Console/Command/GenerateCommandTest.php @@ -28,6 +28,7 @@ class GenerateCommandTest extends TestCase private Stub&Stopwatch $stopwatch; private GenerateCommand $command; + #[\Override] public function setUp(): void { $this->kernel = $this->createStub(Kernel::class); diff --git a/tests/Unit/Console/Command/ServerCommandTest.php b/tests/Unit/Console/Command/ServerCommandTest.php index 4dc0735..15e3eca 100644 --- a/tests/Unit/Console/Command/ServerCommandTest.php +++ b/tests/Unit/Console/Command/ServerCommandTest.php @@ -24,6 +24,7 @@ class ServerCommandTest extends TestCase private Stub&ServerFactory $serverFactory; private ServerCommand $command; + #[\Override] public function setUp(): void { $this->kernel = $this->createStub(Kernel::class); diff --git a/tests/Unit/Console/CommandLoaderTest.php b/tests/Unit/Console/CommandLoaderTest.php index 3ded14a..922c312 100644 --- a/tests/Unit/Console/CommandLoaderTest.php +++ b/tests/Unit/Console/CommandLoaderTest.php @@ -19,6 +19,7 @@ class CommandLoaderTest extends TestCase private Stub&Kernel $kernel; private CommandLoader $loader; + #[\Override] public function setUp(): void { $this->kernel = $this->createStub(Kernel::class); diff --git a/tests/Unit/EventDispatcher/EventDispatcherTest.php b/tests/Unit/EventDispatcher/EventDispatcherTest.php index 1e0d433..75b3655 100644 --- a/tests/Unit/EventDispatcher/EventDispatcherTest.php +++ b/tests/Unit/EventDispatcher/EventDispatcherTest.php @@ -14,6 +14,7 @@ class EventDispatcherTest extends TestCase { private EventDispatcher $dispatcher; + #[\Override] public function setUp(): void { $this->dispatcher = new EventDispatcher(); diff --git a/tests/Unit/EventDispatcher/RouterConfig/RouterConfigEventTest.php b/tests/Unit/EventDispatcher/RouterConfig/RouterConfigEventTest.php index 0b7f4bf..7a65694 100644 --- a/tests/Unit/EventDispatcher/RouterConfig/RouterConfigEventTest.php +++ b/tests/Unit/EventDispatcher/RouterConfig/RouterConfigEventTest.php @@ -15,27 +15,32 @@ class RouterConfigEventTest extends EventTestCase { private RouteSourceCollection $routes; + #[\Override] public function setUp(): void { parent::setUp(); $this->routes = new RouteSourceCollection(); } + #[\Override] protected function getEvent(): EventInterface { return new RouterConfigEvent($this->routes); } + #[\Override] protected function getEventData(): mixed { return new RouterConfigData($this->routes); } + #[\Override] protected function getListenerClass(): string { return RouterConfigListenerInterface::class; } + #[\Override] protected function getListenerMethod(): string { return 'onRouterConfig'; /* @see RouterConfigListenerInterface::onRouterConfig */ diff --git a/tests/Unit/EventDispatcher/RouterReady/RouterReadyEventTest.php b/tests/Unit/EventDispatcher/RouterReady/RouterReadyEventTest.php index 67e3fed..9202417 100644 --- a/tests/Unit/EventDispatcher/RouterReady/RouterReadyEventTest.php +++ b/tests/Unit/EventDispatcher/RouterReady/RouterReadyEventTest.php @@ -15,6 +15,7 @@ class RouterReadyEventTest extends EventTestCase private Stub&Router $router; private \Generator $routes; + #[\Override] public function setUp(): void { parent::setUp(); @@ -22,21 +23,25 @@ public function setUp(): void $this->routes = (static fn() => yield from [])(); } + #[\Override] protected function getEvent(): EventInterface { return new RouterReadyEvent($this->router, $this->routes); } + #[\Override] protected function getEventData(): mixed { return new RouterReadyData($this->router, $this->routes); } + #[\Override] protected function getListenerClass(): string { return RouterReadyListenerInterface::class; } + #[\Override] protected function getListenerMethod(): string { return 'onRouterReady'; /* @see RouterReadyListenerInterface::onRouterReady */ diff --git a/tests/Unit/Extension/ExtensionLoaderTest.php b/tests/Unit/Extension/ExtensionLoaderTest.php index ba5fdb2..8b29299 100644 --- a/tests/Unit/Extension/ExtensionLoaderTest.php +++ b/tests/Unit/Extension/ExtensionLoaderTest.php @@ -18,6 +18,7 @@ class ExtensionLoaderTest extends TestCase private MockObject&EventDispatcher $dispatcher; private ExtensionLoader $loader; + #[\Override] public function setUp(): void { $this->dispatcher = $this->createMock(EventDispatcher::class); diff --git a/tests/Unit/Generator/Distribution/FilesystemDistributionTest.php b/tests/Unit/Generator/Distribution/FilesystemDistributionTest.php index 30cc52d..918e049 100644 --- a/tests/Unit/Generator/Distribution/FilesystemDistributionTest.php +++ b/tests/Unit/Generator/Distribution/FilesystemDistributionTest.php @@ -20,6 +20,7 @@ class FilesystemDistributionTest extends TestCase private MockObject&Filesystem $filesystem; private FilesystemDistribution $distribution; + #[\Override] public function setUp(): void { $this->filesystem = $this->createMock(Filesystem::class); diff --git a/tests/Unit/Generator/SiteGeneratorTest.php b/tests/Unit/Generator/SiteGeneratorTest.php index b2e2bb0..fe04453 100644 --- a/tests/Unit/Generator/SiteGeneratorTest.php +++ b/tests/Unit/Generator/SiteGeneratorTest.php @@ -26,6 +26,7 @@ class SiteGeneratorTest extends TestCase private MockObject&EventDispatcher $eventDispatcher; private SiteGenerator $siteGenerator; + #[\Override] public function setUp(): void { $this->serviceLocator = $this->createStub(ServiceLocator::class); diff --git a/tests/Unit/Generator/SiteGeneratorVisitorTest.php b/tests/Unit/Generator/SiteGeneratorVisitorTest.php index 680b99d..6f0e615 100644 --- a/tests/Unit/Generator/SiteGeneratorVisitorTest.php +++ b/tests/Unit/Generator/SiteGeneratorVisitorTest.php @@ -25,6 +25,7 @@ class SiteGeneratorVisitorTest extends TestCase private Stub&Router $router; private SiteGeneratorVisitor $visitor; + #[\Override] public function setUp(): void { $this->locator = $this->createMock(ServiceLocator::class); @@ -54,6 +55,7 @@ public function testConstructorSymlinkNotSupported(): void public function testVisitControllerInstance(): void { $resource = new ControllerResource(new class implements ControllerInterface { + #[\Override] public function render(Router $router, array $parameters): string { return 'test content'; @@ -74,6 +76,7 @@ public function testVisitControllerReference(): void $reference = 'controller_reference'; $controller = new class implements ControllerInterface { + #[\Override] public function render(Router $router, array $parameters): string { return 'test content'; @@ -113,6 +116,7 @@ public function testVisitControllerStream(): void { $resource = new ControllerResource(new class implements ControllerInterface { /** @return resource */ + #[\Override] public function render(Router $router, array $parameters) { /** @var resource $stream */ diff --git a/tests/Unit/Router/Compiler/RouteCompilerTest.php b/tests/Unit/Router/Compiler/RouteCompilerTest.php index 13154a5..bb10ba0 100644 --- a/tests/Unit/Router/Compiler/RouteCompilerTest.php +++ b/tests/Unit/Router/Compiler/RouteCompilerTest.php @@ -19,6 +19,7 @@ class RouteCompilerTest extends TestCase private Stub&ServiceLocator $serviceLocator; private RouteCompiler $compiler; + #[\Override] public function setUp(): void { $this->serviceLocator = $this->createStub(ServiceLocator::class); diff --git a/tests/Unit/Router/Compiler/RouteCompilerVisitorTest.php b/tests/Unit/Router/Compiler/RouteCompilerVisitorTest.php index 58cd04a..4130832 100644 --- a/tests/Unit/Router/Compiler/RouteCompilerVisitorTest.php +++ b/tests/Unit/Router/Compiler/RouteCompilerVisitorTest.php @@ -22,6 +22,7 @@ class RouteCompilerVisitorTest extends TestCase private MockObject&ServiceLocator $serviceLocator; private RouteCompilerVisitor $visitor; + #[\Override] public function setUp(): void { $this->serviceLocator = $this->createMock(ServiceLocator::class); @@ -124,6 +125,7 @@ public function __construct( private Route $route2, ) {} + #[\Override] public function routes(): iterable { yield $this->route1; @@ -169,6 +171,7 @@ public function __construct( private Route $route2, ) {} + #[\Override] public function routes(): iterable { yield $this->route1; diff --git a/tests/Unit/Router/RouterTest.php b/tests/Unit/Router/RouterTest.php index 5855ccf..da5a76b 100644 --- a/tests/Unit/Router/RouterTest.php +++ b/tests/Unit/Router/RouterTest.php @@ -19,6 +19,7 @@ class RouterTest extends TestCase private RouteContainer $routeContainer; private Router $router; + #[\Override] public function setUp(): void { $this->routeCollection = new CompiledRouteCollection(); diff --git a/tests/Unit/ServiceLocator/ServiceLocatorTest.php b/tests/Unit/ServiceLocator/ServiceLocatorTest.php index caedb24..34455cd 100644 --- a/tests/Unit/ServiceLocator/ServiceLocatorTest.php +++ b/tests/Unit/ServiceLocator/ServiceLocatorTest.php @@ -15,6 +15,7 @@ class ServiceLocatorTest extends TestCase private MockObject&ContainerInterface $container; private ServiceLocator $serviceLocator; + #[\Override] public function setUp(): void { $this->container = $this->createMock(ContainerInterface::class); From 83efbcbc2c00a5148c8eea98fcaeab8f747e37bb Mon Sep 17 00:00:00 2001 From: Volodymyr Stelmakh Date: Sun, 22 Mar 2026 22:17:32 +0100 Subject: [PATCH 4/4] add strict rules for phpstan --- composer.json | 1 + phpstan.neon | 3 +++ src/Router/Compiler/RouteCompilerVisitor.php | 2 +- src/Server/Server.php | 4 ++-- tests/Functional/StasisProcessFactory.php | 6 +++++- tests/Unit/Console/ApplicationFactoryTest.php | 2 +- tests/Unit/Console/Command/GenerateCommandTest.php | 4 ++-- tests/Unit/Console/Command/ServerCommandTest.php | 10 +++++----- tests/Unit/Console/CommandLoaderTest.php | 2 +- tests/Unit/EventDispatcher/EventTestCase.php | 2 +- .../RouterReady/RouterReadyEventTest.php | 2 +- tests/Unit/Extension/ExtensionLoaderTest.php | 4 ++-- tests/Unit/Generator/SiteGeneratorTest.php | 4 ++-- tests/Unit/Generator/SiteGeneratorVisitorTest.php | 4 ++-- tests/Unit/Router/Compiler/RouteCompilerTest.php | 2 +- 15 files changed, 30 insertions(+), 22 deletions(-) diff --git a/composer.json b/composer.json index 91ef197..821db21 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.87", "phpstan/phpstan": "^2.1", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^13.0", "symfony/process": "^7.3 || ^8" }, diff --git a/phpstan.neon b/phpstan.neon index db07dd8..4538339 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,3 +1,6 @@ +includes: + - vendor/phpstan/phpstan-strict-rules/rules.neon + parameters: level: 8 tmpDir: var/phpstan diff --git a/src/Router/Compiler/RouteCompilerVisitor.php b/src/Router/Compiler/RouteCompilerVisitor.php index 80df13b..7a4ce94 100644 --- a/src/Router/Compiler/RouteCompilerVisitor.php +++ b/src/Router/Compiler/RouteCompilerVisitor.php @@ -30,7 +30,7 @@ public function __construct( public function visitRoute(Route $route): void { $canonicalPath = $this->getCanonicalPath($route->path); - $isFile = preg_match('/\.\w+$/', $canonicalPath); + $isFile = (bool) preg_match('/\.\w+$/', $canonicalPath); $distPath = rtrim($canonicalPath) . ($isFile ? '' : '/index.html'); $type = new ControllerResource($route->controller, $route->parameters); $name = $route->name; diff --git a/src/Server/Server.php b/src/Server/Server.php index 5e15787..ced1af4 100644 --- a/src/Server/Server.php +++ b/src/Server/Server.php @@ -139,11 +139,11 @@ private function validatePath(string $path): void private function validateHost(string $host): void { - if (filter_var($host, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { + if (filter_var($host, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) !== false) { return; } - if (filter_var($host, FILTER_VALIDATE_IP)) { + if (filter_var($host, FILTER_VALIDATE_IP) !== false) { return; } diff --git a/tests/Functional/StasisProcessFactory.php b/tests/Functional/StasisProcessFactory.php index b0fc713..60994ce 100644 --- a/tests/Functional/StasisProcessFactory.php +++ b/tests/Functional/StasisProcessFactory.php @@ -21,8 +21,12 @@ public static function create(array $arguments = [], ?string $workdir = null): P private static function getStasisPath(): string { - /** @var string $kernelPath */ $kernelPath = new \ReflectionClass(Kernel::class)->getFileName(); + + if ($kernelPath === false) { + throw new \RuntimeException('Failed to get kernel path.'); + } + return dirname($kernelPath, 2) . '/bin/stasis'; } } diff --git a/tests/Unit/Console/ApplicationFactoryTest.php b/tests/Unit/Console/ApplicationFactoryTest.php index 5cd8d1e..3f3155d 100644 --- a/tests/Unit/Console/ApplicationFactoryTest.php +++ b/tests/Unit/Console/ApplicationFactoryTest.php @@ -12,7 +12,7 @@ class ApplicationFactoryTest extends TestCase { public function testCreateBuildsConfiguredApplication(): void { - $kernel = $this->createStub(Kernel::class); + $kernel = self::createStub(Kernel::class); $application = ApplicationFactory::create($kernel); self::assertSame('Stasis', $application->getName(), 'Unexpected application name.'); diff --git a/tests/Unit/Console/Command/GenerateCommandTest.php b/tests/Unit/Console/Command/GenerateCommandTest.php index 3daf684..c41c9a1 100644 --- a/tests/Unit/Console/Command/GenerateCommandTest.php +++ b/tests/Unit/Console/Command/GenerateCommandTest.php @@ -31,11 +31,11 @@ class GenerateCommandTest extends TestCase #[\Override] public function setUp(): void { - $this->kernel = $this->createStub(Kernel::class); + $this->kernel = self::createStub(Kernel::class); $this->generator = $this->createMock(SiteGenerator::class); $this->compiler = $this->createMock(RouteCompiler::class); $this->routes = []; - $this->stopwatch = $this->createStub(Stopwatch::class); + $this->stopwatch = self::createStub(Stopwatch::class); $this->command = new GenerateCommand( $this->generator, diff --git a/tests/Unit/Console/Command/ServerCommandTest.php b/tests/Unit/Console/Command/ServerCommandTest.php index 15e3eca..25e4ae0 100644 --- a/tests/Unit/Console/Command/ServerCommandTest.php +++ b/tests/Unit/Console/Command/ServerCommandTest.php @@ -27,9 +27,9 @@ class ServerCommandTest extends TestCase #[\Override] public function setUp(): void { - $this->kernel = $this->createStub(Kernel::class); - $this->distribution = $this->createStub(LocalDistributionInterface::class); - $this->serverFactory = $this->createStub(ServerFactory::class); + $this->kernel = self::createStub(Kernel::class); + $this->distribution = self::createStub(LocalDistributionInterface::class); + $this->serverFactory = self::createStub(ServerFactory::class); $this->command = new ServerCommand( $this->distribution, @@ -172,8 +172,8 @@ public function testExecuteVerbose(): void public function testUnsupportedDistribution(): void { // plain distribution (not local) should fail - $distribution = $this->createStub(DistributionInterface::class); - $factory = $this->createStub(ServerFactory::class); + $distribution = self::createStub(DistributionInterface::class); + $factory = self::createStub(ServerFactory::class); $command = new ServerCommand($distribution, $factory); $tester = new CommandTester($command); diff --git a/tests/Unit/Console/CommandLoaderTest.php b/tests/Unit/Console/CommandLoaderTest.php index 922c312..95d4ef4 100644 --- a/tests/Unit/Console/CommandLoaderTest.php +++ b/tests/Unit/Console/CommandLoaderTest.php @@ -22,7 +22,7 @@ class CommandLoaderTest extends TestCase #[\Override] public function setUp(): void { - $this->kernel = $this->createStub(Kernel::class); + $this->kernel = self::createStub(Kernel::class); $this->loader = new CommandLoader($this->kernel, [ StubACommand::class, StubBCommand::class, diff --git a/tests/Unit/EventDispatcher/EventTestCase.php b/tests/Unit/EventDispatcher/EventTestCase.php index 3fb052c..534518f 100644 --- a/tests/Unit/EventDispatcher/EventTestCase.php +++ b/tests/Unit/EventDispatcher/EventTestCase.php @@ -39,7 +39,7 @@ public function testAcceptMatchingListener(): void public function testAcceptNonMatchingListener(): void { $event = $this->getEvent(); - $listener = $this->createStub(ListenerInterface::class); + $listener = self::createStub(ListenerInterface::class); $isAccepted = $event->accept($listener); self::assertFalse($isAccepted, 'Event should not be accepted by a non-matching listener.'); } diff --git a/tests/Unit/EventDispatcher/RouterReady/RouterReadyEventTest.php b/tests/Unit/EventDispatcher/RouterReady/RouterReadyEventTest.php index 9202417..1f47dc2 100644 --- a/tests/Unit/EventDispatcher/RouterReady/RouterReadyEventTest.php +++ b/tests/Unit/EventDispatcher/RouterReady/RouterReadyEventTest.php @@ -19,7 +19,7 @@ class RouterReadyEventTest extends EventTestCase public function setUp(): void { parent::setUp(); - $this->router = $this->createStub(Router::class); + $this->router = self::createStub(Router::class); $this->routes = (static fn() => yield from [])(); } diff --git a/tests/Unit/Extension/ExtensionLoaderTest.php b/tests/Unit/Extension/ExtensionLoaderTest.php index 8b29299..d3f5f6d 100644 --- a/tests/Unit/Extension/ExtensionLoaderTest.php +++ b/tests/Unit/Extension/ExtensionLoaderTest.php @@ -31,10 +31,10 @@ public function testLoad(): void $listenerB = new class implements ListenerInterface {}; $listenerC = new class implements ListenerInterface {}; - $extension1 = $this->createStub(ExtensionInterface::class); + $extension1 = self::createStub(ExtensionInterface::class); $extension1->method('listeners')->willReturn([$listenerA, $listenerB]); - $extension2 = $this->createStub(ExtensionInterface::class); + $extension2 = self::createStub(ExtensionInterface::class); $extension2->method('listeners')->willReturn([$listenerC]); $listeners = []; diff --git a/tests/Unit/Generator/SiteGeneratorTest.php b/tests/Unit/Generator/SiteGeneratorTest.php index fe04453..a8c074a 100644 --- a/tests/Unit/Generator/SiteGeneratorTest.php +++ b/tests/Unit/Generator/SiteGeneratorTest.php @@ -29,7 +29,7 @@ class SiteGeneratorTest extends TestCase #[\Override] public function setUp(): void { - $this->serviceLocator = $this->createStub(ServiceLocator::class); + $this->serviceLocator = self::createStub(ServiceLocator::class); $this->distribution = new MockDistribution(); $this->eventDispatcher = $this->createMock(EventDispatcher::class); $this->siteGenerator = new SiteGenerator($this->serviceLocator, $this->distribution, $this->eventDispatcher); @@ -81,7 +81,7 @@ public function testGenerateSymlinkUnsupported(): void $this->eventDispatcher->expects($this->once())->method('dispatch'); - $distribution = $this->createStub(DistributionInterface::class); + $distribution = self::createStub(DistributionInterface::class); $siteGenerator = new SiteGenerator($this->serviceLocator, $distribution, $this->eventDispatcher); $this->expectException(LogicException::class); diff --git a/tests/Unit/Generator/SiteGeneratorVisitorTest.php b/tests/Unit/Generator/SiteGeneratorVisitorTest.php index 6f0e615..5114b25 100644 --- a/tests/Unit/Generator/SiteGeneratorVisitorTest.php +++ b/tests/Unit/Generator/SiteGeneratorVisitorTest.php @@ -30,7 +30,7 @@ public function setUp(): void { $this->locator = $this->createMock(ServiceLocator::class); $this->distribution = new MockDistribution(); - $this->router = $this->createStub(Router::class); + $this->router = self::createStub(Router::class); $this->visitor = new SiteGeneratorVisitor( $this->locator, @@ -44,7 +44,7 @@ public function setUp(): void public function testConstructorSymlinkNotSupported(): void { $this->locator->expects($this->never())->method('get'); - $distribution = $this->createStub(DistributionInterface::class); + $distribution = self::createStub(DistributionInterface::class); $this->expectException(LogicException::class); $this->expectExceptionMessage('does not support symlinks'); diff --git a/tests/Unit/Router/Compiler/RouteCompilerTest.php b/tests/Unit/Router/Compiler/RouteCompilerTest.php index bb10ba0..637c187 100644 --- a/tests/Unit/Router/Compiler/RouteCompilerTest.php +++ b/tests/Unit/Router/Compiler/RouteCompilerTest.php @@ -22,7 +22,7 @@ class RouteCompilerTest extends TestCase #[\Override] public function setUp(): void { - $this->serviceLocator = $this->createStub(ServiceLocator::class); + $this->serviceLocator = self::createStub(ServiceLocator::class); $this->compiler = new RouteCompiler('/base', $this->serviceLocator); }