From 921f1b3fad9ce0d276e4d6a226dcb61fcb850a41 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 3 Dec 2024 15:22:10 +0100 Subject: [PATCH 1/2] Compare with spaze/phpstan-disallowed-calls --- compare.php | 40 +++++++ compare.phpstan.neon | 42 +++++++ composer.json | 3 +- composer.lock | 73 +++++++++++- .../data/ForbidCustomFunctionsRule/code.php | 104 +++++++++--------- 5 files changed, 206 insertions(+), 56 deletions(-) create mode 100644 compare.php create mode 100644 compare.phpstan.neon diff --git a/compare.php b/compare.php new file mode 100644 index 0000000..6be01f0 --- /dev/null +++ b/compare.php @@ -0,0 +1,40 @@ +#!/usr/bin/env php + 'Method ', + 'is forbidden,' => 'is forbidden.', + ]; + + return str_replace(array_keys($replace), array_values($replace), rtrim($message, '.')); +} + +$iterator = new DirectoryIterator(__DIR__ . '/tests/Rule/data/ForbidCustomFunctionsRule'); + +foreach ($iterator as $fileinfo) { + if (!$fileinfo->isFile() || $fileinfo->getExtension() !== 'php') { + continue; + } + $filePath = $fileinfo->getPathname(); + + $contents = file_get_contents($filePath); + $contentsLines = explode("\n", $contents); + + foreach ($contentsLines as $line => $row) { + $newLine = preg_replace('~ ?// error.*$~', '', $row); + $contentsLines[$line] = $newLine; + } + + foreach ($errors['files'][$filePath]['messages'] ?? [] as $error) { + $line = $error['line']; + $contentsLines[$line - 1] .= ' // error: ' . transformMessage($error['message']); + } + + file_put_contents($filePath, implode("\n", $contentsLines)); +} diff --git a/compare.phpstan.neon b/compare.phpstan.neon new file mode 100644 index 0000000..2099f56 --- /dev/null +++ b/compare.phpstan.neon @@ -0,0 +1,42 @@ +includes: + - vendor/spaze/phpstan-disallowed-calls/extension.neon + +parameters: + customRulesetUsed: true + + # those are copied from ForbidCustomFunctionsRuleTest + disallowedFunctionCalls: + - + function: 'sleep' + message: 'Description 0' + + disallowedMethodCalls: + - + method: 'ForbidCustomFunctionsRule\forbidden_namespaced_function' + message: 'Description 1' + - + method: 'ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::*' + message: 'Description 2' + - + method: 'ForbidCustomFunctionsRule\ClassWithForbiddenConstructor::__construct' + message: 'Description 3' + - + method: 'ForbidCustomFunctionsRule\SomeClass::forbiddenMethod' + message: 'Description 4' + + - + method: 'ForbidCustomFunctionsRule\SomeInterface::forbiddenInterfaceMethod' + message: 'Description 6' + + - + method: 'ForbidCustomFunctionsRule\SomeParent::forbiddenMethodOfParent' + message: 'Description 8' + + disallowedStaticCalls: + - + method: 'ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod' + message: 'Description 5' + + - + method: 'ForbidCustomFunctionsRule\SomeInterface::forbiddenInterfaceStaticMethod' + message: 'Description 7' diff --git a/composer.json b/composer.json index 25d95cf..410fadd 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "shipmonk/composer-dependency-analyser": "^1.7.0", "shipmonk/dead-code-detector": "^0.6.0", "shipmonk/name-collision-detector": "^2.1.1", - "slevomat/coding-standard": "^8.15.0" + "slevomat/coding-standard": "^8.15.0", + "spaze/phpstan-disallowed-calls": "^4.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 203421c..0610209 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8d8b1b3e9ef8f36ebc033de1adbe10d6", + "content-hash": "bb12fe826a13581404d9f25015d5945d", "packages": [ { "name": "phpstan/phpstan", @@ -3053,6 +3053,73 @@ ], "time": "2024-03-09T15:20:58+00:00" }, + { + "name": "spaze/phpstan-disallowed-calls", + "version": "v4.0.1", + "source": { + "type": "git", + "url": "https://github.com/spaze/phpstan-disallowed-calls.git", + "reference": "0f030fd3fb770ee7c262445c8ad3c0b470a32ac5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spaze/phpstan-disallowed-calls/zipball/0f030fd3fb770ee7c262445c8ad3c0b470a32ac5", + "reference": "0f030fd3fb770ee7c262445c8ad3c0b470a32ac5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^1.12.6 || ^2.0" + }, + "require-dev": { + "nette/neon": "^3.3.1", + "nikic/php-parser": "^4.13 || ^5.0", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^1.2 || ^2.0", + "phpunit/phpunit": "^8.5.14 || ^10.1 || ^11.0", + "spaze/coding-standard": "^1.7" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Spaze\\PHPStan\\Rules\\Disallowed\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michal Špaček", + "email": "mail@michalspacek.cz", + "homepage": "https://www.michalspacek.cz" + } + ], + "description": "PHPStan rules to detect disallowed method & function calls, constant, namespace & superglobal usages", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/spaze/phpstan-disallowed-calls/issues", + "source": "https://github.com/spaze/phpstan-disallowed-calls/tree/v4.0.1" + }, + "funding": [ + { + "url": "https://github.com/spaze", + "type": "github" + } + ], + "time": "2024-11-13T17:17:13+00:00" + }, { "name": "squizlabs/php_codesniffer", "version": "3.11.1", @@ -3186,12 +3253,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^7.4 || ^8.0" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/tests/Rule/data/ForbidCustomFunctionsRule/code.php b/tests/Rule/data/ForbidCustomFunctionsRule/code.php index 8b84057..91405f7 100644 --- a/tests/Rule/data/ForbidCustomFunctionsRule/code.php +++ b/tests/Rule/data/ForbidCustomFunctionsRule/code.php @@ -65,99 +65,99 @@ public function test( ChildOfClassWithForbiddenAllMethods $forbiddenClassChild, SomeInterface $interface ) { - sleep(...); // error: Function sleep() is forbidden. Description 0 - sleep(0); // error: Function sleep() is forbidden. Description 0 - array_map('sleep', $array); // error: Function sleep() is forbidden. Description 0 - array_map(array: $array, callback: 'sleep'); // error: Function sleep() is forbidden. Description 0 - array_map([$class, 'forbiddenMethod'], $array); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - array_map([$class, 'forbiddenStaticMethod'], $array); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5 + sleep(...); + sleep(0); // error: Method sleep() is forbidden. Description 0 + array_map('sleep', $array); + array_map(array: $array, callback: 'sleep'); + array_map([$class, 'forbiddenMethod'], $array); + array_map([$class, 'forbiddenStaticMethod'], $array); strlen('sleep'); // not used in callable context [$class, 'forbiddenMethod']; // not used in callable context [$class, 'forbiddenStaticMethod']; // not used in callable context $this->acceptCallable('sleep', [], 'x'); - $this->acceptCallable('x', [], 'sleep'); // error: Function sleep() is forbidden. Description 0 - $this->acceptCallable(callable: 'sleep'); // error: Function sleep() is forbidden. Description 0 + $this->acceptCallable('x', [], 'sleep'); + $this->acceptCallable(callable: 'sleep'); $this->acceptCallable(string: 'sleep'); $this->acceptCallable(callable: 'strlen', array: [], string: 'sleep'); - $this->acceptCallable('x', [], [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - $this->acceptCallable(callable: [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 + $this->acceptCallable('x', [], [$class, 'forbiddenMethod']); + $this->acceptCallable(callable: [$class, 'forbiddenMethod']); self::acceptCallableStatic('sleep', [], 'x'); - self::acceptCallableStatic('x', [], 'sleep'); // error: Function sleep() is forbidden. Description 0 - self::acceptCallableStatic(callable: 'sleep'); // error: Function sleep() is forbidden. Description 0 + self::acceptCallableStatic('x', [], 'sleep'); + self::acceptCallableStatic(callable: 'sleep'); self::acceptCallableStatic(string: 'sleep'); self::acceptCallableStatic(callable: 'strlen', array: [], string: 'sleep'); - self::acceptCallableStatic('x', [], [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - self::acceptCallableStatic(callable: [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 + self::acceptCallableStatic('x', [], [$class, 'forbiddenMethod']); + self::acceptCallableStatic(callable: [$class, 'forbiddenMethod']); $self = self::class; $self::acceptCallableStatic('sleep', [], 'x'); - $self::acceptCallableStatic('x', [], 'sleep'); // error: Function sleep() is forbidden. Description 0 - $self::acceptCallableStatic(callable: 'sleep'); // error: Function sleep() is forbidden. Description 0 + $self::acceptCallableStatic('x', [], 'sleep'); + $self::acceptCallableStatic(callable: 'sleep'); $self::acceptCallableStatic(string: 'sleep'); $self::acceptCallableStatic(callable: 'strlen', array: [], string: 'sleep'); - $self::acceptCallableStatic('x', [], [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - $self::acceptCallableStatic(callable: [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 + $self::acceptCallableStatic('x', [], [$class, 'forbiddenMethod']); + $self::acceptCallableStatic(callable: [$class, 'forbiddenMethod']); new AcceptCallable('sleep', [], 'x'); - new AcceptCallable('x', [], 'sleep'); // error: Function sleep() is forbidden. Description 0 - new AcceptCallable(callable: 'sleep'); // error: Function sleep() is forbidden. Description 0 + new AcceptCallable('x', [], 'sleep'); + new AcceptCallable(callable: 'sleep'); new AcceptCallable(string: 'sleep'); new AcceptCallable(callable: 'strlen', array: [], string: 'sleep'); - new AcceptCallable('x', [], [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - new AcceptCallable(callable: [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 + new AcceptCallable('x', [], [$class, 'forbiddenMethod']); + new AcceptCallable(callable: [$class, 'forbiddenMethod']); $acceptCallableClass = AcceptCallable::class; new $acceptCallableClass('sleep', [], 'x'); - new $acceptCallableClass('x', [], 'sleep'); // error: Function sleep() is forbidden. Description 0 - new $acceptCallableClass(callable: 'sleep'); // error: Function sleep() is forbidden. Description 0 + new $acceptCallableClass('x', [], 'sleep'); + new $acceptCallableClass(callable: 'sleep'); new $acceptCallableClass(string: 'sleep'); new $acceptCallableClass(callable: 'strlen', array: [], string: 'sleep'); - new $acceptCallableClass('x', [], [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - new $acceptCallableClass(callable: [$class, 'forbiddenMethod']); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 + new $acceptCallableClass('x', [], [$class, 'forbiddenMethod']); + new $acceptCallableClass(callable: [$class, 'forbiddenMethod']); new class ('sleep', [], 'x') extends AcceptCallable {}; - new class ('x', [], 'sleep') extends AcceptCallable {}; // error: Function sleep() is forbidden. Description 0 - new class (callable: 'sleep') extends AcceptCallable {}; // error: Function sleep() is forbidden. Description 0 + new class ('x', [], 'sleep') extends AcceptCallable {}; + new class (callable: 'sleep') extends AcceptCallable {}; new class (string: 'sleep') extends AcceptCallable {}; new class (callable: 'strlen', array: [], string: 'sleep') extends AcceptCallable {}; - new class ('x', [], [$class, 'forbiddenMethod']) extends AcceptCallable {}; // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - new class (callable: [$class, 'forbiddenMethod']) extends AcceptCallable {}; // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 + new class ('x', [], [$class, 'forbiddenMethod']) extends AcceptCallable {}; + new class (callable: [$class, 'forbiddenMethod']) extends AcceptCallable {}; $class->allowedMethod(); $class->forbiddenMethod(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 $class?->forbiddenMethod(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - $class->forbiddenMethod(...); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 + $class->forbiddenMethod(...); $class->allowedInterfaceMethod(); - $class->forbiddenInterfaceMethod(); // error: Method ForbidCustomFunctionsRule\SomeInterface::forbiddenInterfaceMethod() is forbidden. Description 6 - $class->forbiddenMethodOfParent(); // error: Method ForbidCustomFunctionsRule\SomeParent::forbiddenMethodOfParent() is forbidden. Description 8 + $class->forbiddenInterfaceMethod(); // error: Method ForbidCustomFunctionsRule\SomeInterface::forbiddenInterfaceMethod() (as ForbidCustomFunctionsRule\SomeClass::forbiddenInterfaceMethod()) is forbidden. Description 6 + $class->forbiddenMethodOfParent(); - $forbiddenClass->foo(); // error: Class ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods is forbidden. Description 2 - $forbiddenClass->bar(); // error: Class ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods is forbidden. Description 2 - $forbiddenClassChild->baz(); // error: Class ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods is forbidden. Description 2 + $forbiddenClass->foo(); // error: Method ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::foo() is forbidden. Description 2. [ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::foo() matches ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::*()] + $forbiddenClass->bar(); // error: Method ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::bar() is forbidden. Description 2. [ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::bar() matches ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::*()] + $forbiddenClassChild->baz(); $forbiddenConstructor->foo(); - $union->forbiddenMethod(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 + $union->forbiddenMethod(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() (as {ForbidCustomFunctionsRule\SomeClass,ForbidCustomFunctionsRule\AnotherClass}::forbiddenMethod()) is forbidden. Description 4 new class {}; - new class extends ClassWithForbiddenConstructor {}; // error: Method ForbidCustomFunctionsRule\ClassWithForbiddenConstructor::__construct() is forbidden. Description 3 - new class extends ClassWithForbiddenAllMethods {}; // error: Class ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods is forbidden. Description 2 + new class extends ClassWithForbiddenConstructor {}; + new class extends ClassWithForbiddenAllMethods {}; new ClassWithForbiddenConstructor(); // error: Method ForbidCustomFunctionsRule\ClassWithForbiddenConstructor::__construct() is forbidden. Description 3 - new ClassWithForbiddenAllMethods(); // error: Class ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods is forbidden. Description 2 + new ClassWithForbiddenAllMethods(); // error: Method ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::__construct() is forbidden. Description 2. [ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::__construct() matches ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::*()] $interface->allowedInterfaceMethod(); $interface->forbiddenInterfaceMethod(); // error: Method ForbidCustomFunctionsRule\SomeInterface::forbiddenInterfaceMethod() is forbidden. Description 6 - SomeClass::forbiddenInterfaceStaticMethod(); // error: Method ForbidCustomFunctionsRule\SomeInterface::forbiddenInterfaceStaticMethod() is forbidden. Description 7 + SomeClass::forbiddenInterfaceStaticMethod(); // error: Method ForbidCustomFunctionsRule\SomeInterface::forbiddenInterfaceStaticMethod() (as ForbidCustomFunctionsRule\SomeClass::forbiddenInterfaceStaticMethod()) is forbidden. Description 7 SomeClass::forbiddenStaticMethod(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5 - SomeClass::forbiddenStaticMethod(...); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5 + SomeClass::forbiddenStaticMethod(...); - forbidden_namespaced_function(); // error: Function ForbidCustomFunctionsRule\forbidden_namespaced_function() is forbidden. Description 1 - forbidden_namespaced_function(...); // error: Function ForbidCustomFunctionsRule\forbidden_namespaced_function() is forbidden. Description 1 + forbidden_namespaced_function(); + forbidden_namespaced_function(...); $forbiddenClassName = 'ForbidCustomFunctionsRule\ClassWithForbiddenConstructor'; $forbiddenMethodName = 'forbiddenMethod'; @@ -165,14 +165,14 @@ public function test( $forbiddenGlobalFunctionName = 'sleep'; $forbiddenFunctionName = 'ForbidCustomFunctionsRule\forbidden_namespaced_function'; - $forbiddenGlobalFunctionName(); // error: Function sleep() is forbidden. Description 0 - $forbiddenFunctionName(); // error: Function ForbidCustomFunctionsRule\forbidden_namespaced_function() is forbidden. Description 1 - $class->$forbiddenMethodName(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - $class::$forbiddenStaticMethodName(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5 - $class->getSelf()->$forbiddenMethodName(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenMethod() is forbidden. Description 4 - $class->getSelf()::$forbiddenStaticMethodName(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5 - $classString::$forbiddenStaticMethodName(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5 - $classStringOrTheClass::$forbiddenStaticMethodName(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5 + $forbiddenGlobalFunctionName(); + $forbiddenFunctionName(); + $class->$forbiddenMethodName(); + $class::$forbiddenStaticMethodName(); + $class->getSelf()->$forbiddenMethodName(); + $class->getSelf()::$forbiddenStaticMethodName(); + $classString::$forbiddenStaticMethodName(); + $classStringOrTheClass::$forbiddenStaticMethodName(); new $forbiddenClassName(); // error: Method ForbidCustomFunctionsRule\ClassWithForbiddenConstructor::__construct() is forbidden. Description 3 } From 76bf09753bfc0fc14800d8775cf48875127ebc3e Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 3 Dec 2024 15:28:04 +0100 Subject: [PATCH 2/2] Fix config for namespaced function --- compare.phpstan.neon | 3 ++- tests/Rule/data/ForbidCustomFunctionsRule/code.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compare.phpstan.neon b/compare.phpstan.neon index 2099f56..e8a956b 100644 --- a/compare.phpstan.neon +++ b/compare.phpstan.neon @@ -10,10 +10,11 @@ parameters: function: 'sleep' message: 'Description 0' - disallowedMethodCalls: - method: 'ForbidCustomFunctionsRule\forbidden_namespaced_function' message: 'Description 1' + + disallowedMethodCalls: - method: 'ForbidCustomFunctionsRule\ClassWithForbiddenAllMethods::*' message: 'Description 2' diff --git a/tests/Rule/data/ForbidCustomFunctionsRule/code.php b/tests/Rule/data/ForbidCustomFunctionsRule/code.php index 91405f7..3606098 100644 --- a/tests/Rule/data/ForbidCustomFunctionsRule/code.php +++ b/tests/Rule/data/ForbidCustomFunctionsRule/code.php @@ -156,7 +156,7 @@ public function test( SomeClass::forbiddenStaticMethod(); // error: Method ForbidCustomFunctionsRule\SomeClass::forbiddenStaticMethod() is forbidden. Description 5 SomeClass::forbiddenStaticMethod(...); - forbidden_namespaced_function(); + forbidden_namespaced_function(); // error: Method ForbidCustomFunctionsRule\forbidden_namespaced_function() (as forbidden_namespaced_function()) is forbidden. Description 1 forbidden_namespaced_function(...); $forbiddenClassName = 'ForbidCustomFunctionsRule\ClassWithForbiddenConstructor';