clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
[*.{php,phpt}]
indent_style = tab
indent_size = 4
[*.xml]
indent_style = tab
indent_size = 4
[*.neon]
indent_style = tab
indent_size = 4
[*.{yaml,yml}]
indent_style = space
indent_size = 2
[composer.json]
indent_style = tab
indent_size = 4

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Ondřej Mirtes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,83 @@
# Rules for detecting usage of deprecated classes, methods, properties, constants and traits.
[![Build](https://github.com/phpstan/phpstan-deprecation-rules/workflows/Build/badge.svg)](https://github.com/phpstan/phpstan-deprecation-rules/actions)
[![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-deprecation-rules/v/stable)](https://packagist.org/packages/phpstan/phpstan-deprecation-rules)
[![License](https://poser.pugx.org/phpstan/phpstan-deprecation-rules/license)](https://packagist.org/packages/phpstan/phpstan-deprecation-rules)
* [PHPStan](https://phpstan.org/)
## Installation
To use this extension, require it in [Composer](https://getcomposer.org/):
```
composer require --dev phpstan/phpstan-deprecation-rules
```
If you also install [phpstan/extension-installer](https://github.com/phpstan/extension-installer) then you're all set!
<details>
<summary>Manual installation</summary>
If you don't want to use `phpstan/extension-installer`, include rules.neon in your project's PHPStan config:
```
includes:
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
```
</details>
## Deprecating code you don't own
This extension emits deprecation warnings on code, which uses properties/functions/methods/classes which are annotated as `@deprecated`.
In case you don't own the code which you want to be considered deprecated, use [PHPStan Stub Files](https://phpstan.org/user-guide/stub-files) to declare deprecations for vendor files like:
```
/** @deprecated */
class ThirdPartyClass {}
```
## Custom deprecated scopes
Usage of deprecated code is not reported in code that is also deprecated:
```php
/** @deprecated */
function doFoo(): void
{
// not reported:
anotherDeprecatedFunction();
}
```
If you have [a different way](https://github.com/phpstan/phpstan-deprecation-rules/issues/64) of marking code that calls deprecated symbols on purpose and you don't want these calls to be reported either, you can write an extension by implementing the [`DeprecatedScopeResolver`](https://github.com/phpstan/phpstan-deprecation-rules/blob/1.1.x/src/Rules/Deprecations/DeprecatedScopeResolver.php) interface.
For example if you mark your PHPUnit tests that test deprecated code with `@group legacy`, you can implement the extension this way:
```php
class GroupLegacyScopeResolver implements DeprecatedScopeResolver
{
public function isScopeDeprecated(Scope $scope): bool
{
$function = $scope->getFunction();
return $function !== null
&& $function->getDocComment() !== null
&& strpos($function->getDocComment(), '@group legacy') !== false;
}
}
```
And register it in your [configuration file](https://phpstan.org/config-reference):
```neon
services:
-
class: GroupLegacyScopeResolver
tags:
- phpstan.deprecations.deprecatedScopeResolver
```
[Learn more about Scope](https://phpstan.org/developing-extensions/scope), a core concept for implementing custom PHPStan extensions.

View File

@@ -0,0 +1,42 @@
{
"name": "phpstan/phpstan-deprecation-rules",
"type": "phpstan-extension",
"description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
"license": [
"MIT"
],
"require": {
"php": "^7.2 || ^8.0",
"phpstan/phpstan": "^1.12"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^9.5"
},
"config": {
"platform": {
"php": "7.4.6"
},
"sort-packages": true
},
"extra": {
"phpstan": {
"includes": [
"rules.neon"
]
}
},
"autoload": {
"psr-4": {
"PHPStan\\": "src/"
}
},
"autoload-dev": {
"classmap": [
"tests/"
]
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1,35 @@
parameters:
deprecationRulesInstalled: true
services:
-
class: PHPStan\Rules\Deprecations\DeprecatedClassHelper
-
class: PHPStan\DependencyInjection\LazyDeprecatedScopeResolverProvider
-
class: PHPStan\Rules\Deprecations\DeprecatedScopeHelper
factory: @PHPStan\DependencyInjection\LazyDeprecatedScopeResolverProvider::get
-
class: PHPStan\Rules\Deprecations\DefaultDeprecatedScopeResolver
tags:
- phpstan.deprecations.deprecatedScopeResolver
rules:
- PHPStan\Rules\Deprecations\AccessDeprecatedPropertyRule
- PHPStan\Rules\Deprecations\AccessDeprecatedStaticPropertyRule
- PHPStan\Rules\Deprecations\CallToDeprecatedFunctionRule
- PHPStan\Rules\Deprecations\CallToDeprecatedMethodRule
- PHPStan\Rules\Deprecations\CallToDeprecatedStaticMethodRule
- PHPStan\Rules\Deprecations\FetchingClassConstOfDeprecatedClassRule
- PHPStan\Rules\Deprecations\FetchingDeprecatedConstRule
- PHPStan\Rules\Deprecations\ImplementationOfDeprecatedInterfaceRule
- PHPStan\Rules\Deprecations\InheritanceOfDeprecatedClassRule
- PHPStan\Rules\Deprecations\InheritanceOfDeprecatedInterfaceRule
- PHPStan\Rules\Deprecations\InstantiationOfDeprecatedClassRule
- PHPStan\Rules\Deprecations\TypeHintDeprecatedInClassMethodSignatureRule
- PHPStan\Rules\Deprecations\TypeHintDeprecatedInClosureSignatureRule
- PHPStan\Rules\Deprecations\TypeHintDeprecatedInFunctionSignatureRule
- PHPStan\Rules\Deprecations\UsageOfDeprecatedCastRule
- PHPStan\Rules\Deprecations\UsageOfDeprecatedTraitRule

View File

@@ -0,0 +1,33 @@
<?php declare(strict_types = 1);
namespace PHPStan\DependencyInjection;
use PHPStan\Rules\Deprecations\DeprecatedScopeHelper;
final class LazyDeprecatedScopeResolverProvider
{
public const EXTENSION_TAG = 'phpstan.deprecations.deprecatedScopeResolver';
/** @var Container */
private $container;
/** @var DeprecatedScopeHelper */
private $scopeHelper;
public function __construct(Container $container)
{
$this->container = $container;
}
public function get(): DeprecatedScopeHelper
{
if ($this->scopeHelper === null) {
$this->scopeHelper = new DeprecatedScopeHelper(
$this->container->getServicesByTag(self::EXTENSION_TAG)
);
}
return $this->scopeHelper;
}
}

View File

@@ -0,0 +1,92 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Identifier;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\MissingPropertyFromReflectionException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;
use function strtolower;
/**
* @implements Rule<PropertyFetch>
*/
class AccessDeprecatedPropertyRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return PropertyFetch::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
if (!$node->name instanceof Identifier) {
return [];
}
$propertyName = $node->name->name;
$propertyAccessedOnType = $scope->getType($node->var);
$referencedClasses = $propertyAccessedOnType->getObjectClassNames();
foreach ($referencedClasses as $referencedClass) {
try {
$classReflection = $this->reflectionProvider->getClass($referencedClass);
$propertyReflection = $classReflection->getProperty($propertyName, $scope);
if ($propertyReflection->isDeprecated()->yes()) {
$description = $propertyReflection->getDeprecatedDescription();
if ($description === null) {
return [
RuleErrorBuilder::message(sprintf(
'Access to deprecated property $%s of %s %s.',
$propertyName,
strtolower($propertyReflection->getDeclaringClass()->getClassTypeDescription()),
$propertyReflection->getDeclaringClass()->getName()
))->identifier('property.deprecated')->build(),
];
}
return [
RuleErrorBuilder::message(sprintf(
"Access to deprecated property $%s of %s %s:\n%s",
$propertyName,
strtolower($propertyReflection->getDeclaringClass()->getClassTypeDescription()),
$propertyReflection->getDeclaringClass()->getName(),
$description
))->identifier('property.deprecated')->build(),
];
}
} catch (ClassNotFoundException $e) {
// Other rules will notify if the class is not found
} catch (MissingPropertyFromReflectionException $e) {
// Other rules will notify if the property is not found
}
}
return [];
}
}

View File

@@ -0,0 +1,118 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\MissingPropertyFromReflectionException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Type\ErrorType;
use PHPStan\Type\Type;
use function sprintf;
use function strtolower;
/**
* @implements Rule<StaticPropertyFetch>
*/
class AccessDeprecatedStaticPropertyRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var RuleLevelHelper */
private $ruleLevelHelper;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->ruleLevelHelper = $ruleLevelHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return StaticPropertyFetch::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
if (!$node->name instanceof Identifier) {
return [];
}
$propertyName = $node->name->name;
$referencedClasses = [];
if ($node->class instanceof Name) {
$referencedClasses[] = $scope->resolveName($node->class);
} else {
$classTypeResult = $this->ruleLevelHelper->findTypeToCheck(
$scope,
$node->class,
'', // We don't care about the error message
static function (Type $type) use ($propertyName): bool {
return $type->canAccessProperties()->yes() && $type->hasProperty($propertyName)->yes();
}
);
if ($classTypeResult->getType() instanceof ErrorType) {
return [];
}
$referencedClasses = $classTypeResult->getReferencedClasses();
}
foreach ($referencedClasses as $referencedClass) {
try {
$class = $this->reflectionProvider->getClass($referencedClass);
$property = $class->getProperty($propertyName, $scope);
} catch (ClassNotFoundException $e) {
continue;
} catch (MissingPropertyFromReflectionException $e) {
continue;
}
if ($property->isDeprecated()->yes()) {
$description = $property->getDeprecatedDescription();
if ($description === null) {
return [
RuleErrorBuilder::message(sprintf(
'Access to deprecated static property $%s of %s %s.',
$propertyName,
strtolower($property->getDeclaringClass()->getClassTypeDescription()),
$property->getDeclaringClass()->getName()
))->identifier('staticProperty.deprecated')->build(),
];
}
return [
RuleErrorBuilder::message(sprintf(
"Access to deprecated static property $%s of %s %s:\n%s",
$propertyName,
strtolower($property->getDeclaringClass()->getClassTypeDescription()),
$property->getDeclaringClass()->getName(),
$description
))->identifier('staticProperty.deprecated')->build(),
];
}
}
return [];
}
}

View File

@@ -0,0 +1,78 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\FunctionNotFoundException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;
/**
* @implements Rule<FuncCall>
*/
class CallToDeprecatedFunctionRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return FuncCall::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
if (!($node->name instanceof Name)) {
return [];
}
try {
$function = $this->reflectionProvider->getFunction($node->name, $scope);
} catch (FunctionNotFoundException $e) {
// Other rules will notify if the function is not found
return [];
}
if ($function->isDeprecated()->yes()) {
$description = $function->getDeprecatedDescription();
if ($description === null) {
return [
RuleErrorBuilder::message(sprintf(
'Call to deprecated function %s().',
$function->getName()
))->identifier('function.deprecated')->build(),
];
}
return [
RuleErrorBuilder::message(sprintf(
"Call to deprecated function %s():\n%s",
$function->getName(),
$description
))->identifier('function.deprecated')->build(),
];
}
return [];
}
}

View File

@@ -0,0 +1,94 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Identifier;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\MissingMethodFromReflectionException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;
use function strtolower;
/**
* @implements Rule<MethodCall>
*/
class CallToDeprecatedMethodRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return MethodCall::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
if (!$node->name instanceof Identifier) {
return [];
}
$methodName = $node->name->name;
$methodCalledOnType = $scope->getType($node->var);
$referencedClasses = $methodCalledOnType->getObjectClassNames();
foreach ($referencedClasses as $referencedClass) {
try {
$classReflection = $this->reflectionProvider->getClass($referencedClass);
$methodReflection = $classReflection->getMethod($methodName, $scope);
if (!$methodReflection->isDeprecated()->yes()) {
continue;
}
$description = $methodReflection->getDeprecatedDescription();
if ($description === null) {
return [
RuleErrorBuilder::message(sprintf(
'Call to deprecated method %s() of %s %s.',
$methodReflection->getName(),
strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()),
$methodReflection->getDeclaringClass()->getName()
))->identifier('method.deprecated')->build(),
];
}
return [
RuleErrorBuilder::message(sprintf(
"Call to deprecated method %s() of %s %s:\n%s",
$methodReflection->getName(),
strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()),
$methodReflection->getDeclaringClass()->getName(),
$description
))->identifier('method.deprecated')->build(),
];
} catch (ClassNotFoundException $e) {
// Other rules will notify if the class is not found
} catch (MissingMethodFromReflectionException $e) {
// Other rules will notify if the the method is not found
}
}
return [];
}
}

View File

@@ -0,0 +1,138 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\MissingMethodFromReflectionException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Type\ErrorType;
use PHPStan\Type\Type;
use function sprintf;
use function strtolower;
/**
* @implements Rule<StaticCall>
*/
class CallToDeprecatedStaticMethodRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var RuleLevelHelper */
private $ruleLevelHelper;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->ruleLevelHelper = $ruleLevelHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return StaticCall::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
if (!$node->name instanceof Identifier) {
return [];
}
$methodName = $node->name->name;
$referencedClasses = [];
if ($node->class instanceof Name) {
$referencedClasses[] = $scope->resolveName($node->class);
} else {
$classTypeResult = $this->ruleLevelHelper->findTypeToCheck(
$scope,
$node->class,
'', // We don't care about the error message
static function (Type $type) use ($methodName): bool {
return $type->canCallMethods()->yes() && $type->hasMethod($methodName)->yes();
}
);
if ($classTypeResult->getType() instanceof ErrorType) {
return [];
}
$referencedClasses = $classTypeResult->getReferencedClasses();
}
$errors = [];
foreach ($referencedClasses as $referencedClass) {
try {
$class = $this->reflectionProvider->getClass($referencedClass);
$methodReflection = $class->getMethod($methodName, $scope);
} catch (ClassNotFoundException $e) {
continue;
} catch (MissingMethodFromReflectionException $e) {
continue;
}
if ($class->isDeprecated()) {
$classDescription = $class->getDeprecatedDescription();
if ($classDescription === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Call to method %s() of deprecated %s %s.',
$methodReflection->getName(),
strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()),
$methodReflection->getDeclaringClass()->getName()
))->identifier(sprintf('staticMethod.deprecated%s', $methodReflection->getDeclaringClass()->getClassTypeDescription()))->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Call to method %s() of deprecated %s %s:\n%s",
$methodReflection->getName(),
strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()),
$methodReflection->getDeclaringClass()->getName(),
$classDescription
))->identifier(sprintf('staticMethod.deprecated%s', $methodReflection->getDeclaringClass()->getClassTypeDescription()))->build();
}
}
if (!$methodReflection->isDeprecated()->yes()) {
continue;
}
$description = $methodReflection->getDeprecatedDescription();
if ($description === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Call to deprecated method %s() of %s %s.',
$methodReflection->getName(),
strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()),
$methodReflection->getDeclaringClass()->getName()
))->identifier('staticMethod.deprecated')->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Call to deprecated method %s() of %s %s:\n%s",
$methodReflection->getName(),
strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()),
$methodReflection->getDeclaringClass()->getName(),
$description
))->identifier('staticMethod.deprecated')->build();
}
}
return $errors;
}
}

