From 9341e858f45216b1dfeb8d08923469be7413a9c7 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sat, 6 Apr 2024 17:49:56 +0200 Subject: [PATCH] setup phpstan --- composer.json | 5 + phpstan.neon.dist | 15 +++ src/Doctrine/Orm/RouteProvider.php | 4 +- src/Doctrine/Phpcr/ContentRepository.php | 14 +++ src/Doctrine/Phpcr/RouteProvider.php | 17 +++- .../RouteDefaultsTwigValidator.php | 3 + .../App/DataFixtures/Phpcr/LoadRouteData.php | 4 + tests/Fixtures/App/Document/Content.php | 2 +- tests/Fixtures/App/Kernel.php | 7 +- .../Doctrine/Phpcr/RedirectRouteTest.php | 3 +- .../Compiler/TemplatingValidatorPassTest.php | 75 --------------- tests/Unit/Doctrine/Phpcr/RouteTest.php | 3 +- .../RouteDefaultsTemplatingValidatorTest.php | 38 -------- .../RouteDefaultsTwigValidatorTest.php | 66 ++++++++++++- .../RouteDefaultsValidatorTest.php | 94 ------------------- 15 files changed, 127 insertions(+), 223 deletions(-) create mode 100644 phpstan.neon.dist delete mode 100644 tests/Unit/DependencyInjection/Compiler/TemplatingValidatorPassTest.php delete mode 100644 tests/Unit/Validator/Constraints/RouteDefaultsTemplatingValidatorTest.php delete mode 100644 tests/Unit/Validator/Constraints/RouteDefaultsValidatorTest.php diff --git a/composer.json b/composer.json index 47bc78ef..1a07d8e1 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,11 @@ "doctrine/phpcr-bundle": "^3.0", "doctrine/phpcr-odm": "^2.0", "jackalope/jackalope-doctrine-dbal": "^2.0", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-doctrine": "^1.3", + "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^9.5", "matthiasnoback/symfony-dependency-injection-test": "^4.1.0 || ^5.1.0", "matthiasnoback/symfony-config-test": "^4.1.0 || ^5.1.0", "symfony/phpunit-bridge": "^7.0.3", diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..22e66521 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,15 @@ +includes: + - vendor/phpstan/phpstan-doctrine/extension.neon + - vendor/phpstan/phpstan-doctrine/rules.neon + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-symfony/extension.neon + - vendor/phpstan/phpstan-symfony/rules.neon +parameters: + level: 2 + paths: + - src/ + - tests/ + excludePaths: + - tests/Fixtures/App/var/ + - tests/Fixtures/App/config/ + - tests/Fixtures/fixtures/config/ diff --git a/src/Doctrine/Orm/RouteProvider.php b/src/Doctrine/Orm/RouteProvider.php index e084857b..27d17fe9 100644 --- a/src/Doctrine/Orm/RouteProvider.php +++ b/src/Doctrine/Orm/RouteProvider.php @@ -52,8 +52,8 @@ public function getRouteCollectionForRequest(Request $request): RouteCollection if (0 === \count($candidates)) { return $collection; } - $routes = $this->getRouteRepository()->findByStaticPrefix($candidates, ['position' => 'ASC']); - /** @var $route Route */ + $routes = $this->getRouteRepository()->findByStaticPrefix($candidates, ['position' => 'ASC']); /** @phpstan-ignore-line */ + /** @var Route $route */ foreach ($routes as $route) { $collection->add($route->getName(), $route); } diff --git a/src/Doctrine/Phpcr/ContentRepository.php b/src/Doctrine/Phpcr/ContentRepository.php index 834cc6ed..96f238a5 100644 --- a/src/Doctrine/Phpcr/ContentRepository.php +++ b/src/Doctrine/Phpcr/ContentRepository.php @@ -11,6 +11,7 @@ namespace Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr; +use Doctrine\ODM\PHPCR\DocumentManagerInterface; use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\DoctrineProvider; use Symfony\Cmf\Component\Routing\ContentRepositoryInterface; @@ -48,4 +49,17 @@ public function getContentId($content): ?string return null; } } + + /** + * Make sure the manager is a PHPCR-ODM manager. + */ + protected function getObjectManager(): DocumentManagerInterface + { + $dm = parent::getObjectManager(); + if (!$dm instanceof DocumentManagerInterface) { + throw new \LogicException(sprintf('Expected %s, got %s', DocumentManagerInterface::class, get_class($dm))); + } + + return $dm; + } } diff --git a/src/Doctrine/Phpcr/RouteProvider.php b/src/Doctrine/Phpcr/RouteProvider.php index 4cabdc87..cf21c4ed 100644 --- a/src/Doctrine/Phpcr/RouteProvider.php +++ b/src/Doctrine/Phpcr/RouteProvider.php @@ -13,6 +13,7 @@ use Doctrine\DBAL\Exception\TableNotFoundException; use Doctrine\ODM\PHPCR\DocumentManager; +use Doctrine\ODM\PHPCR\DocumentManagerInterface; use Doctrine\Persistence\ManagerRegistry; use PHPCR\RepositoryException; use PHPCR\Util\UUIDHelper; @@ -85,7 +86,6 @@ public function getRouteCollectionForRequest(Request $request): RouteCollection } try { - /** @var $dm DocumentManager */ $dm = $this->getObjectManager(); $routes = $dm->findMany($this->className, $candidates); // filter for valid route objects @@ -151,7 +151,6 @@ private function getAllRoutes(): iterable } try { - /** @var $dm DocumentManager */ $dm = $this->getObjectManager(); } catch (RepositoryException $e) { // special case: there is not even a database existing. this means there are no routes. @@ -192,7 +191,6 @@ public function getRoutesByNames(?array $names = null): iterable return []; } - /** @var $dm DocumentManager */ $dm = $this->getObjectManager(); $documents = $dm->findMany($this->className, $candidates); foreach ($documents as $key => $document) { @@ -210,4 +208,17 @@ public function getRoutesByNames(?array $names = null): iterable return $documents; } + + /** + * Make sure the manager is a PHPCR-ODM manager. + */ + protected function getObjectManager(): DocumentManagerInterface + { + $dm = parent::getObjectManager(); + if (!$dm instanceof DocumentManagerInterface) { + throw new \LogicException(sprintf('Expected %s, got %s', DocumentManagerInterface::class, get_class($dm))); + } + + return $dm; + } } diff --git a/src/Validator/Constraints/RouteDefaultsTwigValidator.php b/src/Validator/Constraints/RouteDefaultsTwigValidator.php index 8742e0d6..04258dc9 100644 --- a/src/Validator/Constraints/RouteDefaultsTwigValidator.php +++ b/src/Validator/Constraints/RouteDefaultsTwigValidator.php @@ -33,6 +33,9 @@ public function __construct(ControllerResolverInterface $controllerResolver, ?Lo public function validate($defaults, Constraint $constraint) { + if (!$constraint instanceof RouteDefaults) { + throw new \InvalidArgumentException(sprintf('Expected %s, got %s', RouteDefaults::class, get_class($constraint))); + } if (\array_key_exists('_controller', $defaults) && null !== $defaults['_controller']) { $controller = $defaults['_controller']; diff --git a/tests/Fixtures/App/DataFixtures/Phpcr/LoadRouteData.php b/tests/Fixtures/App/DataFixtures/Phpcr/LoadRouteData.php index 2ad83ea8..756ff05c 100644 --- a/tests/Fixtures/App/DataFixtures/Phpcr/LoadRouteData.php +++ b/tests/Fixtures/App/DataFixtures/Phpcr/LoadRouteData.php @@ -13,6 +13,7 @@ use Doctrine\Common\DataFixtures\FixtureInterface; use Doctrine\ODM\PHPCR\Document\Generic; +use Doctrine\ODM\PHPCR\DocumentManagerInterface; use Doctrine\Persistence\ObjectManager; use PHPCR\Util\NodeHelper; use Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\RedirectRoute; @@ -22,6 +23,9 @@ class LoadRouteData implements FixtureInterface { public function load(ObjectManager $manager) { + if (!$manager instanceof DocumentManagerInterface) { + throw new \InvalidArgumentException(sprintf('Expected %s, got %s', DocumentManagerInterface::class, get_class($manager))); + } NodeHelper::createPath($manager->getPhpcrSession(), '/test'); $root = $manager->find(null, '/test'); diff --git a/tests/Fixtures/App/Document/Content.php b/tests/Fixtures/App/Document/Content.php index c9eab724..bbba34b4 100644 --- a/tests/Fixtures/App/Document/Content.php +++ b/tests/Fixtures/App/Document/Content.php @@ -22,7 +22,7 @@ class Content #[PHPCRODM\ParentDocument] private $parent; - #[PHPCRODM\NodeName] + #[PHPCRODM\Nodename] private $name; #[PHPCRODM\Field(type: 'string')] diff --git a/tests/Fixtures/App/Kernel.php b/tests/Fixtures/App/Kernel.php index 8dbc25a2..14498e8a 100644 --- a/tests/Fixtures/App/Kernel.php +++ b/tests/Fixtures/App/Kernel.php @@ -11,6 +11,7 @@ namespace Symfony\Cmf\Bundle\RoutingBundle\Tests\Fixtures\App; +use Symfony\Cmf\Bundle\ResourceBundle\CmfResourceBundle; use Symfony\Cmf\Bundle\ResourceRestBundle\CmfResourceRestBundle; use Symfony\Cmf\Component\Testing\HttpKernel\TestKernel; use Symfony\Component\Config\Loader\LoaderInterface; @@ -31,10 +32,10 @@ public function configure(): void $this->registerConfiguredBundles(); - if (class_exists(CmfResourceRestBundle::class)) { + if (class_exists(CmfResourceBundle::class) && class_exists(CmfResourceRestBundle::class)) { $this->addBundles([ - new \Symfony\Cmf\Bundle\ResourceBundle\CmfResourceBundle(), - new \Symfony\Cmf\Bundle\ResourceRestBundle\CmfResourceRestBundle(), + new CmfResourceBundle(), + new CmfResourceRestBundle(), ]); } } diff --git a/tests/Functional/Doctrine/Phpcr/RedirectRouteTest.php b/tests/Functional/Doctrine/Phpcr/RedirectRouteTest.php index e663f830..78892b97 100644 --- a/tests/Functional/Doctrine/Phpcr/RedirectRouteTest.php +++ b/tests/Functional/Doctrine/Phpcr/RedirectRouteTest.php @@ -51,9 +51,8 @@ public function testRedirectDoctrine() $route = $this->getDm()->find(null, self::ROUTE_ROOT.'/testroute'); $redirect = $this->getDm()->find(null, self::ROUTE_ROOT.'/redirect'); - $this->assertInstanceOf(RedirectRouteInterface::class, $redirect); + $this->assertInstanceOf(RedirectRoute::class, $redirect); $this->assertSame($redirect, $redirect->getContent()); - $params = $redirect->getParameters(); $this->assertSame($route, $redirect->getRouteTarget()); $defaults = $redirect->getDefaults(); $this->assertEquals(['test' => 'toast'], $defaults); diff --git a/tests/Unit/DependencyInjection/Compiler/TemplatingValidatorPassTest.php b/tests/Unit/DependencyInjection/Compiler/TemplatingValidatorPassTest.php deleted file mode 100644 index 4660036d..00000000 --- a/tests/Unit/DependencyInjection/Compiler/TemplatingValidatorPassTest.php +++ /dev/null @@ -1,75 +0,0 @@ -markTestSkipped(); - } - - parent::setUp(); - - $this->registerValidatorService(); - } - - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new TemplatingValidatorPass()); - } - - /** - * It should replace the validator class with the templating one and - * provide the _templating_ service as second argument. - */ - public function testReplacesValidator() - { - $this->registerService('templating', \stdClass::class); - - $this->compile(); - - $definition = $this->container->getDefinition('cmf_routing.validator.route_defaults'); - - $this->assertEquals(RouteDefaultsTemplatingValidator::class, $definition->getClass()); - $this->assertEquals(['foo', new Reference('templating')], $definition->getArguments()); - } - - /** - * It should leave the validator untouched when the _templating_ service - * is not available. - */ - public function testDoesNotReplaceValidator() - { - $this->compile(); - - $definition = $this->container->getDefinition('cmf_routing.validator.route_defaults'); - - $this->assertEquals(\stdClass::class, $definition->getClass()); - $this->assertEquals(['foo', 'bar'], $definition->getArguments()); - } - - private function registerValidatorService() - { - $definition = $this->registerService('cmf_routing.validator.route_defaults', \stdClass::class); - - $definition->setArguments(['foo', 'bar']); - } -} diff --git a/tests/Unit/Doctrine/Phpcr/RouteTest.php b/tests/Unit/Doctrine/Phpcr/RouteTest.php index 2bae6543..bce445bd 100644 --- a/tests/Unit/Doctrine/Phpcr/RouteTest.php +++ b/tests/Unit/Doctrine/Phpcr/RouteTest.php @@ -33,7 +33,6 @@ public function testGetRouteChildren(): void { $refl = new \ReflectionClass($this->route); $prop = $refl->getProperty('children'); - $prop->setAccessible(true); $prop->setValue($this->route, new ArrayCollection([ new \stdClass(), $this->childRoute1, @@ -41,7 +40,7 @@ public function testGetRouteChildren(): void $res = $this->route->getRouteChildren(); $this->assertCount(1, $res); - $this->assertEquals('child route1', $res[0]->getName()); + $this->assertSame($this->childRoute1, $res[0]); } public function testGetRouteChildrenNull(): void diff --git a/tests/Unit/Validator/Constraints/RouteDefaultsTemplatingValidatorTest.php b/tests/Unit/Validator/Constraints/RouteDefaultsTemplatingValidatorTest.php deleted file mode 100644 index 55c3e17f..00000000 --- a/tests/Unit/Validator/Constraints/RouteDefaultsTemplatingValidatorTest.php +++ /dev/null @@ -1,38 +0,0 @@ -createMock(EngineInterface::class); - } - - protected function setUp(): void - { - if (!class_exists(EngineInterface::class)) { - $this->markTestSkipped(); - } - - parent::setUp(); - } - - protected function createValidator(): ConstraintValidatorInterface - { - return new RouteDefaultsTemplatingValidator($this->controllerResolver, $this->engine); - } -} diff --git a/tests/Unit/Validator/Constraints/RouteDefaultsTwigValidatorTest.php b/tests/Unit/Validator/Constraints/RouteDefaultsTwigValidatorTest.php index 5894ff98..4dd56316 100644 --- a/tests/Unit/Validator/Constraints/RouteDefaultsTwigValidatorTest.php +++ b/tests/Unit/Validator/Constraints/RouteDefaultsTwigValidatorTest.php @@ -11,16 +11,27 @@ namespace Symfony\Cmf\Bundle\RoutingBundle\Tests\Unit\Validator\Constraints; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; use Symfony\Cmf\Bundle\RoutingBundle\Validator\Constraints\RouteDefaults; use Symfony\Cmf\Bundle\RoutingBundle\Validator\Constraints\RouteDefaultsTwigValidator; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; use Symfony\Component\Validator\ConstraintValidatorInterface; +use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; use Twig\Loader\LoaderInterface; -class RouteDefaultsTwigValidatorTest extends RouteDefaultsValidatorTest +class RouteDefaultsTwigValidatorTest extends ConstraintValidatorTestCase { - protected function mockEngine() + protected MockObject&ControllerResolverInterface $controllerResolver; + + private MockObject&LoaderInterface $engine; + + protected function setUp(): void { - return $this->createMock(LoaderInterface::class); + $this->controllerResolver = $this->createMock(ControllerResolverInterface::class); + $this->engine = $this->createMock(LoaderInterface::class); + + parent::setUp(); } protected function createValidator(): ConstraintValidatorInterface @@ -38,4 +49,53 @@ public function testNoTemplateViolationWithoutTwig(): void $this->assertNoViolation(); } + + public function testCorrectControllerPath(): void + { + $this->validator->validate(['_controller' => 'FrameworkBundle:Redirect:redirect'], new RouteDefaults()); + + $this->assertNoViolation(); + } + + public function testControllerPathViolation(): void + { + $this->controllerResolver + ->method('getController') + ->willThrowException(new \LogicException('Invalid controller')) + ; + + $this->validator->validate(['_controller' => 'NotExistingBundle:Foo:bar'], new RouteDefaults()); + + $this->buildViolation('Invalid controller')->assertRaised(); + } + + public function testCorrectTemplate(): void + { + $this->engine + ->method('exists') + ->willReturn(true) + ; + + $this->validator->validate(['_template' => 'TwigBundle::layout.html.twig'], $this->constraint); + + $this->assertNoViolation(); + } + + public function testTemplateViolation(): void + { + $this + ->engine + ->method('exists') + ->willReturn(false) + ; + + $this->validator->validate( + ['_template' => 'NotExistingBundle:Foo:bar.html.twig'], + new RouteDefaults(['message' => 'my message']) + ); + + $this->buildViolation('my message') + ->setParameter('%name%', 'NotExistingBundle:Foo:bar.html.twig') + ->assertRaised(); + } } diff --git a/tests/Unit/Validator/Constraints/RouteDefaultsValidatorTest.php b/tests/Unit/Validator/Constraints/RouteDefaultsValidatorTest.php deleted file mode 100644 index c7ec58b6..00000000 --- a/tests/Unit/Validator/Constraints/RouteDefaultsValidatorTest.php +++ /dev/null @@ -1,94 +0,0 @@ -controllerResolver = $this->createMock(ControllerResolverInterface::class); - $this->engine = $this->mockEngine(); - - parent::setUp(); - } - - /** - * @return MockObject|EngineInterface|LoaderInterface - */ - abstract protected function mockEngine(); - - public function testCorrectControllerPath(): void - { - $this->validator->validate(['_controller' => 'FrameworkBundle:Redirect:redirect'], new RouteDefaults()); - - $this->assertNoViolation(); - } - - public function testControllerPathViolation(): void - { - $this->controllerResolver - ->method('getController') - ->willThrowException(new \LogicException('Invalid controller')) - ; - - $this->validator->validate(['_controller' => 'NotExistingBundle:Foo:bar'], new RouteDefaults()); - - $this->buildViolation('Invalid controller')->assertRaised(); - } - - public function testCorrectTemplate(): void - { - $this->engine - ->method('exists') - ->willReturn(true) - ; - - $this->validator->validate(['_template' => 'TwigBundle::layout.html.twig'], $this->constraint); - - $this->assertNoViolation(); - } - - public function testTemplateViolation(): void - { - $this - ->engine - ->method('exists') - ->willReturn(false) - ; - - $this->validator->validate( - ['_template' => 'NotExistingBundle:Foo:bar.html.twig'], - new RouteDefaults(['message' => 'my message']) - ); - - $this->buildViolation('my message') - ->setParameter('%name%', 'NotExistingBundle:Foo:bar.html.twig') - ->assertRaised(); - } -}