diff --git a/.gitattributes b/.gitattributes index 51b431136..9cd1bb7da 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,3 +15,4 @@ .travis.yml export-ignore appveyor.yml export-ignore phpunit.xml.dist export-ignore +/phpstan/ export-ignore diff --git a/composer.json b/composer.json index d1f98a56a..435aa5c01 100644 --- a/composer.json +++ b/composer.json @@ -66,8 +66,13 @@ }, "autoload-dev": { "psr-4": { - "Composer\\Test\\": "tests/Composer/Test" - } + "Composer\\Test\\": "tests/Composer/Test", + "Composer\\PHPStanRules\\": "phpstan/Rules/src", + "Composer\\PHPStanRulesTests\\": "phpstan/Rules/tests" + }, + "classmap": [ + "phpstan/Rules/tests/data" + ] }, "bin": [ "bin/composer" diff --git a/phpstan/Rules/src/AnonymousFunctionWithThisRule.php b/phpstan/Rules/src/AnonymousFunctionWithThisRule.php new file mode 100644 index 000000000..d9a44ecb5 --- /dev/null +++ b/phpstan/Rules/src/AnonymousFunctionWithThisRule.php @@ -0,0 +1,46 @@ + + */ +final class AnonymousFunctionWithThisRule implements Rule +{ + /** + * @inheritDoc + */ + public function getNodeType(): string + { + return \PhpParser\Node\Expr\Variable::class; + } + + /** + * @inheritDoc + */ + public function processNode(Node $node, Scope $scope): array + { + if (!\is_string($node->name) || $node->name !== 'this') { + return []; + } + + if ($scope->isInClosureBind()) { + return []; + } + + if (!$scope->isInClass()) { + // reported in other standard rule on level 0 + return []; + } + + if ($scope->isInAnonymousFunction()) { + return ['Using $this inside anonymous function is prohibited because of PHP 5.3 support.']; + } + + return []; + } +} diff --git a/phpstan/Rules/tests/AnonymousFunctionWithThisRuleTest.php b/phpstan/Rules/tests/AnonymousFunctionWithThisRuleTest.php new file mode 100644 index 000000000..add285d17 --- /dev/null +++ b/phpstan/Rules/tests/AnonymousFunctionWithThisRuleTest.php @@ -0,0 +1,28 @@ + + */ +final class AnonymousFunctionWithThisRuleTest extends RuleTestCase +{ + /** + * @inheritDoc + */ + protected function getRule(): \PHPStan\Rules\Rule + { + return new AnonymousFunctionWithThisRule(); + } + + public function testWithThis(): void + { + $this->analyse([__DIR__ . '/data/method-with-this.php'], [ + ['Using $this inside anonymous function is prohibited because of PHP 5.3 support.', 13], + ['Using $this inside anonymous function is prohibited because of PHP 5.3 support.', 17], + ]); + } +} diff --git a/phpstan/Rules/tests/data/method-with-this.php b/phpstan/Rules/tests/data/method-with-this.php new file mode 100644 index 000000000..e0b15bffa --- /dev/null +++ b/phpstan/Rules/tests/data/method-with-this.php @@ -0,0 +1,34 @@ +firstProp; + }; + + call_user_func(function() { + $this->funMethod(); + }, $this); + + $bind = 'bind'; + function() use($bind) { + + }; + } +} + +function global_ok() { + $_SERVER['REMOTE_ADDR']; +} + +function global_this() { + // not checked by our rule, it is checked with standard phpstan rule on level 0 + $this['REMOTE_ADDR']; +} diff --git a/phpstan/config.neon b/phpstan/config.neon index 09b42ed27..599d27d51 100644 --- a/phpstan/config.neon +++ b/phpstan/config.neon @@ -23,6 +23,15 @@ parameters: # we don't have different constructors for parent/child - '~^Unsafe usage of new static\(\)\.$~' + + # hhvm should have support for $this in closures + - + count: 1 + message: '~^Using \$this inside anonymous function is prohibited because of PHP 5\.3 support\.$~' + path: '../tests/Composer/Test/Repository/PlatformRepositoryTest.php' paths: - ../src - ../tests + +rules: + - Composer\PHPStanRules\AnonymousFunctionWithThisRule