View File

@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PHPStan\Analyser\Scope;
final class DefaultDeprecatedScopeResolver implements DeprecatedScopeResolver
{
public function isScopeDeprecated(Scope $scope): bool
{
$class = $scope->getClassReflection();
if ($class !== null && $class->isDeprecated()) {
return true;
}
$trait = $scope->getTraitReflection();
if ($trait !== null && $trait->isDeprecated()) {
return true;
}
$function = $scope->getFunction();
if ($function !== null && $function->isDeprecated()->yes()) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,55 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use function sprintf;
class DeprecatedClassHelper
{
/** @var ReflectionProvider */
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
{
$this->reflectionProvider = $reflectionProvider;
}
public function getClassDeprecationDescription(ClassReflection $class): string
{
$description = $class->getDeprecatedDescription();
if ($description === null) {
return '.';
}
return sprintf(":\n%s", $description);
}
/**
* @param string[] $referencedClasses
* @return ClassReflection[]
*/
public function filterDeprecatedClasses(array $referencedClasses): array
{
$deprecatedClasses = [];
foreach ($referencedClasses as $referencedClass) {
try {
$class = $this->reflectionProvider->getClass($referencedClass);
} catch (ClassNotFoundException $e) {
continue;
}
if (!$class->isDeprecated()) {
continue;
}
$deprecatedClasses[] = $class;
}
return $deprecatedClasses;
}
}

View File

@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PHPStan\Analyser\Scope;
class DeprecatedScopeHelper
{
/** @var DeprecatedScopeResolver[] */
private $resolvers;
/**
* @param DeprecatedScopeResolver[] $checkers
*/
public function __construct(array $checkers)
{
$this->resolvers = $checkers;
}
public function isScopeDeprecated(Scope $scope): bool
{
foreach ($this->resolvers as $checker) {
if ($checker->isScopeDeprecated($scope)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PHPStan\Analyser\Scope;
/**
* This is the interface for custom deprecated scope resolvers.
*
* To register it in the configuration file use the `phpstan.deprecations.deprecatedScopeResolver` service tag:
*
* ```
* services:
* -
* class: App\PHPStan\MyExtension
* tags:
* - phpstan.deprecations.deprecatedScopeResolver
* ```
*
* @api
*/
interface DeprecatedScopeResolver
{
public function isScopeDeprecated(Scope $scope): bool;
}

View File

@@ -0,0 +1,144 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Type\ErrorType;
use PHPStan\Type\Type;
use function sprintf;
use function strtolower;
/**
* @implements Rule<ClassConstFetch>
*/
class FetchingClassConstOfDeprecatedClassRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var RuleLevelHelper */
private $ruleLevelHelper;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->ruleLevelHelper = $ruleLevelHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return ClassConstFetch::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
if (!$node->name instanceof Identifier) {
return [];
}
$constantName = $node->name->name;
$referencedClasses = [];
if ($node->class instanceof Name) {
$referencedClasses[] = $scope->resolveName($node->class);
} else {
$classTypeResult = $this->ruleLevelHelper->findTypeToCheck(
$scope,
$node->class,
'', // We don't care about the error message
static function (Type $type) use ($constantName): bool {
return $type->canAccessConstants()->yes() && $type->hasConstant($constantName)->yes();
}
);
if ($classTypeResult->getType() instanceof ErrorType) {
return [];
}
$referencedClasses = $classTypeResult->getReferencedClasses();
}
$errors = [];
foreach ($referencedClasses as $referencedClass) {
try {
$class = $this->reflectionProvider->getClass($referencedClass);
} catch (ClassNotFoundException $e) {
continue;
}
if ($class->isDeprecated()) {
$classDescription = $class->getDeprecatedDescription();
if ($classDescription === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Fetching class constant %s of deprecated %s %s.',
$constantName,
strtolower($class->getClassTypeDescription()),
$referencedClass
))->identifier(sprintf('classConstant.deprecated%s', $class->getClassTypeDescription()))->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Fetching class constant %s of deprecated %s %s:\n%s",
$constantName,
strtolower($class->getClassTypeDescription()),
$referencedClass,
$classDescription
))->identifier(sprintf('classConstant.deprecated%s', $class->getClassTypeDescription()))->build();
}
}
if (strtolower($constantName) === 'class') {
continue;
}
if (!$class->hasConstant($constantName)) {
continue;
}
$constantReflection = $class->getConstant($constantName);
if (!$constantReflection->isDeprecated()->yes()) {
continue;
}
$description = $constantReflection->getDeprecatedDescription();
if ($description === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Fetching deprecated class constant %s of %s %s.',
$constantName,
strtolower($class->getClassTypeDescription()),
$referencedClass
))->identifier('classConstant.deprecated')->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Fetching deprecated class constant %s of %s %s:\n%s",
$constantName,
strtolower($class->getClassTypeDescription()),
$referencedClass,
$description
))->identifier('classConstant.deprecated')->build();
}
}
return $errors;
}
}

View File

@@ -0,0 +1,60 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Expr\ConstFetch;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;
/**
* @implements Rule<ConstFetch>
*/
class FetchingDeprecatedConstRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return ConstFetch::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
if (!$this->reflectionProvider->hasConstant($node->name, $scope)) {
return [];
}
$constantReflection = $this->reflectionProvider->getConstant($node->name, $scope);
if ($constantReflection->isDeprecated()->yes()) {
return [
RuleErrorBuilder::message(sprintf(
$constantReflection->getDeprecatedDescription() ?? 'Use of constant %s is deprecated.',
$constantReflection->getName()
))->identifier('constant.deprecated')->build(),
];
}
return [];
}
}

View File

@@ -0,0 +1,105 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;
/**
* @implements Rule<Class_>
*/
class ImplementationOfDeprecatedInterfaceRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return Class_::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
$errors = [];
$className = isset($node->namespacedName)
? (string) $node->namespacedName
: (string) $node->name;
try {
$class = $this->reflectionProvider->getClass($className);
} catch (ClassNotFoundException $e) {
return [];
}
if ($class->isDeprecated()) {
return [];
}
foreach ($node->implements as $implement) {
$interfaceName = (string) $implement;
try {
$interface = $this->reflectionProvider->getClass($interfaceName);
if ($interface->isDeprecated()) {
$description = $interface->getDeprecatedDescription();
if (!$class->isAnonymous()) {
if ($description === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Class %s implements deprecated interface %s.',
$className,
$interfaceName
))->identifier('class.implementsDeprecatedInterface')->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Class %s implements deprecated interface %s:\n%s",
$className,
$interfaceName,
$description
))->identifier('class.implementsDeprecatedInterface')->build();
}
} else {
if ($description === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Anonymous class implements deprecated interface %s.',
$interfaceName
))->identifier('class.implementsDeprecatedInterface')->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Anonymous class implements deprecated interface %s:\n%s",
$interfaceName,
$description
))->identifier('class.implementsDeprecatedInterface')->build();
}
}
}
} catch (ClassNotFoundException $e) {
// Other rules will notify if the interface is not found
}
}
return $errors;
}
}

View File

@@ -0,0 +1,102 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;
/**
* @implements Rule<Class_>
*/
class InheritanceOfDeprecatedClassRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return Class_::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
if ($node->extends === null) {
return [];
}
$errors = [];
$className = isset($node->namespacedName)
? (string) $node->namespacedName
: (string) $node->name;
try {
$class = $this->reflectionProvider->getClass($className);
} catch (ClassNotFoundException $e) {
return [];
}
$parentClassName = (string) $node->extends;
try {
$parentClass = $this->reflectionProvider->getClass($parentClassName);
$description = $parentClass->getDeprecatedDescription();
if ($parentClass->isDeprecated()) {
if (!$class->isAnonymous()) {
if ($description === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Class %s extends deprecated class %s.',
$className,
$parentClassName
))->identifier('class.extendsDeprecatedClass')->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Class %s extends deprecated class %s:\n%s",
$className,
$parentClassName,
$description
))->identifier('class.extendsDeprecatedClass')->build();
}
} else {
if ($description === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Anonymous class extends deprecated class %s.',
$parentClassName
))->identifier('class.extendsDeprecatedClass')->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Anonymous class extends deprecated class %s:\n%s",
$parentClassName,
$description
))->identifier('class.extendsDeprecatedClass')->build();
}
}
}
} catch (ClassNotFoundException $e) {
// Other rules will notify if the interface is not found
}
return $errors;
}
}

View File

@@ -0,0 +1,84 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Stmt\Interface_;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;
/**
* @implements Rule<Interface_>
*/
class InheritanceOfDeprecatedInterfaceRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
{
$this->reflectionProvider = $reflectionProvider;
}
public function getNodeType(): string
{
return Interface_::class;
}
public function processNode(Node $node, Scope $scope): array
{
$interfaceName = isset($node->namespacedName)
? (string) $node->namespacedName
: (string) $node->name;
try {
$interface = $this->reflectionProvider->getClass($interfaceName);
} catch (ClassNotFoundException $e) {
return [];
}
if ($interface->isDeprecated()) {
return [];
}
$errors = [];
foreach ($node->extends as $parentInterfaceName) {
$parentInterfaceName = (string) $parentInterfaceName;
try {
$parentInterface = $this->reflectionProvider->getClass($parentInterfaceName);
if (!$parentInterface->isDeprecated()) {
continue;
}
$description = $parentInterface->getDeprecatedDescription();
if ($description === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Interface %s extends deprecated interface %s.',
$interfaceName,
$parentInterfaceName
))->identifier('interface.extendsDeprecatedInterface')->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Interface %s extends deprecated interface %s:\n%s",
$interfaceName,
$parentInterfaceName,
$description
))->identifier('interface.extendsDeprecatedInterface')->build();
}
} catch (ClassNotFoundException $e) {
// Other rules will notify if the interface is not found
}
}
return $errors;
}
}

View File

@@ -0,0 +1,109 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Rules\RuleLevelHelper;
use PHPStan\Type\ErrorType;
use function sprintf;
/**
* @implements Rule<New_>
*/
class InstantiationOfDeprecatedClassRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var RuleLevelHelper */
private $ruleLevelHelper;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, RuleLevelHelper $ruleLevelHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->ruleLevelHelper = $ruleLevelHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return New_::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
$referencedClasses = [];
if ($node->class instanceof Name) {
$referencedClasses[] = $scope->resolveName($node->class);
} elseif ($node->class instanceof Class_) {
if (!isset($node->class->namespacedName)) {
return [];
}
$referencedClasses[] = $scope->resolveName($node->class->namespacedName);
} else {
$classTypeResult = $this->ruleLevelHelper->findTypeToCheck(
$scope,
$node->class,
'', // We don't care about the error message
static function (): bool {
return true;
}
);
if ($classTypeResult->getType() instanceof ErrorType) {
return [];
}
$referencedClasses = $classTypeResult->getReferencedClasses();
}
$errors = [];
foreach ($referencedClasses as $referencedClass) {
try {
$class = $this->reflectionProvider->getClass($referencedClass);
} catch (ClassNotFoundException $e) {
continue;
}
if (!$class->isDeprecated()) {
continue;
}
$description = $class->getDeprecatedDescription();
if ($description === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Instantiation of deprecated class %s.',
$referencedClass
))->identifier('new.deprecated')->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Instantiation of deprecated class %s:\n%s",
$referencedClass,
$description
))->identifier('new.deprecated')->build();
}
}
return $errors;
}
}

View File

