diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e75020f..49fea76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,15 +11,12 @@ jobs: strategy: matrix: operating-system: ['ubuntu-latest'] - php-versions: ['7.4'] - magento: ['2.3.7-p2', '2.4.0', '2.4.1', '2.4.2', '2.4.2-p1', '2.4.2-p2', '2.4.3', '2.4.3-p1'] + php-versions: ['8.1'] + magento: ['2.4.4', '2.4.5'] coveralls: [ false ] include: - php-versions: '8.1' - magento: '2.4.4' - operating-system: 'ubuntu-latest' - - php-versions: '8.1' - magento: '2.4.5' + magento: '2.4.6' operating-system: 'ubuntu-latest' coveralls: true steps: @@ -37,45 +34,17 @@ jobs: - name: Install Composer dependencies run: composer install - - name: Install Magento 2.3.7-p2 - if: matrix.magento == '2.3.7-p2' - run: composer update --with-dependencies magento/framework:102.0.7-p2 laminas/laminas-code:3.4.1 - - - name: Install Magento 2.4.0 - if: matrix.magento == '2.4.0' - run: composer update --with-dependencies magento/framework:103.0.0 laminas/laminas-code:3.4.1 - - - name: Install Magento 2.4.1 - if: matrix.magento == '2.4.1' - run: composer update --with-dependencies magento/framework:103.0.1 laminas/laminas-code:3.4.1 - - - name: Install Magento 2.4.2 - if: matrix.magento == '2.4.2' - run: composer update --with-dependencies magento/framework:103.0.2 laminas/laminas-code:3.4.1 - - - name: Install Magento 2.4.2-p1 - if: matrix.magento == '2.4.2-p1' - run: composer update --with-dependencies magento/framework:103.0.2-p1 laminas/laminas-code:3.4.1 - - - name: Install Magento 2.4.2-p2 - if: matrix.magento == '2.4.2-p2' - run: composer update --with-dependencies magento/framework:103.0.2-p1 laminas/laminas-code:3.4.1 - - - name: Install Magento 2.4.3 - if: matrix.magento == '2.4.3' - run: composer update --with-dependencies magento/framework:103.0.3 laminas/laminas-code:3.5.1 - - - name: Install Magento 2.4.3-p1 - if: matrix.magento == '2.4.3-p1' - run: composer update --with-dependencies magento/framework:103.0.3-p1 laminas/laminas-code:3.5.1 - - name: Install Magento 2.4.4 if: matrix.magento == '2.4.4' - run: composer update --with-dependencies magento/framework:103.0.4 laminas/laminas-code:4.5.1 + run: composer update --with-dependencies magento/framework:103.0.4 laminas/laminas-code:4.5.1 symfony/yaml symfony/console madewithlove/license-checker - name: Install Magento 2.4.5 if: matrix.magento == '2.4.5' - run: composer update --with-dependencies magento/framework:103.0.5 laminas/laminas-code:4.5.2 roave/security-advisories + run: composer update --with-dependencies magento/framework:103.0.5 laminas/laminas-code:4.5.2 roave/security-advisories symfony/yaml symfony/console madewithlove/license-checker + + - name: Install Magento 2.4.6 + if: matrix.magento == '2.4.6' + run: composer update --with-dependencies magento/framework:103.0.6 laminas/laminas-code:4.10.0 roave/security-advisories symfony/yaml symfony/console madewithlove/license-checker - name: Composer license check run: composer check-license diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f94d6c..f2c3858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,137 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 0.42.0 + +### Added + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- [#340](https://github.com/bitExpert/phpstan-magento/pull/340) fix: error where scopeConfig proxy would get $scopeType = default + +## 0.41.0 + +### Added + +- [#338](https://github.com/bitExpert/phpstan-magento/pull/338) Feature/type for controller result factory + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 0.40.0 + +### Added + +- [#334](https://github.com/bitExpert/phpstan-magento/pull/334) PHPStan 2.0 compatibility +- [#332](https://github.com/bitExpert/phpstan-magento/pull/332) Add support for symfony/finder 7+ + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 0.32.0 + +### Added + +- [#326](https://github.com/bitExpert/phpstan-magento/pull/326) Update phpstan/phpstan requirement to ~1.12.0 + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + +## 0.31.0 + +### Added + +- [#318](https://github.com/bitExpert/phpstan-magento/pull/318) Update phpstan/phpstan requirement to ~1.11.1 +- [#308](https://github.com/bitExpert/phpstan-magento/pull/308) Update nikic/php-parser requirement to ^5.0.1 +- [#304](https://github.com/bitExpert/phpstan-magento/pull/304) Extend Neon validation logic for CI + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- [#306](https://github.com/bitExpert/phpstan-magento/pull/306) Fix typo + +## 0.30.1 + +### Added + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- [#302](https://github.com/bitExpert/phpstan-magento/pull/302) Fix problem with parametersSchema + +## 0.30.0 + +### Added + +- [#300](https://github.com/bitExpert/phpstan-magento/pull/300) Add rule "Disallow setTemplate() in Block classes" +- [#299](https://github.com/bitExpert/phpstan-magento/pull/299) Add rule "resource models should be used directly" +- [#298](https://github.com/bitExpert/phpstan-magento/pull/298) Add Magento 2.4.6 to CI pipeline + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 0.29.0 ### Added diff --git a/README.md b/README.md index 2ff17e6..990cf4e 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ You can use this PHPStan extension for both Magento module projects and Magento [](https://github.com/bitExpert/phpstan-magento/actions) [](https://coveralls.io/github/bitExpert/phpstan-magento?branch=master) [](https://packagist.org/packages/bitExpert/phpstan-magento/) +[](https://rheinneckar.social/@bitexpert) ## Requirements @@ -13,10 +14,10 @@ PHP: PHP 7.2 or higher Magento: Magento 2.3.0 or higher -PHPStan: PHPStan 1.10 +PHPStan: PHPStan 2.0 or higher If you are using a Magento version that requires an older version of PHPStan (e.g. 0.12.77), you need to manually upgrade it before -installing this extension. in your composer.json Change the PHPStan version to `~1.10` and run: +installing this extension. in your composer.json Change the PHPStan version to `~2.0` and run: ``` composer update phpstan/phpstan --with-all-dependencies @@ -30,7 +31,7 @@ composer.phar require --dev phpstan/extension-installer ``` <details> - <summary>Composer Allow-PLugins configuration</summary> + <summary>Composer Allow-Plugins configuration</summary> If you're using Composer >= 2.2.0 you have to allow the execution of composer plugins ([see allow-plugins section](https://getcomposer.org/doc/06-config.md#allow-plugins)) as follows: @@ -73,7 +74,9 @@ This PHPStan extension works for both Magento module projects and Magento applic - Extension attributes - PHPStan rules - Service contracts + - Resource Models should be used directly - Collections should be used directly via factory + - Do not use setTemplate in Block classes For a detailed overview, check the feature documentation [here](docs/features.md). diff --git a/bin/ci_neon_lint b/bin/ci_neon_lint index b4e5135..9044313 100755 --- a/bin/ci_neon_lint +++ b/bin/ci_neon_lint @@ -13,6 +13,31 @@ declare(strict_types=1); require_once __DIR__ . '/../vendor/autoload.php'; +function convertToNetteSchemaElement($entity) +{ + $schema = []; + + if ($entity instanceof \Nette\Neon\Entity) { + if (count($entity->attributes) === 0) { + return new \Nette\Schema\Elements\Type((string)$entity->value); + } + + foreach($entity->attributes as $key => $value) { + if (is_array($value)) { + return convertToNetteSchemaElement($value); + } else { + $schema[$key] = new \Nette\Schema\Elements\Type($value); + } + } + } else if (is_array($entity)) { + foreach ($entity as $key => $value) { + $schema[$key] = convertToNetteSchemaElement($value); + } + } + + return new \Nette\Schema\Elements\Structure($schema); +} + // this CLI script will lint all the .neon files in the repository $path = realpath(__DIR__ . '/../'); @@ -34,7 +59,24 @@ foreach ($it as $file) { throw new \RuntimeException(sprintf('Class "%s" does not exist', $value)); } }); - } catch (Nette\Neon\Exception $e) { + + if(isset($neon['parameters']) && isset($neon['parametersSchema'])) { + $schema = []; + foreach($neon['parametersSchema'] as $key => $item) { + $schema[$key] = convertToNetteSchemaElement($item); + } + $schema = new \Nette\Schema\Elements\Structure($schema); + + // remove phpstam parameters to not trigger a failed schema validation + unset($neon['parameters']['bootstrapFiles']); + + $processor = new \Nette\Schema\Processor(); + $processor->process($schema, $neon['parameters']); + } + } catch (\Nette\Schema\ValidationException $e) { + $success = false; + echo sprintf("Schema validation failed: %s", $e->getMessage())."\n"; + } catch (\Nette\Neon\Exception $e) { $success = false; $relPath = str_replace($path . DIRECTORY_SEPARATOR, '', $file->getRealPath()); echo sprintf('Failed parsing file "%s"', $relPath)."\n"; diff --git a/composer.json b/composer.json index f3b336e..853b830 100644 --- a/composer.json +++ b/composer.json @@ -22,9 +22,9 @@ "require": { "php": "^7.2.0 || ^8.1.0", "ext-dom": "*", - "laminas/laminas-code": "~3.3.0 || ~3.4.1 || ~3.5.1 || ^4.5", - "phpstan/phpstan": "~1.10.3", - "symfony/finder": "^3.0 || ^4.0 || ^5.0 || ^6.0" + "laminas/laminas-code": "~3.3.0 || ~3.4.1 || ~3.5.1 || ^4.5 || ^4.10", + "phpstan/phpstan": "^2.0", + "symfony/finder": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" }, "conflict": { "magento/framework": "<102.0.0" @@ -37,10 +37,10 @@ "magento/framework": ">=102.0.0", "mikey179/vfsstream": "^1.6.10", "nette/neon": "^3.3.3", - "nikic/php-parser": "^4.13.2", + "nikic/php-parser": "^5.3", "phpstan/extension-installer": "^1.1.0", - "phpstan/phpstan-phpunit": "^1.1.1", - "phpstan/phpstan-strict-rules": "^1.2.3", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^9.5.20", "squizlabs/php_codesniffer": "^3.6.2" }, diff --git a/docs/features.md b/docs/features.md index 2da9045..735ccb2 100644 --- a/docs/features.md +++ b/docs/features.md @@ -24,6 +24,9 @@ Additionally, a PHPStan rule checks that only `Magento\Framework\Data\Collection ### ObjectManager type hints A type extension is provided so that `Magento\Framework\App\ObjectManager` method calls do return the correct return type. +### ResultFactory type hints +Correct type is returned from `Magento\Framework\Controller\ResultFactory` based on passed parameter. + ## Magic method calls For some classes like `Magento\Framework\DataObject` or `Magento\Framework\Session\SessionManager` PHPStan logic is provided to be able to let the magic method calls return proper types. @@ -61,6 +64,17 @@ parameters: checkServiceContracts: false ``` +### Resource Models should be used directly + +Since Magento framework version 100.1.0 it is no longer recommended to use `\Magento\Framework\Model\AbtractModel::getResource()` for retrieving the model resource. Use [service contracts](https://devdocs.magento.com/guides/v2.4/extension-dev-guide/service-contracts/service-contracts.html) instead. + +To disable this rule add the following code to your `phpstan.neon` configuration file: +```neon +parameters: + magento: + checkResourceModelsUsedDirectly: false +``` + ### Collections should be used directly via factory Since Magento framework version 101.0.0 Collections should be used directly via factory instead of calling @@ -72,3 +86,14 @@ parameters: magento: checkCollectionViaFactory: false ``` + +### Do not use setTemplate in Block classes + +As the [ExtDN](https://github.com/extdn/extdn-phpcs/blob/master/Extdn/Sniffs/Blocks/SetTemplateInBlockSniff.md) folks have put it: Setters are deprecated in Block classes, because constructor arguments should be preferred instead. Any call to `$this->setTemplate()` after calling upon the parent constructor would undo the configuration via XML layout. + +To disable this rule add the following code to your `phpstan.neon` configuration file: +```neon +parameters: + extdn: + setTemplateDisallowedForBlockClasses: false +``` diff --git a/extension.neon b/extension.neon index ed89b52..aa90e9d 100644 --- a/extension.neon +++ b/extension.neon @@ -2,22 +2,32 @@ parameters: magento: checkCollectionViaFactory: true checkServiceContracts: true + checkResourceModelsUsedDirectly: true magentoRoot: %currentWorkingDirectory% + extdn: + setTemplateDisallowedForBlockClasses: true bootstrapFiles: - magento-autoloader.php parametersSchema: magento: structure([ checkCollectionViaFactory: bool() checkServiceContracts: bool() + checkResourceModelsUsedDirectly: bool() magentoRoot: string() ]) + extdn: structure([ + setTemplateDisallowedForBlockClasses: bool() + ]) conditionalTags: bitExpert\PHPStan\Magento\Rules\AbstractModelRetrieveCollectionViaFactoryRule: phpstan.rules.rule: %magento.checkCollectionViaFactory% bitExpert\PHPStan\Magento\Rules\AbstractModelUseServiceContractRule: phpstan.rules.rule: %magento.checkServiceContracts% - + bitExpert\PHPStan\Magento\Rules\ResourceModelsShouldBeUsedDirectlyRule: + phpstan.rules.rule: %magento.checkResourceModelsUsedDirectly% + bitExpert\PHPStan\Magento\Rules\SetTemplateDisallowedForBlockRule: + phpstan.rules.rule: %extdn.setTemplateDisallowedForBlockClasses% services: - class: bitExpert\PHPStan\Magento\Type\ObjectManagerDynamicReturnTypeExtension @@ -27,6 +37,10 @@ services: class: bitExpert\PHPStan\Magento\Type\TestFrameworkObjectManagerDynamicReturnTypeExtension tags: - phpstan.broker.dynamicMethodReturnTypeExtension + - + class: bitExpert\PHPStan\Magento\Type\ControllerResultFactoryReturnTypeExtension + tags: + - phpstan.broker.dynamicMethodReturnTypeExtension - class: bitExpert\PHPStan\Magento\Reflection\Framework\Session\SessionManagerMagicMethodReflectionExtension tags: @@ -43,6 +57,10 @@ services: class: bitExpert\PHPStan\Magento\Rules\AbstractModelRetrieveCollectionViaFactoryRule - class: bitExpert\PHPStan\Magento\Rules\AbstractModelUseServiceContractRule + - + class: bitExpert\PHPStan\Magento\Rules\ResourceModelsShouldBeUsedDirectlyRule + - + class: bitExpert\PHPStan\Magento\Rules\SetTemplateDisallowedForBlockRule fileCacheStorage: class: bitExpert\PHPStan\Magento\Autoload\Cache\FileCacheStorage arguments: diff --git a/phpstan.neon b/phpstan.neon index 0f85674..c94cc2b 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -28,6 +28,15 @@ parameters: - message: '~is not covered by backward compatibility promise.~' path: src/bitExpert/PHPStan/Magento/Reflection/AbstractMagicMethodReflectionExtension.php + - + message: '~PHPDoc tag @var assumes the expression with type~' + path: 'src/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRule.php' + - + message: '~PHPDoc tag @var assumes the expression with type~' + path: 'src/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtension.php' + - + message: '~Function is_subclass_of\(\) is a runtime reflection concept that might not work in PHPStan~' + path: 'src/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtension.php' - message: '~is not covered by backward compatibility promise.~' path: tests/bitExpert/PHPStan/Magento/Autoload/FactoryAutoloaderUnitTest.php @@ -41,17 +50,23 @@ parameters: message: '~is not covered by backward compatibility promise.~' path: tests/bitExpert/PHPStan/Magento/Autoload/ExtensionAutoloaderUnitTest.php - - message: '~Parameter #1 $argument of class ReflectionClass constructor expects class-string<MyUncachedExtension>|MyUncachedExtension, string given~' + message: '~Parameter #1 \$argument of class ReflectionClass constructor expects class-string<MyUncachedExtension>|MyUncachedExtension, string given~' path: tests/bitExpert/PHPStan/Magento/Autoload/ExtensionAutoloaderUnitTest.php - message: '~is not covered by backward compatibility promise.~' path: tests/bitExpert/PHPStan/Magento/Autoload/ExtensionInterfaceAutoloaderUnitTest.php - - message: '~Parameter #1 $argument of class ReflectionClass constructor expects class-string<UncachedExtensionInterface>|UncachedExtensionInterface, string given~' + message: '~Parameter #1 \$argument of class ReflectionClass constructor expects class-string<UncachedExtensionInterface>|UncachedExtensionInterface, string given~' path: tests/bitExpert/PHPStan/Magento/Autoload/ExtensionInterfaceAutoloaderUnitTest.php - message: '~bitExpert\\PHPStan\\Magento\\Rules\\Helper\\SampleModel::__construct\(\) does not call parent constructor~' path: tests/bitExpert/PHPStan/Magento/Rules/Helper/SampleModel.php + - + message: '~bitExpert\\PHPStan\\Magento\\Rules\\Helper\\SampleBlock::__construct\(\) does not call parent constructor~' + path: tests/bitExpert/PHPStan/Magento/Rules/Helper/SampleBlock.php - message: '~Call to static method PHPUnit\\Framework\\Assert::assertInstanceOf~' path: tests/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest.php + - + message: '~PHPDoc tag @var assumes the expression with type~' + path: tests/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest.php diff --git a/src/bitExpert/PHPStan/Magento/Autoload/DataProvider/ClassLoaderProvider.php b/src/bitExpert/PHPStan/Magento/Autoload/DataProvider/ClassLoaderProvider.php index 8142429..152464e 100644 --- a/src/bitExpert/PHPStan/Magento/Autoload/DataProvider/ClassLoaderProvider.php +++ b/src/bitExpert/PHPStan/Magento/Autoload/DataProvider/ClassLoaderProvider.php @@ -30,6 +30,7 @@ public function __construct(string $magentoRoot) $this->composer = new ClassLoader($magentoRoot . '/vendor'); $autoloadFile = $magentoRoot . '/vendor/composer/autoload_namespaces.php'; if (is_file($autoloadFile)) { + /** @var array<string, string> $map */ $map = require $autoloadFile; foreach ($map as $namespace => $path) { $this->composer->set($namespace, $path); @@ -38,6 +39,7 @@ public function __construct(string $magentoRoot) $autoloadFile = $magentoRoot . '/vendor/composer/autoload_psr4.php'; if (is_file($autoloadFile)) { + /** @var array<string, string> $map */ $map = require $autoloadFile; foreach ($map as $namespace => $path) { $this->composer->setPsr4($namespace, $path); @@ -46,6 +48,7 @@ public function __construct(string $magentoRoot) $autoloadFile = $magentoRoot . '/vendor/composer/autoload_classmap.php'; if (is_file($autoloadFile)) { + /** @var ?array<string, string> $classMap */ $classMap = require $autoloadFile; if (is_array($classMap)) { $this->composer->addClassMap($classMap); diff --git a/src/bitExpert/PHPStan/Magento/Autoload/FactoryAutoloader.php b/src/bitExpert/PHPStan/Magento/Autoload/FactoryAutoloader.php index 6eee7e4..51630c2 100644 --- a/src/bitExpert/PHPStan/Magento/Autoload/FactoryAutoloader.php +++ b/src/bitExpert/PHPStan/Magento/Autoload/FactoryAutoloader.php @@ -69,7 +69,7 @@ protected function getFileContents(string $class): string $namespace = explode('\\', ltrim($class, '\\')); /** @var string $factoryClassname */ $factoryClassname = array_pop($namespace); - $originalClassname = preg_replace('#Factory$#', '', $factoryClassname); + $originalClassname = (string) preg_replace('#Factory$#', '', $factoryClassname); $namespace = implode('\\', $namespace); $template = "<?php\n"; diff --git a/src/bitExpert/PHPStan/Magento/Autoload/ProxyAutoloader.php b/src/bitExpert/PHPStan/Magento/Autoload/ProxyAutoloader.php index 4cee192..c4f0131 100644 --- a/src/bitExpert/PHPStan/Magento/Autoload/ProxyAutoloader.php +++ b/src/bitExpert/PHPStan/Magento/Autoload/ProxyAutoloader.php @@ -110,7 +110,9 @@ protected function getFileContents(string $class): string $defaultValue = ' = false'; break; default: - $defaultValue = ' = ' . $parameter->getDefaultValue(); + if (is_string($parameter->getDefaultValue())) { + $defaultValue = ' = \'' . $parameter->getDefaultValue() . '\''; + } break; } } diff --git a/src/bitExpert/PHPStan/Magento/Reflection/MagicMethodReflection.php b/src/bitExpert/PHPStan/Magento/Reflection/MagicMethodReflection.php index aea4596..7bfee93 100644 --- a/src/bitExpert/PHPStan/Magento/Reflection/MagicMethodReflection.php +++ b/src/bitExpert/PHPStan/Magento/Reflection/MagicMethodReflection.php @@ -28,7 +28,7 @@ class MagicMethodReflection implements MethodReflection */ private $declaringClass; /** - * @var ParametersAcceptor[] + * @var list<ParametersAcceptor> */ private $variants; @@ -37,7 +37,7 @@ class MagicMethodReflection implements MethodReflection * * @param string $name * @param ClassReflection $declaringClass - * @param ParametersAcceptor[] $variants + * @param list<ParametersAcceptor> $variants */ public function __construct(string $name, ClassReflection $declaringClass, array $variants = []) { @@ -76,9 +76,6 @@ public function getPrototype(): ClassMemberReflection return $this; } - /** - * @return ParametersAcceptor[] - */ public function getVariants(): array { return $this->variants; diff --git a/src/bitExpert/PHPStan/Magento/Rules/AbstractModelRetrieveCollectionViaFactoryRule.php b/src/bitExpert/PHPStan/Magento/Rules/AbstractModelRetrieveCollectionViaFactoryRule.php index fb81938..9a1c3c5 100644 --- a/src/bitExpert/PHPStan/Magento/Rules/AbstractModelRetrieveCollectionViaFactoryRule.php +++ b/src/bitExpert/PHPStan/Magento/Rules/AbstractModelRetrieveCollectionViaFactoryRule.php @@ -16,7 +16,8 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; +use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ObjectType; use PHPStan\Type\VerbosityLevel; @@ -36,18 +37,8 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @param Node $node - * @param Scope $scope - * @return (string|\PHPStan\Rules\RuleError)[] errors - * @throws ShouldNotHappenException - */ public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall) { - throw new ShouldNotHappenException(); - } - if (!$node->name instanceof Node\Identifier) { return []; } @@ -63,11 +54,15 @@ public function processNode(Node $node, Scope $scope): array } return [ - sprintf( - 'Collections should be used directly via factory, not via %s::%s() method', - $type->describe(VerbosityLevel::typeOnly()), - $node->name->name + RuleErrorBuilder::message( + sprintf( + 'Collections should be used directly via factory, not via %s::%s() method', + $type->describe(VerbosityLevel::typeOnly()), + $node->name->name + ) ) + ->identifier('bitExpertMagento.abstractModelRetrieveCollectionViaFactory') + ->build() ]; } } diff --git a/src/bitExpert/PHPStan/Magento/Rules/AbstractModelUseServiceContractRule.php b/src/bitExpert/PHPStan/Magento/Rules/AbstractModelUseServiceContractRule.php index 17c47f9..0a0b620 100644 --- a/src/bitExpert/PHPStan/Magento/Rules/AbstractModelUseServiceContractRule.php +++ b/src/bitExpert/PHPStan/Magento/Rules/AbstractModelUseServiceContractRule.php @@ -16,7 +16,8 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; +use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ObjectType; use PHPStan\Type\VerbosityLevel; @@ -36,18 +37,8 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @param Node $node - * @param Scope $scope - * @return (string|\PHPStan\Rules\RuleError)[] errors - * @throws ShouldNotHappenException - */ public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall) { - throw new ShouldNotHappenException(); - } - if (!$node->name instanceof Node\Identifier) { return []; } @@ -63,11 +54,15 @@ public function processNode(Node $node, Scope $scope): array } return [ - sprintf( - 'Use service contracts to persist entities in favour of %s::%s() method', - $type->describe(VerbosityLevel::typeOnly()), - $node->name->name + RuleErrorBuilder::message( + sprintf( + 'Use service contracts to persist entities in favour of %s::%s() method', + $type->describe(VerbosityLevel::typeOnly()), + $node->name->name + ) ) + ->identifier('bitExpertMagento.abstractModelUseServiceContract') + ->build() ]; } } diff --git a/src/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRule.php b/src/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRule.php index 82563a7..1135929 100644 --- a/src/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRule.php +++ b/src/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRule.php @@ -17,7 +17,8 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; +use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ErrorType; use PHPStan\Type\ObjectType; @@ -38,18 +39,8 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @param Node $node - * @param Scope $scope - * @return (string|\PHPStan\Rules\RuleError)[] errors - * @throws ShouldNotHappenException - */ public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall) { - throw new ShouldNotHappenException(); - } - if (!$node->name instanceof Node\Identifier) { return []; } @@ -78,11 +69,16 @@ public function processNode(Node $node, Scope $scope): array $args = $node->args; /** @var ConstantStringType $argType */ $argType = $scope->getType($args[0]->value); + return [ - sprintf( - '%s does not extend \Magento\Framework\Data\Collection as required!', - $argType->getValue() + RuleErrorBuilder::message( + sprintf( + '%s does not extend \Magento\Framework\Data\Collection as required!', + $argType->getValue() + ) ) + ->identifier('bitExpertMagento.getCollectionMockMethodNeedsCollectionSubclass') + ->build() ]; } } diff --git a/src/bitExpert/PHPStan/Magento/Rules/ResourceModelsShouldBeUsedDirectlyRule.php b/src/bitExpert/PHPStan/Magento/Rules/ResourceModelsShouldBeUsedDirectlyRule.php new file mode 100644 index 0000000..4252629 --- /dev/null +++ b/src/bitExpert/PHPStan/Magento/Rules/ResourceModelsShouldBeUsedDirectlyRule.php @@ -0,0 +1,68 @@ +<?php + +/* + * This file is part of the phpstan-magento package. + * + * (c) bitExpert AG + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace bitExpert\PHPStan\Magento\Rules; + +use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PHPStan\Analyser\Scope; +use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\ObjectType; +use PHPStan\Type\VerbosityLevel; + +/** + * Since 100.1.0 resource models should be used directly. + * + * @implements Rule<MethodCall> + */ +class ResourceModelsShouldBeUsedDirectlyRule implements Rule +{ + /** + * @phpstan-return class-string<MethodCall> + * @return string + */ + public function getNodeType(): string + { + return MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Node\Identifier) { + return []; + } + + if (!in_array($node->name->name, ['getResource', '_getResource'], true)) { + return []; + } + + $type = $scope->getType($node->var); + $isAbstractModelType = (new ObjectType('Magento\Framework\Model\AbstractModel'))->isSuperTypeOf($type); + if (!$isAbstractModelType->yes()) { + return []; + } + + return [ + RuleErrorBuilder::message( + sprintf( + '%s::%s() is deprecated. Use Resource Models directly', + $type->describe(VerbosityLevel::typeOnly()), + $node->name->name + ) + ) + ->identifier('bitExpertMagento.resourceModelsShouldBeUsedDirectly') + ->build() + ]; + } +} diff --git a/src/bitExpert/PHPStan/Magento/Rules/SetTemplateDisallowedForBlockRule.php b/src/bitExpert/PHPStan/Magento/Rules/SetTemplateDisallowedForBlockRule.php new file mode 100644 index 0000000..b986700 --- /dev/null +++ b/src/bitExpert/PHPStan/Magento/Rules/SetTemplateDisallowedForBlockRule.php @@ -0,0 +1,69 @@ +<?php + +/* + * This file is part of the phpstan-magento package. + * + * (c) bitExpert AG + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace bitExpert\PHPStan\Magento\Rules; + +use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PHPStan\Analyser\Scope; +use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\ObjectType; +use PHPStan\Type\VerbosityLevel; + +/** + * Do not use setTemplate in Block classes, see + * https://github.com/extdn/extdn-phpcs/blob/master/Extdn/Sniffs/Blocks/SetTemplateInBlockSniff.md + * + * @implements Rule<MethodCall> + */ +class SetTemplateDisallowedForBlockRule implements Rule +{ + /** + * @phpstan-return class-string<MethodCall> + * @return string + */ + public function getNodeType(): string + { + return MethodCall::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$node->name instanceof Node\Identifier) { + return []; + } + + if ($node->name->name !== 'setTemplate') { + return []; + } + + $type = $scope->getType($node->var); + $isAbstractModelType = (new ObjectType('Magento\Framework\View\Element\Template'))->isSuperTypeOf($type); + if (!$isAbstractModelType->yes()) { + return []; + } + + return [ + RuleErrorBuilder::message( + sprintf( + 'Setter methods like %s::%s() are deprecated in Block classes, use constructor arguments instead', + $type->describe(VerbosityLevel::typeOnly()), + $node->name->name + ) + ) + ->identifier('bitExpertMagento.setTemplateDisallowedForBlock') + ->build() + ]; + } +} diff --git a/src/bitExpert/PHPStan/Magento/Type/ControllerResultFactoryReturnTypeExtension.php b/src/bitExpert/PHPStan/Magento/Type/ControllerResultFactoryReturnTypeExtension.php new file mode 100644 index 0000000..6047375 --- /dev/null +++ b/src/bitExpert/PHPStan/Magento/Type/ControllerResultFactoryReturnTypeExtension.php @@ -0,0 +1,66 @@ +<?php + +/* + * This file is part of the phpstan-magento package. + * + * (c) bitExpert AG + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace bitExpert\PHPStan\Magento\Type; + +use PhpParser\Node\Expr\ClassConstFetch; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PHPStan\Analyser\Scope; +use PHPStan\Reflection\MethodReflection; +use PHPStan\Type\DynamicMethodReturnTypeExtension; +use PHPStan\Type\ObjectType; +use PHPStan\Type\Type; + +/** + * \Magento\Framework\Controller\ResultFactory returns result type based on first parameter + */ +class ControllerResultFactoryReturnTypeExtension implements DynamicMethodReturnTypeExtension +{ + /** @see \Magento\Framework\Controller\ResultFactory */ + private const TYPE_MAP = [ + 'TYPE_JSON' => \Magento\Framework\Controller\Result\Json::class, + 'TYPE_RAW' => \Magento\Framework\Controller\Result\Raw::class, + 'TYPE_REDIRECT' => \Magento\Framework\Controller\Result\Redirect::class, + 'TYPE_FORWARD' => \Magento\Framework\Controller\Result\Forward::class, + 'TYPE_LAYOUT' => \Magento\Framework\View\Result\Layout::class, + 'TYPE_PAGE' => \Magento\Framework\View\Result\Page::class, + ]; + + public function getClass(): string + { + return \Magento\Framework\Controller\ResultFactory::class; + } + + public function isMethodSupported(MethodReflection $methodReflection): bool + { + return $methodReflection->getName() === 'create'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): ?ObjectType { + $class = null; + if (\count($methodCall->getArgs()) > 0) { + $arg = $methodCall->getArgs()[0]; + $expr = $arg->value; + + if ($expr instanceof ClassConstFetch && $expr->name instanceof Identifier) { + $class = self::TYPE_MAP[$expr->name->toString()] ?? null; + } + } + + return $class !== null ? new ObjectType($class) : null; + } +} diff --git a/src/bitExpert/PHPStan/Magento/Type/ObjectManagerDynamicReturnTypeExtension.php b/src/bitExpert/PHPStan/Magento/Type/ObjectManagerDynamicReturnTypeExtension.php index 96d4692..84e257f 100644 --- a/src/bitExpert/PHPStan/Magento/Type/ObjectManagerDynamicReturnTypeExtension.php +++ b/src/bitExpert/PHPStan/Magento/Type/ObjectManagerDynamicReturnTypeExtension.php @@ -24,9 +24,6 @@ class ObjectManagerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** - * @return string - */ public function getClass(): string { return 'Magento\Framework\App\ObjectManager'; @@ -64,9 +61,15 @@ public function getTypeFromMethodCall( /** @var \PhpParser\Node\Arg[] $args */ $args = $methodCall->args; $argType = $scope->getType($args[0]->value); - if (!$argType instanceof ConstantStringType) { + if ($argType->getConstantStrings() === []) { return $mixedType; } - return TypeCombinator::addNull(new ObjectType($argType->getValue())); + + $types = []; + foreach ($argType->getConstantStrings() as $constantString) { + $types[] = TypeCombinator::addNull(new ObjectType($constantString->getValue())); + } + + return TypeCombinator::union(...$types); } } diff --git a/src/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtension.php b/src/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtension.php index 0a5d083..8827e41 100644 --- a/src/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtension.php +++ b/src/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtension.php @@ -28,9 +28,6 @@ class TestFrameworkObjectManagerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** - * @return string - */ public function getClass(): string { return 'Magento\Framework\TestFramework\Unit\Helper\ObjectManager'; @@ -92,10 +89,16 @@ private function getTypeForGetObjectMethodCall( /** @var \PhpParser\Node\Arg[] $args */ $args = $methodCall->args; $argType = $scope->getType($args[0]->value); - if (!$argType instanceof ConstantStringType) { + if ($argType->getConstantStrings() === []) { return $mixedType; } - return TypeCombinator::addNull(new ObjectType($argType->getValue())); + + $types = []; + foreach ($argType->getConstantStrings() as $constantString) { + $types[] = TypeCombinator::addNull(new ObjectType($constantString->getValue())); + } + + return TypeCombinator::union(...$types); } /** diff --git a/tests/bitExpert/PHPStan/Magento/Autoload/ExtensionAutoloaderUnitTest.php b/tests/bitExpert/PHPStan/Magento/Autoload/ExtensionAutoloaderUnitTest.php index cd1bca7..8c4a93b 100644 --- a/tests/bitExpert/PHPStan/Magento/Autoload/ExtensionAutoloaderUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Autoload/ExtensionAutoloaderUnitTest.php @@ -7,20 +7,25 @@ use bitExpert\PHPStan\Magento\Autoload\DataProvider\ExtensionAttributeDataProvider; use org\bovigo\vfs\vfsStream; use PHPStan\Cache\Cache; +use PHPStan\Cache\CacheStorage; use PHPUnit\Framework\TestCase; class ExtensionAutoloaderUnitTest extends TestCase { /** - * @var Cache|\PHPUnit\Framework\MockObject\MockObject + * @var Cache */ private $cache; /** - * @var ClassLoaderProvider|\PHPUnit\Framework\MockObject\MockObject + * @var CacheStorage&\PHPUnit\Framework\MockObject\MockObject + */ + private $cacheStorage; + /** + * @var ClassLoaderProvider&\PHPUnit\Framework\MockObject\MockObject */ private $classLoader; /** - * @var ExtensionAttributeDataProvider|\PHPUnit\Framework\MockObject\MockObject + * @var ExtensionAttributeDataProvider&\PHPUnit\Framework\MockObject\MockObject */ private $extAttrDataProvider; /** @@ -30,7 +35,8 @@ class ExtensionAutoloaderUnitTest extends TestCase protected function setUp(): void { - $this->cache = $this->createMock(Cache::class); + $this->cacheStorage = $this->createMock(CacheStorage::class); + $this->cache = new Cache($this->cacheStorage); $this->classLoader = $this->createMock(ClassLoaderProvider::class); $this->extAttrDataProvider = $this->createMock(ExtensionAttributeDataProvider::class); $this->autoloader = new ExtensionAutoloader( @@ -47,7 +53,7 @@ public function autoloaderIgnoresClassesWithoutExtensionInterfacePostfix(): void { $this->classLoader->expects(self::never()) ->method('findFile'); - $this->cache->expects(self::never()) + $this->cacheStorage->expects(self::never()) ->method('load'); $this->autoloader->autoload('SomeClass'); @@ -61,7 +67,7 @@ public function autoloaderPrefersLocalFile(): void $this->classLoader->expects(self::once()) ->method('findFile') ->willReturn(__DIR__ . '/HelperExtension.php'); - $this->cache->expects(self::never()) + $this->cacheStorage->expects(self::never()) ->method('load'); $this->autoloader->autoload(HelperExtension::class); @@ -77,11 +83,11 @@ public function autoloaderUsesCachedFileWhenFound(): void $this->classLoader->expects(self::once()) ->method('findFile') ->willReturn(false); - $this->cache->expects(self::once()) + $this->cacheStorage->expects(self::once()) ->method('load') ->willReturn(__DIR__ . '/HelperExtension.php'); - $this->cache->expects(self::never()) + $this->cacheStorage->expects(self::never()) ->method('save'); $this->autoloader->autoload(HelperExtension::class); diff --git a/tests/bitExpert/PHPStan/Magento/Autoload/ExtensionInterfaceAutoloaderUnitTest.php b/tests/bitExpert/PHPStan/Magento/Autoload/ExtensionInterfaceAutoloaderUnitTest.php index f62ba1a..2f0e750 100644 --- a/tests/bitExpert/PHPStan/Magento/Autoload/ExtensionInterfaceAutoloaderUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Autoload/ExtensionInterfaceAutoloaderUnitTest.php @@ -8,20 +8,25 @@ use InvalidArgumentException; use org\bovigo\vfs\vfsStream; use PHPStan\Cache\Cache; +use PHPStan\Cache\CacheStorage; use PHPUnit\Framework\TestCase; class ExtensionInterfaceAutoloaderUnitTest extends TestCase { /** - * @var Cache|\PHPUnit\Framework\MockObject\MockObject + * @var Cache */ private $cache; /** - * @var ExtensionAttributeDataProvider|\PHPUnit\Framework\MockObject\MockObject + * @var CacheStorage&\PHPUnit\Framework\MockObject\MockObject + */ + private $cacheStorage; + /** + * @var ExtensionAttributeDataProvider&\PHPUnit\Framework\MockObject\MockObject */ private $extAttrDataProvider; /** - * @var ClassLoaderProvider|\PHPUnit\Framework\MockObject\MockObject + * @var ClassLoaderProvider&\PHPUnit\Framework\MockObject\MockObject */ private $classLoader; /** @@ -31,7 +36,8 @@ class ExtensionInterfaceAutoloaderUnitTest extends TestCase protected function setUp(): void { - $this->cache = $this->createMock(Cache::class); + $this->cacheStorage = $this->createMock(CacheStorage::class); + $this->cache = new Cache($this->cacheStorage); $this->classLoader = $this->createMock(ClassLoaderProvider::class); $this->extAttrDataProvider = $this->createMock(ExtensionAttributeDataProvider::class); $this->autoloader = new ExtensionInterfaceAutoloader( @@ -48,7 +54,7 @@ public function autoloaderIgnoresClassesWithoutExtensionInterfacePostfix(): void { $this->classLoader->expects(self::never()) ->method('findFile'); - $this->cache->expects(self::never()) + $this->cacheStorage->expects(self::never()) ->method('load'); $this->autoloader->autoload('SomeClass'); @@ -62,7 +68,7 @@ public function autoloaderPrefersLocalFile(): void $this->classLoader->expects(self::once()) ->method('findFile') ->willReturn(__DIR__ . '/HelperExtensionInterface.php'); - $this->cache->expects(self::never()) + $this->cacheStorage->expects(self::never()) ->method('load'); $this->autoloader->autoload(HelperExtensionInterface::class); @@ -78,11 +84,11 @@ public function autoloaderUsesCachedFileWhenFound(): void $this->classLoader->expects(self::once()) ->method('findFile') ->willReturn(false); - $this->cache->expects(self::once()) + $this->cacheStorage->expects(self::once()) ->method('load') ->willReturn(__DIR__ . '/HelperExtensionInterface.php'); - $this->cache->expects(self::never()) + $this->cacheStorage->expects(self::never()) ->method('save'); $this->autoloader->autoload(HelperExtensionInterface::class); @@ -103,7 +109,7 @@ public function autoloadDoesNotGenerateInterfaceWhenNoAttributesExist(): void $this->classLoader->expects(self::once()) ->method('findFile') ->willReturn(false); - $this->cache->expects(self::once()) + $this->cacheStorage->expects(self::once()) ->method('load') ->willReturn(null); diff --git a/tests/bitExpert/PHPStan/Magento/Autoload/RegistrationUnitTest.php b/tests/bitExpert/PHPStan/Magento/Autoload/RegistrationUnitTest.php index ca86cb4..c14eea9 100644 --- a/tests/bitExpert/PHPStan/Magento/Autoload/RegistrationUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Autoload/RegistrationUnitTest.php @@ -21,20 +21,19 @@ class RegistrationUnitTest extends TestCase { /** * @test - * @dataProvider provideAutoloaders() + * @dataProvider provideAutoloaders */ public function autoloadersCanRegisterAndUnregister(Autoloader $autoloader): void { - /** @var array<callable> $initialAutoloadFunctions */ $initialAutoloadFunctions = spl_autoload_functions(); $autoloader->register(); - /** @var array<callable> $registerAutoloadFunctions */ + $registerAutoloadFunctions = spl_autoload_functions(); static::assertCount(count($initialAutoloadFunctions) + 1, $registerAutoloadFunctions); $autoloader->unregister(); - /** @var array<callable> $unregisterAutoloadFunctions */ + $unregisterAutoloadFunctions = spl_autoload_functions(); static::assertCount(count($initialAutoloadFunctions), $unregisterAutoloadFunctions); } diff --git a/tests/bitExpert/PHPStan/Magento/Reflection/Framework/DataObjectHelper.php b/tests/bitExpert/PHPStan/Magento/Reflection/Framework/DataObjectHelper.php new file mode 100644 index 0000000..97e9a20 --- /dev/null +++ b/tests/bitExpert/PHPStan/Magento/Reflection/Framework/DataObjectHelper.php @@ -0,0 +1,17 @@ +<?php + +/* + * This file is part of the phpstan-magento package. + * + * (c) bitExpert AG + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace bitExpert\PHPStan\Magento\Reflection\Framework; + +class DataObjectHelper extends \Magento\Framework\DataObject +{ +} diff --git a/tests/bitExpert/PHPStan/Magento/Reflection/Framework/DataObjectMagicMethodReflectionExtensionUnitTest.php b/tests/bitExpert/PHPStan/Magento/Reflection/Framework/DataObjectMagicMethodReflectionExtensionUnitTest.php index 46c270f..c3d61f5 100644 --- a/tests/bitExpert/PHPStan/Magento/Reflection/Framework/DataObjectMagicMethodReflectionExtensionUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Reflection/Framework/DataObjectMagicMethodReflectionExtensionUnitTest.php @@ -13,15 +13,16 @@ namespace bitExpert\PHPStan\Magento\Reflection\Framework; use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\ReflectionProvider; use PHPStan\ShouldNotHappenException; +use PHPStan\Testing\PHPStanTestCase; use PHPStan\Type\BooleanType; use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\StringType; use PHPStan\Type\UnionType; -use PHPUnit\Framework\TestCase; -class DataObjectMagicMethodReflectionExtensionUnitTest extends TestCase +class DataObjectMagicMethodReflectionExtensionUnitTest extends PHPStanTestCase { /** * @var DataObjectMagicMethodReflectionExtension @@ -29,14 +30,17 @@ class DataObjectMagicMethodReflectionExtensionUnitTest extends TestCase private $extension; /** - * @var ClassReflection|\PHPUnit\Framework\MockObject\MockObject + * @var ClassReflection */ private $classReflection; protected function setUp(): void { + /** @var ReflectionProvider $reflectionProvider */ + $reflectionProvider = $this->getContainer()->getService('reflectionProvider'); + $this->classReflection = $reflectionProvider->getClass(DataObjectHelper::class); + $this->extension = new DataObjectMagicMethodReflectionExtension(); - $this->classReflection = $this->createMock(ClassReflection::class); } /** @@ -170,13 +174,6 @@ public function throwsExceptionForUnknownMethodNames(): void */ public function hasMethodDetectsDataObjectClass(string $method, bool $expectedResult): void { - $this->classReflection->expects(self::once()) - ->method('getParentClassesNames') - ->willReturn([]); - $this->classReflection->expects(self::once()) - ->method('getName') - ->willReturn('Magento\Framework\DataObject'); - self::assertSame($expectedResult, $this->extension->hasMethod($this->classReflection, $method)); } @@ -188,13 +185,6 @@ public function hasMethodDetectsDataObjectClass(string $method, bool $expectedRe */ public function hasMethodDetectsDataObjectParentClass(string $method, bool $expectedResult): void { - $this->classReflection->expects(self::once()) - ->method('getParentClassesNames') - ->willReturn(['Magento\Framework\DataObject']); - $this->classReflection->expects(self::once()) - ->method('getName') - ->willReturn('Magento\Framework\Shell\Response'); - self::assertSame($expectedResult, $this->extension->hasMethod($this->classReflection, $method)); } diff --git a/tests/bitExpert/PHPStan/Magento/Reflection/Framework/Session/SessionManagerHelper.php b/tests/bitExpert/PHPStan/Magento/Reflection/Framework/Session/SessionManagerHelper.php new file mode 100644 index 0000000..80c812b --- /dev/null +++ b/tests/bitExpert/PHPStan/Magento/Reflection/Framework/Session/SessionManagerHelper.php @@ -0,0 +1,21 @@ +<?php + +/* + * This file is part of the phpstan-magento package. + * + * (c) bitExpert AG + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace bitExpert\PHPStan\Magento\Reflection\Framework\Session; + +/** + * Class helper needed by the SessionManagerMagicMethodReflectionExtensionUnitTest + * to test for SessionManager subclasses + */ +class SessionManagerHelper extends \Magento\Framework\Session\SessionManager +{ +} diff --git a/tests/bitExpert/PHPStan/Magento/Reflection/Framework/Session/SessionManagerMagicMethodReflectionExtensionUnitTest.php b/tests/bitExpert/PHPStan/Magento/Reflection/Framework/Session/SessionManagerMagicMethodReflectionExtensionUnitTest.php index e78dcba..80b6599 100644 --- a/tests/bitExpert/PHPStan/Magento/Reflection/Framework/Session/SessionManagerMagicMethodReflectionExtensionUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Reflection/Framework/Session/SessionManagerMagicMethodReflectionExtensionUnitTest.php @@ -13,12 +13,13 @@ namespace bitExpert\PHPStan\Magento\Reflection\Framework\Session; use PHPStan\Reflection\ClassReflection; +use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Testing\PHPStanTestCase; use PHPStan\Type\BooleanType; use PHPStan\Type\MixedType; use PHPStan\Type\StringType; -use PHPUnit\Framework\TestCase; -class SessionManagerMagicMethodReflectionExtensionUnitTest extends TestCase +class SessionManagerMagicMethodReflectionExtensionUnitTest extends PHPStanTestCase { /** @@ -27,14 +28,16 @@ class SessionManagerMagicMethodReflectionExtensionUnitTest extends TestCase private $extension; /** - * @var ClassReflection|\PHPUnit\Framework\MockObject\MockObject + * @var ClassReflection */ private $classReflection; protected function setUp(): void { + /** @var ReflectionProvider $reflectionProvider */ + $reflectionProvider = $this->getContainer()->getService('reflectionProvider'); + $this->classReflection = $reflectionProvider->getClass(SessionManagerHelper::class); $this->extension = new SessionManagerMagicMethodReflectionExtension(); - $this->classReflection = $this->createMock(ClassReflection::class); } /** @@ -78,10 +81,6 @@ public function returnMagicMethodReflectionForGetMethod(): void */ public function hasMethodDetectSessionManager(string $method, bool $expectedResult): void { - $this->classReflection->expects(self::once()) - ->method('isSubclassOf') - ->willReturn(true); - self::assertSame($expectedResult, $this->extension->hasMethod($this->classReflection, $method)); } diff --git a/tests/bitExpert/PHPStan/Magento/Reflection/MagicMethodReflectionUnitTest.php b/tests/bitExpert/PHPStan/Magento/Reflection/MagicMethodReflectionUnitTest.php index 939c8a7..a75c296 100644 --- a/tests/bitExpert/PHPStan/Magento/Reflection/MagicMethodReflectionUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Reflection/MagicMethodReflectionUnitTest.php @@ -12,17 +12,20 @@ namespace bitExpert\PHPStan\Magento\Reflection; -use PHPStan\Reflection\ClassReflection; -use PHPUnit\Framework\TestCase; +use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Testing\PHPStanTestCase; +use PHPStan\TrinaryLogic; -class MagicMethodReflectionUnitTest extends TestCase +class MagicMethodReflectionUnitTest extends PHPStanTestCase { /** * @test */ public function magicMethodReflectionCreation(): void { - $classReflection = $this->createMock(ClassReflection::class); + /** @var ReflectionProvider $reflectionProvider */ + $reflectionProvider = $this->getContainer()->getService('reflectionProvider'); + $classReflection = $reflectionProvider->getClass('Magento\Framework\App\RequestInterface'); $methodName = 'myTestMethod'; $variants = []; @@ -36,10 +39,10 @@ public function magicMethodReflectionCreation(): void self::assertSame($reflection, $reflection->getPrototype()); self::assertSame($variants, $reflection->getVariants()); self::assertNull($reflection->getDocComment()); - self::assertSame(\PHPStan\TrinaryLogic::createNo(), $reflection->isDeprecated()); + self::assertSame(TrinaryLogic::createNo(), $reflection->isDeprecated()); self::assertSame('', $reflection->getDeprecatedDescription()); - self::assertSame(\PHPStan\TrinaryLogic::createNo(), $reflection->isFinal()); - self::assertSame(\PHPStan\TrinaryLogic::createNo(), $reflection->isInternal()); + self::assertSame(TrinaryLogic::createNo(), $reflection->isFinal()); + self::assertSame(TrinaryLogic::createNo(), $reflection->isInternal()); self::assertNull($reflection->getThrowType()); } @@ -47,13 +50,15 @@ public function magicMethodReflectionCreation(): void * @test * @dataProvider sideeffectsDataprovider * @param string $methodName - * @param \PHPStan\TrinaryLogic $expectedResult + * @param TrinaryLogic $expectedResult */ public function magicMethodReflectionCreationSideeffects( string $methodName, - \PHPStan\TrinaryLogic $expectedResult + TrinaryLogic $expectedResult ): void { - $classReflection = $this->createMock(ClassReflection::class); + /** @var ReflectionProvider $reflectionProvider */ + $reflectionProvider = $this->getContainer()->getService('reflectionProvider'); + $classReflection = $reflectionProvider->getClass('Magento\Framework\App\RequestInterface'); $variants = []; $reflection = new MagicMethodReflection($methodName, $classReflection, $variants); @@ -66,11 +71,11 @@ public function magicMethodReflectionCreationSideeffects( public function sideeffectsDataprovider(): array { return [ - ['getTest', \PHPStan\TrinaryLogic::createNo()], - ['setTest', \PHPStan\TrinaryLogic::createYes()], - ['unsetTest', \PHPStan\TrinaryLogic::createYes()], - ['hasText', \PHPStan\TrinaryLogic::createNo()], - ['someOtherMethod', \PHPStan\TrinaryLogic::createNo()], + ['getTest', TrinaryLogic::createNo()], + ['setTest', TrinaryLogic::createYes()], + ['unsetTest', TrinaryLogic::createYes()], + ['hasText', TrinaryLogic::createNo()], + ['someOtherMethod', TrinaryLogic::createNo()], ]; } } diff --git a/tests/bitExpert/PHPStan/Magento/Rules/AbstractModelRetrieveCollectionViaFactoryRuleUnitTest.php b/tests/bitExpert/PHPStan/Magento/Rules/AbstractModelRetrieveCollectionViaFactoryRuleUnitTest.php index a15d6d4..933cfd3 100644 --- a/tests/bitExpert/PHPStan/Magento/Rules/AbstractModelRetrieveCollectionViaFactoryRuleUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Rules/AbstractModelRetrieveCollectionViaFactoryRuleUnitTest.php @@ -17,7 +17,6 @@ use PhpParser\Node\Expr\Variable; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; use PHPStan\Testing\RuleTestCase; /** @@ -54,20 +53,6 @@ public function getNodeTypeMethodReturnsMethodCall(): void self::assertSame(MethodCall::class, $rule->getNodeType()); } - /** - * @test - */ - public function processNodeThrowsExceptionForNonMethodCallNodes(): void - { - $this->expectException(ShouldNotHappenException::class); - - $node = new Variable('var'); - $scope = $this->createMock(Scope::class); - - $rule = new AbstractModelRetrieveCollectionViaFactoryRule(); - $rule->processNode($node, $scope); - } - /** * @test */ diff --git a/tests/bitExpert/PHPStan/Magento/Rules/AbstractModelUseServiceContractRuleUnitTest.php b/tests/bitExpert/PHPStan/Magento/Rules/AbstractModelUseServiceContractRuleUnitTest.php index 20c802e..83155bb 100644 --- a/tests/bitExpert/PHPStan/Magento/Rules/AbstractModelUseServiceContractRuleUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Rules/AbstractModelUseServiceContractRuleUnitTest.php @@ -17,7 +17,6 @@ use PhpParser\Node\Expr\Variable; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; use PHPStan\Testing\RuleTestCase; /** @@ -61,20 +60,6 @@ public function getNodeTypeMethodReturnsMethodCall(): void self::assertSame(MethodCall::class, $rule->getNodeType()); } - /** - * @test - */ - public function processNodeThrowsExceptionForNonMethodCallNodes(): void - { - $this->expectException(ShouldNotHappenException::class); - - $node = new Variable('var'); - $scope = $this->createMock(Scope::class); - - $rule = new AbstractModelUseServiceContractRule(); - $rule->processNode($node, $scope); - } - /** * @test */ diff --git a/tests/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRuleUnitTest.php b/tests/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRuleUnitTest.php index 7632376..3c0255d 100644 --- a/tests/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRuleUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Rules/GetCollectionMockMethodNeedsCollectionSubclassRuleUnitTest.php @@ -18,7 +18,6 @@ use PhpParser\Node\Expr\Variable; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; use PHPStan\Testing\RuleTestCase; /** @@ -63,19 +62,6 @@ public function getNodeTypeMethodReturnsMethodCall(): void self::assertSame(MethodCall::class, $rule->getNodeType()); } - /** - * @test - */ - public function processNodeThrowsExceptionForNonMethodCallNodes(): void - { - $this->expectException(ShouldNotHappenException::class); - - $node = new Variable('var'); - $scope = $this->createMock(Scope::class); - - $rule = new GetCollectionMockMethodNeedsCollectionSubclassRule(); - $rule->processNode($node, $scope); - } /** * @test diff --git a/tests/bitExpert/PHPStan/Magento/Rules/Helper/SampleBlock.php b/tests/bitExpert/PHPStan/Magento/Rules/Helper/SampleBlock.php new file mode 100644 index 0000000..ddfe61f --- /dev/null +++ b/tests/bitExpert/PHPStan/Magento/Rules/Helper/SampleBlock.php @@ -0,0 +1,24 @@ +<?php + +/* + * This file is part of the phpstan-magento package. + * + * (c) bitExpert AG + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace bitExpert\PHPStan\Magento\Rules\Helper; + +use \Magento\Framework\View\Element\Template; + +class SampleBlock extends Template +{ + public function __construct() + { + // We do not call the parent constructor here on purpose as we do not want do to deal with creating + // not needed dependencies just for the testcase! + } +} diff --git a/tests/bitExpert/PHPStan/Magento/Rules/Helper/block_settemplate.php b/tests/bitExpert/PHPStan/Magento/Rules/Helper/block_settemplate.php new file mode 100644 index 0000000..dfa3192 --- /dev/null +++ b/tests/bitExpert/PHPStan/Magento/Rules/Helper/block_settemplate.php @@ -0,0 +1,4 @@ +<?php + +$block = new \bitExpert\PHPStan\Magento\Rules\Helper\SampleBlock(); +$block->setTemplate('template.phtml'); diff --git a/tests/bitExpert/PHPStan/Magento/Rules/Helper/resource_model.php b/tests/bitExpert/PHPStan/Magento/Rules/Helper/resource_model.php new file mode 100644 index 0000000..a16c226 --- /dev/null +++ b/tests/bitExpert/PHPStan/Magento/Rules/Helper/resource_model.php @@ -0,0 +1,4 @@ +<?php + +$model = new \bitExpert\PHPStan\Magento\Rules\Helper\SampleModel(); +$model->getResource(); diff --git a/tests/bitExpert/PHPStan/Magento/Rules/ResourceModelsShouldBeUsedDirectlyRuleUnitTest.php b/tests/bitExpert/PHPStan/Magento/Rules/ResourceModelsShouldBeUsedDirectlyRuleUnitTest.php new file mode 100644 index 0000000..ce67520 --- /dev/null +++ b/tests/bitExpert/PHPStan/Magento/Rules/ResourceModelsShouldBeUsedDirectlyRuleUnitTest.php @@ -0,0 +1,82 @@ +<?php + +/* + * This file is part of the phpstan-magento package. + * + * (c) bitExpert AG + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace bitExpert\PHPStan\Magento\Rules; + +use bitExpert\PHPStan\Magento\Rules\Helper\SampleModel; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\Variable; +use PHPStan\Analyser\Scope; +use PHPStan\Rules\Rule; +use PHPStan\Testing\RuleTestCase; + +/** + * @extends \PHPStan\Testing\RuleTestCase<ResourceModelsShouldBeUsedDirectlyRule> + */ +class ResourceModelsShouldBeUsedDirectlyRuleUnitTest extends RuleTestCase +{ + protected function getRule(): Rule + { + return new ResourceModelsShouldBeUsedDirectlyRule(); + } + + /** + * @test + */ + public function checkCaughtExceptions(): void + { + $this->analyse([__DIR__ . '/Helper/resource_model.php'], [ + [ + SampleModel::class . '::getResource() is deprecated. Use Resource Models directly', + 4, + ], + ]); + } + + /** + * @test + */ + public function getNodeTypeMethodReturnsMethodCall(): void + { + $rule = new ResourceModelsShouldBeUsedDirectlyRule(); + + self::assertSame(MethodCall::class, $rule->getNodeType()); + } + + /** + * @test + */ + public function processNodeReturnsEarlyWhenNodeNameIsWrongType(): void + { + $node = new MethodCall(new Variable('var'), new Variable('wrong_node')); + $scope = $this->createMock(Scope::class); + + $rule = new ResourceModelsShouldBeUsedDirectlyRule(); + $return = $rule->processNode($node, $scope); + + self::assertCount(0, $return); + } + + /** + * @test + */ + public function processNodeReturnsEarlyWhenNodeNameIsNotSaveOrLoadOrDelete(): void + { + $node = new MethodCall(new Variable('var'), 'wrong_node_name'); + $scope = $this->createMock(Scope::class); + + $rule = new ResourceModelsShouldBeUsedDirectlyRule(); + $return = $rule->processNode($node, $scope); + + self::assertCount(0, $return); + } +} diff --git a/tests/bitExpert/PHPStan/Magento/Rules/SetTemplateDisallowedForBlockRuleUnitTest.php b/tests/bitExpert/PHPStan/Magento/Rules/SetTemplateDisallowedForBlockRuleUnitTest.php new file mode 100644 index 0000000..274bf40 --- /dev/null +++ b/tests/bitExpert/PHPStan/Magento/Rules/SetTemplateDisallowedForBlockRuleUnitTest.php @@ -0,0 +1,84 @@ +<?php + +/* + * This file is part of the phpstan-magento package. + * + * (c) bitExpert AG + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace bitExpert\PHPStan\Magento\Rules; + +use bitExpert\PHPStan\Magento\Rules\Helper\SampleBlock; +use bitExpert\PHPStan\Magento\Rules\Helper\SampleModel; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Expr\Variable; +use PHPStan\Analyser\Scope; +use PHPStan\Rules\Rule; +use PHPStan\Testing\RuleTestCase; + +/** + * @extends \PHPStan\Testing\RuleTestCase<SetTemplateDisallowedForBlockRule> + */ +class SetTemplateDisallowedForBlockRuleUnitTest extends RuleTestCase +{ + protected function getRule(): Rule + { + return new SetTemplateDisallowedForBlockRule(); + } + + /** + * @test + */ + public function checkCaughtExceptions(): void + { + $this->analyse([__DIR__ . '/Helper/block_settemplate.php'], [ + [ + 'Setter methods like '.SampleBlock::class.'::setTemplate() are deprecated in Block classes, '. + 'use constructor arguments instead', + 4, + ], + ]); + } + + /** + * @test + */ + public function getNodeTypeMethodReturnsMethodCall(): void + { + $rule = new SetTemplateDisallowedForBlockRule(); + + self::assertSame(MethodCall::class, $rule->getNodeType()); + } + + /** + * @test + */ + public function processNodeReturnsEarlyWhenNodeNameIsWrongType(): void + { + $node = new MethodCall(new Variable('var'), new Variable('wrong_node')); + $scope = $this->createMock(Scope::class); + + $rule = new SetTemplateDisallowedForBlockRule(); + $return = $rule->processNode($node, $scope); + + self::assertCount(0, $return); + } + + /** + * @test + */ + public function processNodeReturnsEarlyWhenNodeNameIsNotSaveOrLoadOrDelete(): void + { + $node = new MethodCall(new Variable('var'), 'wrong_node_name'); + $scope = $this->createMock(Scope::class); + + $rule = new SetTemplateDisallowedForBlockRule(); + $return = $rule->processNode($node, $scope); + + self::assertCount(0, $return); + } +} diff --git a/tests/bitExpert/PHPStan/Magento/Type/ControllerResultFactoryReturnTypeExtensionUnitTest.php b/tests/bitExpert/PHPStan/Magento/Type/ControllerResultFactoryReturnTypeExtensionUnitTest.php new file mode 100644 index 0000000..8587d98 --- /dev/null +++ b/tests/bitExpert/PHPStan/Magento/Type/ControllerResultFactoryReturnTypeExtensionUnitTest.php @@ -0,0 +1,98 @@ +<?php + +/* + * This file is part of the phpstan-magento package. + * + * (c) bitExpert AG + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +declare(strict_types=1); + +namespace bitExpert\PHPStan\Magento\Type; + +use PhpParser\Node\Arg; +use PhpParser\Node\Expr\ClassConstFetch; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PHPStan\Analyser\Scope; +use PHPStan\Reflection\MethodReflection; +use PHPStan\Testing\PHPStanTestCase; +use PHPStan\Type\ObjectType; + +class ControllerResultFactoryReturnTypeExtensionUnitTest extends PHPStanTestCase +{ + /** + * @var ControllerResultFactoryReturnTypeExtension + */ + private $extension; + + protected function setUp(): void + { + $this->extension = new ControllerResultFactoryReturnTypeExtension(); + } + + /** + * @return mixed[] + */ + public function returnTypeDataProvider(): array + { + return [ + ['TYPE_JSON', 'Magento\Framework\Controller\Result\Json'], + ['TYPE_PAGE', 'Magento\Framework\View\Result\Page'] + ]; + } + + /** + * @return mixed[] + */ + public function isMethodSupportedDataProvider(): array + { + return [ + ['create', true], + ['get', false] + ]; + } + + /** + * @test + * @dataProvider isMethodSupportedDataProvider + * @param string $method + * @param bool $expectedResult + */ + public function checkIfMethodIsSupported(string $method, bool $expectedResult): void + { + $methodReflection = $this->createMock(MethodReflection::class); + $methodReflection->method('getName')->willReturn($method); + + self::assertSame($expectedResult, $this->extension->isMethodSupported($methodReflection)); + } + + /** + * @test + * @dataProvider returnTypeDataProvider + * @param string $param + * @param string $expectedResult + */ + public function returnValidResultType(string $param, string $expectedResult): void + { + $methodReflection = $this->createMock(MethodReflection::class); + $scope = $this->createMock(Scope::class); + + $identifier = $this->createConfiguredMock(Identifier::class, ['toString' => $param]); + + $expr = $this->createMock(ClassConstFetch::class); + $expr->name = $identifier; + + $arg = $this->createMock(Arg::class); + $arg->value = $expr; + + $methodCall = $this->createConfiguredMock(MethodCall::class, ['getArgs' => [$arg]]); + + $resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope); + + self::assertNotNull($resultType); + self::assertSame($expectedResult, $resultType->getClassName()); + } +} diff --git a/tests/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest.php b/tests/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest.php index bd579ff..0b2d001 100644 --- a/tests/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest.php +++ b/tests/bitExpert/PHPStan/Magento/Type/TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest.php @@ -15,6 +15,7 @@ use Magento\Framework\View\Design\Theme\ThemeList; use PhpParser\Node\Arg; use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; use PhpParser\Node\Scalar\LNumber; use PhpParser\Node\Scalar\String_; use PHPStan\Analyser\Scope; @@ -79,6 +80,7 @@ public function returnsErrorTypeForUnkownMethodCall(): void $scope = $this->createMock(Scope::class); $methodCall = $this->createMock(MethodCall::class); $methodCall->args = []; + $methodCall->name = new Identifier('somethingUnknown'); $resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope); @@ -94,7 +96,7 @@ public function returnsMixedTypeForGetObjectCallWithoutParameter(): void $scope = $this->createMock(Scope::class); $methodCall = $this->createMock(MethodCall::class); $methodCall->args = []; - $methodCall->name = new \PhpParser\Node\Identifier('getObject'); + $methodCall->name = new Identifier('getObject'); $resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope); @@ -110,7 +112,7 @@ public function returnsMixedTypeForGetObjectCallWithNonStringParameter(): void $scope = $this->createMock(Scope::class); $methodCall = $this->createMock(MethodCall::class); $methodCall->args = [new Arg(new LNumber(1))]; - $methodCall->name = new \PhpParser\Node\Identifier('getObject'); + $methodCall->name = new Identifier('getObject'); $resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope); @@ -130,7 +132,7 @@ public function returnsUnionTypeForGetObjectCallWithStringParameter(): void $methodCall = $this->createMock(MethodCall::class); $methodCall->args = [new Arg(new String_('someArg'))]; - $methodCall->name = new \PhpParser\Node\Identifier('getObject'); + $methodCall->name = new Identifier('getObject'); $resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope); @@ -146,7 +148,7 @@ public function returnsUnionTypeForGetConstructArgumentsCall(): void $scope = $this->createMock(Scope::class); $methodCall = $this->createMock(MethodCall::class); $methodCall->args = []; - $methodCall->name = new \PhpParser\Node\Identifier('getConstructArguments'); + $methodCall->name = new Identifier('getConstructArguments'); /** @var UnionType $resultType */ $resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope); @@ -172,7 +174,7 @@ public function returnsErrorTypeForGetCollectionMockMethod(): void $methodCall = $this->createMock(MethodCall::class); $methodCall->args = [new Arg(new String_(self::class))]; - $methodCall->name = new \PhpParser\Node\Identifier('getCollectionMock'); + $methodCall->name = new Identifier('getCollectionMock'); $resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope); @@ -193,7 +195,7 @@ public function returnsUnionTypeForGetCollectionMockMethod(): void $methodCall = $this->createMock(MethodCall::class); $methodCall->args = [new Arg(new String_(ThemeList::class))]; - $methodCall->name = new \PhpParser\Node\Identifier('getCollectionMock'); + $methodCall->name = new Identifier('getCollectionMock'); /** @var IntersectionType $resultType */ $resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope);