diff --git a/composer.json b/composer.json index 05c3a5f389d0..5070e672c9ba 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ }, "require-dev": { "codeigniter/coding-standard": "^1.5", + "codeigniter/phpstan-codeigniter": "^v1.1", "ergebnis/composer-normalize": "^2.28", "fakerphp/faker": "^1.9", "kint-php/kint": "^5.0.4", @@ -26,6 +27,7 @@ "nexusphp/cs-config": "^3.6", "nexusphp/tachycardia": "^1.0", "php-coveralls/php-coveralls": "^2.5", + "phpstan/extension-installer": "^1.3", "phpstan/phpstan": "^1.10.2", "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpcov": "^8.2", @@ -70,7 +72,8 @@ }, "config": { "allow-plugins": { - "ergebnis/composer-normalize": true + "ergebnis/composer-normalize": true, + "phpstan/extension-installer": true }, "optimize-autoloader": true, "preferred-install": "dist", diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 4d1cde2372b4..106de3723ef0 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -46,6 +46,21 @@ 'count' => 5, 'path' => __DIR__ . '/system/CLI/BaseCommand.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'ANSICON\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/CLI/CLI.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'NO_COLOR\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/CLI/CLI.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'argv\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/CLI/CLI.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 6, @@ -216,6 +231,11 @@ 'count' => 1, 'path' => __DIR__ . '/system/Commands/Database/ShowTableInfo.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'encryption\\.key\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Commands/Encryption/GenerateKey.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 2, @@ -466,6 +486,26 @@ 'count' => 1, 'path' => __DIR__ . '/system/Commands/ListCommands.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'DOCUMENT_ROOT\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/Commands/Server/rewrite.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'REQUEST_URI\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Commands/Server/rewrite.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Assigning \'/index\\.php\' directly on offset \'SCRIPT_NAME\' of \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Commands/Server/rewrite.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'CI_ENVIRONMENT\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 3, + 'path' => __DIR__ . '/system/Commands/Utilities/Environment.php', +]; $ignoreErrors[] = [ 'message' => '#^PHPDoc type array\\ of property CodeIgniter\\\\Commands\\\\Utilities\\\\Environment\\:\\:\\$arguments is not the same as PHPDoc type array of overridden property CodeIgniter\\\\CLI\\\\BaseCommand\\:\\:\\$arguments\\.$#', 'count' => 1, @@ -486,6 +526,16 @@ 'count' => 1, 'path' => __DIR__ . '/system/Commands/Utilities/Publish.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'HTTP_HOST\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Commands/Utilities/Routes.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Assigning non\\-falsy\\-string directly on offset \'HTTP_HOST\' of \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Commands/Utilities/Routes.php', +]; $ignoreErrors[] = [ 'message' => '#^PHPDoc type array\\ of property CodeIgniter\\\\Commands\\\\Utilities\\\\Routes\\:\\:\\$options is not the same as PHPDoc type array of overridden property CodeIgniter\\\\CLI\\\\BaseCommand\\:\\:\\$options\\.$#', 'count' => 1, @@ -516,6 +566,21 @@ 'count' => 1, 'path' => __DIR__ . '/system/Commands/Utilities/Routes/ControllerMethodReader.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'REMOTE_ADDR\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Common.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'REQUEST_METHOD\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Common.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset string directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Common.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 8, @@ -531,16 +596,56 @@ 'count' => 1, 'path' => __DIR__ . '/system/ComposerScripts.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'CI_ENVIRONMENT\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/Config/AutoloadConfig.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset non\\-falsy\\-string directly on \\$_SERVER is discouraged\\.$#', + 'count' => 4, + 'path' => __DIR__ . '/system/Config/BaseConfig.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Argument \\#1 \\$name \\(\'Config\\\\\\\\Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Config/BaseConfig.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Argument \\#1 \\$name \\(\'Config\\\\\\\\Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/Config/BaseService.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 3, 'path' => __DIR__ . '/system/Config/BaseService.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset string directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/Config/DotEnv.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Assigning string directly on offset string of \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Config/DotEnv.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 2, 'path' => __DIR__ . '/system/Config/DotEnv.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'SERVER_PROTOCOL\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Config/Services.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Argument \\#1 \\$name \\(\'Config\\\\\\\\Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Config/Services.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 1, @@ -1446,6 +1551,16 @@ 'count' => 2, 'path' => __DIR__ . '/system/Debug/Toolbar/Collectors/Views.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'SERVER_ADDR\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/Email/Email.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'SERVER_NAME\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/Email/Email.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 14, @@ -1496,6 +1611,11 @@ 'count' => 4, 'path' => __DIR__ . '/system/Entity/Entity.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Argument \\#1 \\$name \\(\'Config\\\\\\\\Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Events/Events.php', +]; $ignoreErrors[] = [ 'message' => '#^Method CodeIgniter\\\\Events\\\\Events\\:\\:on\\(\\) has parameter \\$callback with no signature specified for callable\\.$#', 'count' => 1, @@ -1536,6 +1656,11 @@ 'count' => 3, 'path' => __DIR__ . '/system/Files/File.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Argument \\#1 \\$name \\(\'Config\\\\\\\\Modules\'\\) passed to function config does not extend CodeIgniter\\\\\\\\Config\\\\\\\\BaseConfig\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Filters/Filters.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 2, @@ -1571,6 +1696,11 @@ 'count' => 9, 'path' => __DIR__ . '/system/HTTP/ContentSecurityPolicy.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'HTTP_USER_AGENT\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/HTTP/DownloadResponse.php', +]; $ignoreErrors[] = [ 'message' => '#^Return type \\(CodeIgniter\\\\HTTP\\\\DownloadResponse\\) of method CodeIgniter\\\\HTTP\\\\DownloadResponse\\:\\:noCache\\(\\) should be covariant with return type \\(\\$this\\(CodeIgniter\\\\HTTP\\\\Response\\)\\) of method CodeIgniter\\\\HTTP\\\\Response\\:\\:noCache\\(\\)$#', 'count' => 1, @@ -1651,6 +1781,36 @@ 'count' => 1, 'path' => __DIR__ . '/system/HTTP/Files/UploadedFile.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'HTTPS\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/HTTP/IncomingRequest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'QUERY_STRING\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 3, + 'path' => __DIR__ . '/system/HTTP/IncomingRequest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'REQUEST_URI\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/HTTP/IncomingRequest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'SCRIPT_NAME\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 4, + 'path' => __DIR__ . '/system/HTTP/IncomingRequest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset array\\|string directly on \\$_GET is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/HTTP/IncomingRequest.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Assigning string directly on offset \'QUERY_STRING\' of \\$_SERVER is discouraged\\.$#', + 'count' => 3, + 'path' => __DIR__ . '/system/HTTP/IncomingRequest.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 7, @@ -1661,6 +1821,16 @@ 'count' => 1, 'path' => __DIR__ . '/system/HTTP/IncomingRequest.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'CONTENT_TYPE\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/HTTP/Message.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \\(int\\|string\\) directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/HTTP/Message.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 2, @@ -1701,6 +1871,21 @@ 'count' => 3, 'path' => __DIR__ . '/system/HTTP/Request.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'REQUEST_METHOD\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 3, + 'path' => __DIR__ . '/system/HTTP/Response.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'SERVER_PROTOCOL\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/HTTP/Response.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'SERVER_SOFTWARE\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/HTTP/Response.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 6, @@ -1746,6 +1931,16 @@ 'count' => 1, 'path' => __DIR__ . '/system/HTTP/URI.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'HTTP_REFERER\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 4, + 'path' => __DIR__ . '/system/HTTP/UserAgent.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'HTTP_USER_AGENT\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/HTTP/UserAgent.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 3, @@ -1971,6 +2166,11 @@ 'count' => 1, 'path' => __DIR__ . '/system/Model.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset mixed directly on \\$_GET is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Pager/Pager.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 4, @@ -1986,6 +2186,11 @@ 'count' => 1, 'path' => __DIR__ . '/system/Pager/Views/default_simple.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Argument \\#1 \\$name \\(class\\-string\\) passed to function model does not extend CodeIgniter\\\\\\\\Model\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/RESTful/BaseResource.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 4, @@ -2131,6 +2336,11 @@ 'count' => 1, 'path' => __DIR__ . '/system/Router/RouteCollectionInterface.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'REQUEST_METHOD\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Router/Router.php', +]; $ignoreErrors[] = [ 'message' => '#^Call to an undefined method CodeIgniter\\\\Router\\\\RouteCollectionInterface\\:\\:getDefaultNamespace\\(\\)\\.$#', 'count' => 2, @@ -2296,6 +2506,11 @@ 'count' => 5, 'path' => __DIR__ . '/system/Session/Handlers/RedisHandler.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'HTTP_X_REQUESTED_WITH\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 2, + 'path' => __DIR__ . '/system/Session/Session.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 13, @@ -2606,6 +2821,21 @@ 'count' => 1, 'path' => __DIR__ . '/system/Test/Fabricator.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Parameter \\#1 \\$name of function model expects a valid class string, string given\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Test/Fabricator.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Assigning \'test\' directly on offset \'HTTPS\' of \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Test/FeatureTestCase.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Assigning string directly on offset \'REQUEST_METHOD\' of \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Test/FeatureTestCase.php', +]; $ignoreErrors[] = [ 'message' => '#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#', 'count' => 10, @@ -3131,6 +3361,21 @@ 'count' => 1, 'path' => __DIR__ . '/system/Test/TestResponse.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Accessing offset \'app\\.baseURL\' directly on \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Test/bootstrap.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Assigning \'http\\://example\\.com/\' directly on offset \'app\\.baseURL\' of \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Test/bootstrap.php', +]; +$ignoreErrors[] = [ + 'message' => '#^Assigning \'testing\' directly on offset \'CI_ENVIRONMENT\' of \\$_SERVER is discouraged\\.$#', + 'count' => 1, + 'path' => __DIR__ . '/system/Test/bootstrap.php', +]; $ignoreErrors[] = [ 'message' => '#^Property CodeIgniter\\\\Throttle\\\\Throttler\\:\\:\\$testTime \\(int\\) on left side of \\?\\? is not nullable\\.$#', 'count' => 1, @@ -3246,11 +3491,6 @@ 'count' => 2, 'path' => __DIR__ . '/system/View/View.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$node \\(PhpParser\\\\Node\\\\Expr\\\\New_\\) of method Utils\\\\PHPStan\\\\CheckFrameworkExceptionInstantiationViaNamedConstructorRule\\:\\:processNode\\(\\) should be contravariant with parameter \\$node \\(PhpParser\\\\Node\\) of method PHPStan\\\\Rules\\\\Rule\\\\:\\:processNode\\(\\)$#', - 'count' => 1, - 'path' => __DIR__ . '/utils/PHPStan/CheckFrameworkExceptionInstantiationViaNamedConstructorRule.php', -]; $ignoreErrors[] = [ 'message' => '#^Parameter \\#1 \\$node \\(PhpParser\\\\Node\\\\Stmt\\) of method Utils\\\\PHPStan\\\\CheckUseStatementsAfterLicenseRule\\:\\:processNode\\(\\) should be contravariant with parameter \\$node \\(PhpParser\\\\Node\\) of method PHPStan\\\\Rules\\\\Rule\\\\:\\:processNode\\(\\)$#', 'count' => 1, diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 77470c3c0613..cb04b9ee9be5 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,14 +3,9 @@ services: class: Utils\PHPStan\CheckUseStatementsAfterLicenseRule tags: - phpstan.rules.rule - - - class: Utils\PHPStan\CheckFrameworkExceptionInstantiationViaNamedConstructorRule - tags: - - phpstan.rules.rule includes: - phpstan-baseline.php - - vendor/phpstan/phpstan-strict-rules/rules.neon parameters: phpVersion: 80100 @@ -34,10 +29,6 @@ parameters: - system/Validation/Views/single.php scanDirectories: - system/Helpers - dynamicConstantNames: - - APP_NAMESPACE - - CI_DEBUG - - ENVIRONMENT checkMissingIterableValueType: false checkGenericClassInNonGenericObjectType: false checkMissingCallableSignature: true diff --git a/rector.php b/rector.php index 61ca28b47045..9f6a214e6bad 100644 --- a/rector.php +++ b/rector.php @@ -67,7 +67,11 @@ __DIR__ . '/system/Test/bootstrap.php', ]); - $rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon.dist'); + $rectorConfig->phpstanConfigs([ + __DIR__ . '/phpstan.neon.dist', + __DIR__ . '/vendor/codeigniter/phpstan-codeigniter/extension.neon', + __DIR__ . '/vendor/phpstan/phpstan-strict-rules/rules.neon', + ]); // is there a file you need to skip? $rectorConfig->skip([ diff --git a/utils/PHPStan/CheckFrameworkExceptionInstantiationViaNamedConstructorRule.php b/utils/PHPStan/CheckFrameworkExceptionInstantiationViaNamedConstructorRule.php deleted file mode 100644 index 5cca5ec8f4d4..000000000000 --- a/utils/PHPStan/CheckFrameworkExceptionInstantiationViaNamedConstructorRule.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Utils\PHPStan; - -use CodeIgniter\Exceptions\FrameworkException; -use PhpParser\Node; -use PhpParser\Node\Expr\New_; -use PhpParser\Node\Name\FullyQualified; -use PHPStan\Analyser\Scope; -use PHPStan\Rules\Rule; - -final class CheckFrameworkExceptionInstantiationViaNamedConstructorRule implements Rule -{ - private const ERROR_MESSAGE = 'FrameworkException instance creation via new expression is not allowed, use its named constructor instead'; - - public function getNodeType(): string - { - return New_::class; - } - - /** - * @param New_ $node - */ - public function processNode(Node $node, Scope $scope): array - { - $class = $node->class; - if (! $class instanceof FullyQualified) { - return []; - } - - if (! is_a((string) $class, FrameworkException::class, true)) { - return []; - } - - return [self::ERROR_MESSAGE]; - } -}