@@ -0,0 +1,98 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassMethodNode;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;
use function strtolower;
/**
* @implements Rule<InClassMethodNode>
*/
class TypeHintDeprecatedInClassMethodSignatureRule implements Rule
{
/** @var DeprecatedClassHelper */
private $deprecatedClassHelper;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(DeprecatedClassHelper $deprecatedClassHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->deprecatedClassHelper = $deprecatedClassHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return InClassMethodNode::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
$method = $node->getMethodReflection();
$methodSignature = ParametersAcceptorSelector::selectSingle($method->getVariants());
$errors = [];
foreach ($methodSignature->getParameters() as $parameter) {
$deprecatedClasses = $this->deprecatedClassHelper->filterDeprecatedClasses($parameter->getType()->getReferencedClasses());
foreach ($deprecatedClasses as $deprecatedClass) {
if ($method->getDeclaringClass()->isAnonymous()) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Parameter $%s of method %s() in anonymous class has typehint with deprecated %s %s%s',
$parameter->getName(),
$method->getName(),
strtolower($deprecatedClass->getClassTypeDescription()),
$deprecatedClass->getName(),
$this->deprecatedClassHelper->getClassDeprecationDescription($deprecatedClass)
))->identifier(sprintf('parameter.deprecated%s', $deprecatedClass->getClassTypeDescription()))->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
'Parameter $%s of method %s::%s() has typehint with deprecated %s %s%s',
$parameter->getName(),
$method->getDeclaringClass()->getName(),
$method->getName(),
strtolower($deprecatedClass->getClassTypeDescription()),
$deprecatedClass->getName(),
$this->deprecatedClassHelper->getClassDeprecationDescription($deprecatedClass)
))->identifier(sprintf('parameter.deprecated%s', $deprecatedClass->getClassTypeDescription()))->build();
}
}
}
$deprecatedClasses = $this->deprecatedClassHelper->filterDeprecatedClasses($methodSignature->getReturnType()->getReferencedClasses());
foreach ($deprecatedClasses as $deprecatedClass) {
if ($method->getDeclaringClass()->isAnonymous()) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Return type of method %s() in anonymous class has typehint with deprecated %s %s%s',
$method->getName(),
strtolower($deprecatedClass->getClassTypeDescription()),
$deprecatedClass->getName(),
$this->deprecatedClassHelper->getClassDeprecationDescription($deprecatedClass)
))->identifier(sprintf('return.deprecated%s', $deprecatedClass->getClassTypeDescription()))->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
'Return type of method %s::%s() has typehint with deprecated %s %s%s',
$method->getDeclaringClass()->getName(),
$method->getName(),
strtolower($deprecatedClass->getClassTypeDescription()),
$deprecatedClass->getName(),
$this->deprecatedClassHelper->getClassDeprecationDescription($deprecatedClass)
))->identifier(sprintf('return.deprecated%s', $deprecatedClass->getClassTypeDescription()))->build();
}
}
return $errors;
}
}

View File

@@ -0,0 +1,75 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClosureNode;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\ShouldNotHappenException;
use function sprintf;
use function strtolower;
/**
* @implements Rule<InClosureNode>
*/
class TypeHintDeprecatedInClosureSignatureRule implements Rule
{
/** @var DeprecatedClassHelper */
private $deprecatedClassHelper;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(DeprecatedClassHelper $deprecatedClassHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->deprecatedClassHelper = $deprecatedClassHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return InClosureNode::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
$functionSignature = $scope->getAnonymousFunctionReflection();
if ($functionSignature === null) {
throw new ShouldNotHappenException();
}
$errors = [];
foreach ($functionSignature->getParameters() as $parameter) {
$deprecatedClasses = $this->deprecatedClassHelper->filterDeprecatedClasses($parameter->getType()->getReferencedClasses());
foreach ($deprecatedClasses as $deprecatedClass) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Parameter $%s of anonymous function has typehint with deprecated %s %s%s',
$parameter->getName(),
strtolower($deprecatedClass->getClassTypeDescription()),
$deprecatedClass->getName(),
$this->deprecatedClassHelper->getClassDeprecationDescription($deprecatedClass)
))->identifier(sprintf('parameter.deprecated%s', $deprecatedClass->getClassTypeDescription()))->build();
}
}
$deprecatedClasses = $this->deprecatedClassHelper->filterDeprecatedClasses($functionSignature->getReturnType()->getReferencedClasses());
foreach ($deprecatedClasses as $deprecatedClass) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Return type of anonymous function has typehint with deprecated %s %s%s',
strtolower($deprecatedClass->getClassTypeDescription()),
$deprecatedClass->getName(),
$this->deprecatedClassHelper->getClassDeprecationDescription($deprecatedClass)
))->identifier(sprintf('return.deprecated%s', $deprecatedClass->getClassTypeDescription()))->build();
}
return $errors;
}
}

View File

