From 0800e6af55662930b35dbd0d9bd8819a2e1ce432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Sun, 21 Jan 2018 19:06:02 +0100 Subject: [PATCH] Make the extension official --- .coveralls.yml | 6 +- .gitattributes | 16 +-- .travis.yml | 48 ++------- README.md | 38 ++++---- build.xml | 97 +++++++++++++++++++ composer.json | 50 +++++----- extension.neon | 22 +++-- phpcs.xml | 43 ++++++++ phpcs.xml.dist | 4 - phpstan.neon | 5 +- phpunit.xml.dist | 13 --- .../ContainerInterfacePrivateServiceRule.php | 30 +++--- .../ContainerInterfaceUnknownServiceRule.php | 30 +++--- src/{ => Symfony}/ServiceMap.php | 48 ++++----- .../XmlContainerNotExistsException.php | 5 +- ...nerInterfaceDynamicReturnTypeExtension.php | 23 ++--- .../ControllerDynamicReturnTypeExtension.php | 23 ++--- ...ntainerInterfacePrivateServiceRuleTest.php | 29 ------ ...ntainerInterfaceUnknownServiceRuleTest.php | 41 -------- ...ntainerInterfacePrivateServiceRuleTest.php | 34 +++++++ ...ntainerInterfaceUnknownServiceRuleTest.php | 46 +++++++++ .../{data => Symfony}/ExampleController.php | 7 +- tests/{ => Symfony}/ServiceMapTest.php | 47 ++++----- tests/{ => Symfony/data}/container.xml | 0 ...nterfaceDynamicReturnTypeExtensionTest.php | 33 ++++--- ...ntrollerDynamicReturnTypeExtensionTest.php | 33 ++++--- tests/bootstrap.php | 3 + tests/phpunit.xml | 27 ++++++ 28 files changed, 470 insertions(+), 331 deletions(-) create mode 100644 build.xml create mode 100644 phpcs.xml delete mode 100644 phpcs.xml.dist delete mode 100644 phpunit.xml.dist rename src/Rules/{ => Symfony}/ContainerInterfacePrivateServiceRule.php (72%) rename src/Rules/{ => Symfony}/ContainerInterfaceUnknownServiceRule.php (74%) rename src/{ => Symfony}/ServiceMap.php (60%) rename src/{Exception => Symfony}/XmlContainerNotExistsException.php (52%) rename src/Type/{ => Symfony}/ContainerInterfaceDynamicReturnTypeExtension.php (76%) rename src/Type/{ => Symfony}/ControllerDynamicReturnTypeExtension.php (76%) delete mode 100644 tests/Rules/ContainerInterfacePrivateServiceRuleTest.php delete mode 100644 tests/Rules/ContainerInterfaceUnknownServiceRuleTest.php create mode 100644 tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php create mode 100644 tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php rename tests/Rules/{data => Symfony}/ExampleController.php (87%) rename tests/{ => Symfony}/ServiceMapTest.php (50%) rename tests/{ => Symfony/data}/container.xml (100%) rename tests/Type/{ => Symfony}/ContainerInterfaceDynamicReturnTypeExtensionTest.php (77%) rename tests/Type/{ => Symfony}/ControllerDynamicReturnTypeExtensionTest.php (78%) create mode 100644 tests/bootstrap.php create mode 100644 tests/phpunit.xml diff --git a/.coveralls.yml b/.coveralls.yml index c3641aa..267998d 100644 --- a/.coveralls.yml +++ b/.coveralls.yml @@ -1,3 +1,3 @@ -service_name: travis-ci -coverage_clover: coverage.xml -json_path: coverage.json +service_name: travis-ci +coverage_clover: tests/tmp/clover.xml +json_path: tests/tmp/coveralls.json diff --git a/.gitattributes b/.gitattributes index 8c2b448..193cb88 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,8 +1,8 @@ -tests export-ignore -.coveralls.yml export-ignore -.gitattributes export-ignore -.gitignore export-ignore -.travis.yml export-ignore -phpstan.neon export-ignore -phpunit.xml.dist export-ignore -ruleset.xml export-ignore +tests export-ignore +.coveralls.yml export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore +build.xml +phpcs.xml +phpstan.neon export-ignore diff --git a/.travis.yml b/.travis.yml index 558c723..c73cacf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,43 +1,15 @@ language: php - -sudo: false - -cache: - directories: - - $HOME/.composer/cache - -env: - - DEPENDENCIES="--prefer-lowest" - - DEPENDENCIES="" - php: - 7.1 - 7.2 - - master - -matrix: - fast_finish: true - include: - - php: 7.2 - env: COVERAGE="1" - allow_failures: - - php: 7.2 - env: COVERAGE="1" - - php: master - -install: - - travis_retry composer update --no-interaction --prefer-dist $DEPENDENCIES - +env: + - dependencies=lowest + - dependencies=highest +before_script: + - composer self-update + - if [ "$dependencies" = "lowest" ]; then composer update --prefer-lowest --no-interaction; fi; + - if [ "$dependencies" = "highest" ]; then composer update --no-interaction; fi; script: - - > - if [ "$COVERAGE" != "1" ]; then - phpenv config-rm xdebug.ini - && composer check; fi - - if [ "$COVERAGE" == "1" ]; then ./vendor/bin/phpunit --coverage-clover=./coverage.xml; fi - -after_success: - - > - if [ $COVERAGE == "1" ]; then - wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar - && php ./coveralls.phar --verbose - || true; fi + - vendor/bin/phing +after_script: + - php vendor/bin/coveralls -v diff --git a/README.md b/README.md index cce9fba..99cc25c 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,31 @@ -# Symfony extension for PHPStan +# PHPStan Symfony Framework extensions and rules -## What does it do? +[![Build Status](https://travis-ci.org/phpstan/phpstan-symfony.svg)](https://travis-ci.org/phpstan/phpstan-symfony) +[![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-symfony/v/stable)](https://packagist.org/packages/phpstan/phpstan-symfony) +[![License](https://poser.pugx.org/phpstan/phpstan-symfony/license)](https://packagist.org/packages/phpstan/phpstan-symfony) -* Provides correct return type for `ContainerInterface::get()` method, -* provides correct return type for `Controller::get()` method, -* notifies you when you try to get an unregistered service from the container, -* notifies you when you try to get a private service from the container. +* [PHPStan](https://github.com/phpstan/phpstan) -## Installation +This extension provides following features: -```sh -composer require --dev lookyman/phpstan-symfony -``` +* Provides correct return type for `ContainerInterface::get()` method. +* Provides correct return type for `Controller::get()` method. +* Notifies you when you try to get an unregistered service from the container. +* Notifies you when you try to get a private service from the container. + +## Usage -## Configuration +To use this extension, require it in [Composer](https://getcomposer.org/): + +```bash +composer require --dev phpstan/phpstan-symfony +``` -Put this into your `phpstan.neon` config: +And include extension.neon in your project's PHPStan config: -```neon +``` includes: - - vendor/lookyman/phpstan-symfony/extension.neon + - vendor/phpstan/phpstan-symfony/extension.neon parameters: symfony: container_xml_path: %rootDir%/../../../var/cache/dev/appDevDebugProjectContainer.xml # or srcDevDebugProjectContainer.xml for Symfony 4+ @@ -30,7 +36,3 @@ parameters: It can only recognize pure strings or `::class` constants passed into `get()` method. This follows from the nature of static code analysis. You have to provide a path to `appDevDebugProjectContainer.xml` or similar xml file describing your container. - -## Need something? - -I don't use Symfony that often. So it might be entirely possible that something doesn't work here or that it lacks some functionality. If that's the case, **PLEASE DO NOT HESITATE** to open an issue or send a pull request. I will have a look at it and together we'll get you what you need. Thanks. diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..4c3e828 --- /dev/null +++ b/build.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composer.json b/composer.json index 8b77541..5b9dff4 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,7 @@ { - "name": "lookyman/phpstan-symfony", - "license": "MIT", - "description": "Symfony extension for PHPStan", - "keywords": ["PHPStan", "Symfony"], + "name": "phpstan/phpstan-symfony", + "description": "Symfony Framework extensions and rules for PHPStan", + "license": ["MIT"], "authors": [ { "name": "Lukáš Unger", @@ -10,38 +9,35 @@ "homepage": "https://lookyman.net" } ], + "minimum-stability": "dev", + "prefer-stable": true, + "extra": { + "branch-alias": { + "dev-master": "0.10-dev" + } + }, "require": { "php": "^7.1", - "phpstan/phpstan": "^0.9.2" + "phpstan/phpstan": "^0.10" }, "require-dev": { - "jakub-onderka/php-parallel-lint": "^0.9.2", - "phpunit/phpunit": "^6.4 || ^7.0", - "phpstan/phpstan-phpunit": "^0.9.0", - "symfony/framework-bundle": "^4.0", - "phpstan/phpstan-strict-rules": "^0.9.0", - "lookyman/coding-standard": "0.1.0" + "consistence/coding-standard": "^3.0.1", + "jakub-onderka/php-parallel-lint": "^1.0", + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4", + "phpunit/phpunit": "^7.0", + "phing/phing": "^2.16.0", + "phpstan/phpstan-strict-rules": "^0.10", + "satooshi/php-coveralls": "^1.0", + "slevomat/coding-standard": "^4.5.2", + "phpstan/phpstan-phpunit": "^0.10", + "symfony/framework-bundle": "^4.0" }, "autoload": { "psr-4": { - "Lookyman\\PHPStan\\Symfony\\": "src/" + "PHPStan\\": "src/" } }, "autoload-dev": { - "psr-4": { - "Lookyman\\PHPStan\\Symfony\\": "tests/" - } - }, - "scripts": { - "lint": "parallel-lint ./src ./tests", - "cs": "phpcs --colors --extensions=php --encoding=utf-8 -sp ./src ./tests", - "tests": "phpunit --coverage-text", - "stan": "phpstan analyse -l max -c ./phpstan.neon ./src ./tests", - "check": [ - "@lint", - "@cs", - "@tests", - "@stan" - ] + "classmap": ["tests/"] } } diff --git a/extension.neon b/extension.neon index 9a414d4..a4cf761 100644 --- a/extension.neon +++ b/extension.neon @@ -1,14 +1,18 @@ services: - - class: Lookyman\PHPStan\Symfony\Type\ContainerInterfaceDynamicReturnTypeExtension - tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + class: PHPStan\Type\Symfony\ContainerInterfaceDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension - - class: Lookyman\PHPStan\Symfony\Type\ControllerDynamicReturnTypeExtension - tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + class: PHPStan\Type\Symfony\ControllerDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension - - class: Lookyman\PHPStan\Symfony\Rules\ContainerInterfacePrivateServiceRule - tags: [phpstan.rules.rule] + class: PHPStan\Rules\Symfony\ContainerInterfacePrivateServiceRule + tags: + - phpstan.rules.rule - - class: Lookyman\PHPStan\Symfony\Rules\ContainerInterfaceUnknownServiceRule - tags: [phpstan.rules.rule] - - Lookyman\PHPStan\Symfony\ServiceMap(%symfony.container_xml_path%) + class: PHPStan\Rules\Symfony\ContainerInterfaceUnknownServiceRule + tags: + - phpstan.rules.rule + - PHPStan\Symfony\ServiceMap(%symfony.container_xml_path%) diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..b36b184 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tests/*/data + diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index 541a3e7..0000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/phpstan.neon b/phpstan.neon index 71b6b4f..df72db1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,4 +5,7 @@ includes: parameters: excludes_analyse: - - %rootDir%/../../../tests/*/data/* + - */tests/*/data/* + ignoreErrors: + - '#Call to an undefined method object::noMethod\(\)\.#' + - '#Offset mixed does not exist on array\(\)\.#' diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index 9326a7c..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,13 +0,0 @@ - - - - - ./tests - - - - - ./src - - - diff --git a/src/Rules/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php similarity index 72% rename from src/Rules/ContainerInterfacePrivateServiceRule.php rename to src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index 7e3fc29..df680fe 100644 --- a/src/Rules/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -1,23 +1,19 @@ -name === 'get') { @@ -37,13 +38,10 @@ public function processNode(Node $node, Scope $scope): array $baseController = new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\Controller'); $isInstanceOfController = $type instanceof ThisType && $baseController->isSuperTypeOf($type)->yes(); $isContainerInterface = $type instanceof ObjectType && $type->getClassName() === 'Symfony\Component\DependencyInjection\ContainerInterface'; - if (($isContainerInterface || $isInstanceOfController) - && isset($node->args[0]) - && $node->args[0] instanceof Arg - ) { + if (($isContainerInterface || $isInstanceOfController) && isset($node->args[0])) { $service = $this->serviceMap->getServiceFromNode($node->args[0]->value, $scope); - if ($service !== \null && !$service['public']) { - return [\sprintf('Service "%s" is private.', $service['id'])]; + if ($service !== null && $service['public'] === false) { + return [sprintf('Service "%s" is private.', $service['id'])]; } } } diff --git a/src/Rules/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php similarity index 74% rename from src/Rules/ContainerInterfaceUnknownServiceRule.php rename to src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index 09027df..19207d1 100644 --- a/src/Rules/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -1,23 +1,19 @@ -name === 'get') { @@ -37,15 +38,12 @@ public function processNode(Node $node, Scope $scope): array $baseController = new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\Controller'); $isInstanceOfController = $type instanceof ThisType && $baseController->isSuperTypeOf($type)->yes(); $isContainerInterface = $type instanceof ObjectType && $type->getClassName() === 'Symfony\Component\DependencyInjection\ContainerInterface'; - if (($isInstanceOfController || $isContainerInterface) - && isset($node->args[0]) - && $node->args[0] instanceof Arg - ) { + if (($isInstanceOfController || $isContainerInterface) && isset($node->args[0])) { $service = $this->serviceMap->getServiceFromNode($node->args[0]->value, $scope); - if ($service === \null) { + if ($service === null) { $serviceId = ServiceMap::getServiceIdFromNode($node->args[0]->value, $scope); if ($serviceId !== null) { - return [\sprintf('Service "%s" is not registered in the container.', $serviceId)]; + return [sprintf('Service "%s" is not registered in the container.', $serviceId)]; } } } diff --git a/src/ServiceMap.php b/src/Symfony/ServiceMap.php similarity index 60% rename from src/ServiceMap.php rename to src/Symfony/ServiceMap.php index 1ad27c9..f930d33 100644 --- a/src/ServiceMap.php +++ b/src/Symfony/ServiceMap.php @@ -1,31 +1,27 @@ - */ private $services; public function __construct(string $containerXml) { $this->services = $aliases = []; - /** @var \SimpleXMLElement $def */ - $xml = @\simplexml_load_file($containerXml); + /** @var \SimpleXMLElement|false $xml */ + $xml = @simplexml_load_file($containerXml); if ($xml === false) { - throw new XmlContainerNotExistsException(\sprintf('Container %s not exists', $containerXml)); + throw new \PHPStan\Symfony\XmlContainerNotExistsException(sprintf('Container %s not exists', $containerXml)); } foreach ($xml->services->service as $def) { $attrs = $def->attributes(); @@ -34,32 +30,38 @@ public function __construct(string $containerXml) } $service = [ 'id' => (string) $attrs->id, - 'class' => isset($attrs->class) ? (string) $attrs->class : \null, + 'class' => isset($attrs->class) ? (string) $attrs->class : null, 'public' => !isset($attrs->public) || (string) $attrs->public !== 'false', 'synthetic' => isset($attrs->synthetic) && (string) $attrs->synthetic === 'true', ]; if (isset($attrs->alias)) { - $aliases[(string) $attrs->id] = \array_merge($service, ['alias' => (string) $attrs->alias]); + $aliases[(string) $attrs->id] = array_merge($service, ['alias' => (string) $attrs->alias]); } else { $this->services[(string) $attrs->id] = $service; } } foreach ($aliases as $id => $alias) { - if (\array_key_exists($alias['alias'], $this->services)) { - $this->services[$id] = [ - 'id' => $id, - 'class' => $this->services[$alias['alias']]['class'], - 'public' => $alias['public'], - 'synthetic' => $alias['synthetic'], - ]; + if (!array_key_exists($alias['alias'], $this->services)) { + continue; } + $this->services[$id] = [ + 'id' => $id, + 'class' => $this->services[$alias['alias']]['class'], + 'public' => $alias['public'], + 'synthetic' => $alias['synthetic'], + ]; } } + /** + * @param Node $node + * @param Scope $scope + * @return mixed[]|null + */ public function getServiceFromNode(Node $node, Scope $scope): ?array { $serviceId = self::getServiceIdFromNode($node, $scope); - return $serviceId !== \null && \array_key_exists($serviceId, $this->services) ? $this->services[$serviceId] : \null; + return $serviceId !== null && array_key_exists($serviceId, $this->services) ? $this->services[$serviceId] : null; } public static function getServiceIdFromNode(Node $node, Scope $scope): ?string @@ -74,10 +76,10 @@ public static function getServiceIdFromNode(Node $node, Scope $scope): ?string $left = self::getServiceIdFromNode($node->left, $scope); $right = self::getServiceIdFromNode($node->right, $scope); if ($left !== null && $right !== null) { - return \sprintf('%s%s', $left, $right); + return sprintf('%s%s', $left, $right); } } - return \null; + return null; } } diff --git a/src/Exception/XmlContainerNotExistsException.php b/src/Symfony/XmlContainerNotExistsException.php similarity index 52% rename from src/Exception/XmlContainerNotExistsException.php rename to src/Symfony/XmlContainerNotExistsException.php index 4fa4cba..7238cc6 100644 --- a/src/Exception/XmlContainerNotExistsException.php +++ b/src/Symfony/XmlContainerNotExistsException.php @@ -1,7 +1,6 @@ -args[0]) - && $methodCall->args[0] instanceof Arg - ) { + ): Type + { + if (isset($methodCall->args[0])) { $service = $this->serviceMap->getServiceFromNode($methodCall->args[0]->value, $scope); - if ($service !== \null && !$service['synthetic']) { + if ($service !== null && $service['synthetic'] === false) { return new ObjectType($service['class'] ?? $service['id']); } } diff --git a/src/Type/ControllerDynamicReturnTypeExtension.php b/src/Type/Symfony/ControllerDynamicReturnTypeExtension.php similarity index 76% rename from src/Type/ControllerDynamicReturnTypeExtension.php rename to src/Type/Symfony/ControllerDynamicReturnTypeExtension.php index ae50c8c..e79bb29 100644 --- a/src/Type/ControllerDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ControllerDynamicReturnTypeExtension.php @@ -1,23 +1,19 @@ -args[0]) - && $methodCall->args[0] instanceof Arg - ) { + ): Type + { + if (isset($methodCall->args[0])) { $service = $this->serviceMap->getServiceFromNode($methodCall->args[0]->value, $scope); - if ($service !== \null && !$service['synthetic']) { + if ($service !== null && $service['synthetic'] === false) { return new ObjectType($service['class'] ?? $service['id']); } } diff --git a/tests/Rules/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/ContainerInterfacePrivateServiceRuleTest.php deleted file mode 100644 index 88a7a5e..0000000 --- a/tests/Rules/ContainerInterfacePrivateServiceRuleTest.php +++ /dev/null @@ -1,29 +0,0 @@ -analyse([__DIR__ . '/data/ExampleController.php'], [ - [ - 'Service "private" is private.', - 14, - ], - ]); - } - -} diff --git a/tests/Rules/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/ContainerInterfaceUnknownServiceRuleTest.php deleted file mode 100644 index a396501..0000000 --- a/tests/Rules/ContainerInterfaceUnknownServiceRuleTest.php +++ /dev/null @@ -1,41 +0,0 @@ -analyse([__DIR__ . '/data/ExampleController.php'], [ - [ - 'Service "service.not.found" is not registered in the container.', - 20, - ], - [ - 'Service "Lookyman\PHPStan\Symfony\ServiceMap" is not registered in the container.', - 26, - ], - [ - 'Service "service.Lookyman\PHPStan\Symfony\ServiceMap" is not registered in the container.', - 38, - ], - [ - 'Service "Lookyman\PHPStan\Symfony\Rules\data\ExampleController" is not registered in the container.', - 44, - ], - ]); - } - -} diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php new file mode 100644 index 0000000..7b1e1e3 --- /dev/null +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php @@ -0,0 +1,34 @@ +analyse( + [__DIR__ . '/ExampleController.php'], + [ + [ + 'Service "private" is private.', + 13, + ], + ] + ); + } + +} diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php new file mode 100644 index 0000000..288e276 --- /dev/null +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -0,0 +1,46 @@ +analyse( + [__DIR__ . '/ExampleController.php'], + [ + [ + 'Service "service.not.found" is not registered in the container.', + 19, + ], + [ + 'Service "PHPStan\Symfony\ServiceMap" is not registered in the container.', + 25, + ], + [ + 'Service "service.PHPStan\Symfony\ServiceMap" is not registered in the container.', + 37, + ], + [ + 'Service "PHPStan\Rules\Symfony\ExampleController" is not registered in the container.', + 43, + ], + ] + ); + } + +} diff --git a/tests/Rules/data/ExampleController.php b/tests/Rules/Symfony/ExampleController.php similarity index 87% rename from tests/Rules/data/ExampleController.php rename to tests/Rules/Symfony/ExampleController.php index bb2776f..09b8375 100644 --- a/tests/Rules/data/ExampleController.php +++ b/tests/Rules/Symfony/ExampleController.php @@ -1,9 +1,8 @@ -createBroker(); $printer = new Standard(); - $serviceMap = new ServiceMap(__DIR__ . '/container.xml'); + $serviceMap = new ServiceMap(__DIR__ . '/data/container.xml'); self::assertSame($service, $serviceMap->getServiceFromNode( new String_($service['id']), - new Scope($this->createBroker(), $printer, new TypeSpecifier($printer), '') + new Scope($broker, $printer, new TypeSpecifier($printer, $broker, [], [], []), ScopeContext::create('')) )); } - /** - * @expectedException \Lookyman\PHPStan\Symfony\Exception\XmlContainerNotExistsException - */ public function testFileNotExists(): void { + $this->expectException(\PHPStan\Symfony\XmlContainerNotExistsException::class); new ServiceMap(__DIR__ . '/foo.xml'); } + /** + * @return mixed[] + */ public function getServiceFromNodeProvider(): array { return [ - [['id' => 'withoutClass', 'class' => \null, 'public' => \true, 'synthetic' => \false]], - [['id' => 'withClass', 'class' => 'Foo', 'public' => \true, 'synthetic' => \false]], - [['id' => 'withoutPublic', 'class' => 'Foo', 'public' => \true, 'synthetic' => \false]], - [['id' => 'publicNotFalse', 'class' => 'Foo', 'public' => \true, 'synthetic' => \false]], - [['id' => 'private', 'class' => 'Foo', 'public' => \false, 'synthetic' => \false]], - [['id' => 'synthetic', 'class' => 'Foo', 'public' => \true, 'synthetic' => \true]], - [['id' => 'alias', 'class' => 'Foo', 'public' => \true, 'synthetic' => \false]], + [['id' => 'withoutClass', 'class' => null, 'public' => true, 'synthetic' => false]], + [['id' => 'withClass', 'class' => 'Foo', 'public' => true, 'synthetic' => false]], + [['id' => 'withoutPublic', 'class' => 'Foo', 'public' => true, 'synthetic' => false]], + [['id' => 'publicNotFalse', 'class' => 'Foo', 'public' => true, 'synthetic' => false]], + [['id' => 'private', 'class' => 'Foo', 'public' => false, 'synthetic' => false]], + [['id' => 'synthetic', 'class' => 'Foo', 'public' => true, 'synthetic' => true]], + [['id' => 'alias', 'class' => 'Foo', 'public' => true, 'synthetic' => false]], ]; } @@ -58,14 +61,14 @@ public function testGetServiceIdFromNode(): void { $broker = $this->createBroker(); $printer = new Standard(); - $scope = new Scope($broker, $printer, new TypeSpecifier($printer), ''); + $scope = new Scope($broker, $printer, new TypeSpecifier($printer, $broker, [], [], []), ScopeContext::create('')); self::assertSame('foo', ServiceMap::getServiceIdFromNode(new String_('foo'), $scope)); self::assertSame('bar', ServiceMap::getServiceIdFromNode(new ClassConstFetch(new Name('bar'), ''), $scope)); self::assertSame('foobar', ServiceMap::getServiceIdFromNode(new Concat(new String_('foo'), new ClassConstFetch(new Name('bar'), '')), $scope)); $scope = $scope->enterClass($broker->getClass(ExampleController::class)); - self::assertEquals(ExampleController::class, ServiceMap::getServiceIdFromNode(new ClassConstFetch(new Name('static'), ExampleController::class), $scope)); + self::assertSame(ExampleController::class, ServiceMap::getServiceIdFromNode(new ClassConstFetch(new Name('static'), ExampleController::class), $scope)); } } diff --git a/tests/container.xml b/tests/Symfony/data/container.xml similarity index 100% rename from tests/container.xml rename to tests/Symfony/data/container.xml diff --git a/tests/Type/ContainerInterfaceDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/ContainerInterfaceDynamicReturnTypeExtensionTest.php similarity index 77% rename from tests/Type/ContainerInterfaceDynamicReturnTypeExtensionTest.php rename to tests/Type/Symfony/ContainerInterfaceDynamicReturnTypeExtensionTest.php index fc7c9d9..e840162 100644 --- a/tests/Type/ContainerInterfaceDynamicReturnTypeExtensionTest.php +++ b/tests/Type/Symfony/ContainerInterfaceDynamicReturnTypeExtensionTest.php @@ -1,22 +1,21 @@ -getClass()); + $extension = new ContainerInterfaceDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../../Symfony/data/container.xml')); + self::assertSame('Symfony\Component\DependencyInjection\ContainerInterface', $extension->getClass()); } public function testIsMethodSupported(): void @@ -43,17 +42,20 @@ public function testIsMethodSupported(): void $methodFoo = $this->createMock(MethodReflection::class); $methodFoo->expects(self::once())->method('getName')->willReturn('foo'); - $extension = new ContainerInterfaceDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../container.xml')); + $extension = new ContainerInterfaceDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../../Symfony/data/container.xml')); self::assertTrue($extension->isMethodSupported($methodGet)); self::assertFalse($extension->isMethodSupported($methodFoo)); } /** * @dataProvider getTypeFromMethodCallProvider + * @param MethodReflection $methodReflection + * @param MethodCall $methodCall + * @param Type $expectedType */ public function testGetTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Type $expectedType): void { - $extension = new ContainerInterfaceDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../container.xml')); + $extension = new ContainerInterfaceDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../../Symfony/data/container.xml')); $type = $extension->getTypeFromMethodCall( $methodReflection, $methodCall, @@ -62,6 +64,9 @@ public function testGetTypeFromMethodCall(MethodReflection $methodReflection, Me self::assertEquals($expectedType, $type); } + /** + * @return mixed[] + */ public function getTypeFromMethodCallProvider(): array { $notFoundType = $this->createMock(Type::class); diff --git a/tests/Type/ControllerDynamicReturnTypeExtensionTest.php b/tests/Type/Symfony/ControllerDynamicReturnTypeExtensionTest.php similarity index 78% rename from tests/Type/ControllerDynamicReturnTypeExtensionTest.php rename to tests/Type/Symfony/ControllerDynamicReturnTypeExtensionTest.php index 8833b1d..f314d83 100644 --- a/tests/Type/ControllerDynamicReturnTypeExtensionTest.php +++ b/tests/Type/Symfony/ControllerDynamicReturnTypeExtensionTest.php @@ -1,22 +1,21 @@ -getClass()); + $extension = new ControllerDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../../Symfony/data/container.xml')); + self::assertSame('Symfony\Bundle\FrameworkBundle\Controller\Controller', $extension->getClass()); } public function testIsMethodSupported(): void @@ -43,17 +42,20 @@ public function testIsMethodSupported(): void $methodFoo = $this->createMock(MethodReflection::class); $methodFoo->expects(self::once())->method('getName')->willReturn('foo'); - $extension = new ControllerDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../container.xml')); + $extension = new ControllerDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../../Symfony/data/container.xml')); self::assertTrue($extension->isMethodSupported($methodGet)); self::assertFalse($extension->isMethodSupported($methodFoo)); } /** * @dataProvider getTypeFromMethodCallProvider + * @param MethodReflection $methodReflection + * @param MethodCall $methodCall + * @param Type $expectedType */ public function testGetTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Type $expectedType): void { - $extension = new ControllerDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../container.xml')); + $extension = new ControllerDynamicReturnTypeExtension(new ServiceMap(__DIR__ . '/../../Symfony/data/container.xml')); $type = $extension->getTypeFromMethodCall( $methodReflection, $methodCall, @@ -62,6 +64,9 @@ public function testGetTypeFromMethodCall(MethodReflection $methodReflection, Me self::assertEquals($expectedType, $type); } + /** + * @return mixed[] + */ public function getTypeFromMethodCallProvider(): array { $notFoundType = $this->createMock(Type::class); diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..22e480d --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,3 @@ + + + + ../src + + + + + + +