@@ -0,0 +1,79 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InFunctionNode;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\ShouldNotHappenException;
use function sprintf;
use function strtolower;
/**
* @implements Rule<InFunctionNode>
*/
class TypeHintDeprecatedInFunctionSignatureRule implements Rule
{
/** @var DeprecatedClassHelper */
private $deprecatedClassHelper;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(DeprecatedClassHelper $deprecatedClassHelper, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->deprecatedClassHelper = $deprecatedClassHelper;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return InFunctionNode::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
$function = $scope->getFunction();
if ($function === null) {
throw new ShouldNotHappenException();
}
$functionSignature = ParametersAcceptorSelector::selectSingle($function->getVariants());
$errors = [];
foreach ($functionSignature->getParameters() as $parameter) {
$deprecatedClasses = $this->deprecatedClassHelper->filterDeprecatedClasses($parameter->getType()->getReferencedClasses());
foreach ($deprecatedClasses as $deprecatedClass) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Parameter $%s of function %s() has typehint with deprecated %s %s%s',
$parameter->getName(),
$function->getName(),
strtolower($deprecatedClass->getClassTypeDescription()),
$deprecatedClass->getName(),
$this->deprecatedClassHelper->getClassDeprecationDescription($deprecatedClass)
))->identifier(sprintf('parameter.deprecated%s', $deprecatedClass->getClassTypeDescription()))->build();
}
}
$deprecatedClasses = $this->deprecatedClassHelper->filterDeprecatedClasses($functionSignature->getReturnType()->getReferencedClasses());
foreach ($deprecatedClasses as $deprecatedClass) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Return type of function %s() has typehint with deprecated %s %s%s',
$function->getName(),
strtolower($deprecatedClass->getClassTypeDescription()),
$deprecatedClass->getName(),
$this->deprecatedClassHelper->getClassDeprecationDescription($deprecatedClass)
))->identifier(sprintf('return.deprecated%s', $deprecatedClass->getClassTypeDescription()))->build();
}
return $errors;
}
}

View File

@@ -0,0 +1,65 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Expr\Cast;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function sprintf;
/**
* @implements Rule<Cast>
*/
class UsageOfDeprecatedCastRule implements Rule
{
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return Cast::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
$castedType = $scope->getType($node->expr);
if (! $castedType->hasMethod('__toString')->yes()) {
return [];
}
$method = $castedType->getMethod('__toString', $scope);
if (! $method->isDeprecated()->yes()) {
return [];
}
$description = $method->getDeprecatedDescription();
if ($description === null) {
return [
RuleErrorBuilder::message(sprintf(
'Casting class %s to string is deprecated.',
$method->getDeclaringClass()->getName()
))->identifier('class.toStringDeprecated')->build(),
];
}
return [
RuleErrorBuilder::message(sprintf(
"Casting class %s to string is deprecated.:\n%s",
$method->getDeclaringClass()->getName(),
$description
))->identifier('class.toStringDeprecated')->build(),
];
}
}

View File

@@ -0,0 +1,84 @@
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Deprecations;
use PhpParser\Node;
use PhpParser\Node\Stmt\TraitUse;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\ShouldNotHappenException;
use function sprintf;
/**
* @implements Rule<TraitUse>
*/
class UsageOfDeprecatedTraitRule implements Rule
{
/** @var ReflectionProvider */
private $reflectionProvider;
/** @var DeprecatedScopeHelper */
private $deprecatedScopeHelper;
public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper)
{
$this->reflectionProvider = $reflectionProvider;
$this->deprecatedScopeHelper = $deprecatedScopeHelper;
}
public function getNodeType(): string
{
return TraitUse::class;
}
public function processNode(Node $node, Scope $scope): array
{
if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) {
return [];
}
$classReflection = $scope->getClassReflection();
if ($classReflection === null) {
throw new ShouldNotHappenException();
}
$errors = [];
$className = $classReflection->getName();
foreach ($node->traits as $traitNameNode) {
$traitName = (string) $traitNameNode;
try {
$trait = $this->reflectionProvider->getClass($traitName);
if (!$trait->isDeprecated()) {
continue;
}
$description = $trait->getDeprecatedDescription();
if ($description === null) {
$errors[] = RuleErrorBuilder::message(sprintf(
'Usage of deprecated trait %s in class %s.',
$traitName,
$className
))->identifier('traitUse.deprecated')->build();
} else {
$errors[] = RuleErrorBuilder::message(sprintf(
"Usage of deprecated trait %s in class %s:\n%s",
$traitName,
$className,
$description
))->identifier('traitUse.deprecated')->build();
}
} catch (ClassNotFoundException $e) {
continue;
}
}
return $errors;
}
}