├── .editorconfig ├── LICENSE ├── README.md ├── composer.json ├── phpstan-baseline.neon ├── rules.neon └── src ├── DependencyInjection └── LazyDeprecatedScopeResolverProvider.php └── Rules └── Deprecations ├── CallWithDeprecatedIniOptionRule.php ├── DefaultDeprecatedScopeResolver.php ├── DeprecatedScopeHelper.php ├── DeprecatedScopeResolver.php ├── FetchingDeprecatedConstRule.php ├── RestrictedDeprecatedClassConstantUsageExtension.php ├── RestrictedDeprecatedClassNameUsageExtension.php ├── RestrictedDeprecatedFunctionUsageExtension.php ├── RestrictedDeprecatedMethodUsageExtension.php └── RestrictedDeprecatedPropertyUsageExtension.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | 9 | [*.{php,phpt}] 10 | indent_style = tab 11 | indent_size = 4 12 | 13 | [*.xml] 14 | indent_style = tab 15 | indent_size = 4 16 | 17 | [*.neon] 18 | indent_style = tab 19 | indent_size = 4 20 | 21 | [*.{yaml,yml}] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [composer.json] 26 | indent_style = tab 27 | indent_size = 4 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Ondřej Mirtes 4 | Copyright (c) 2025 PHPStan s.r.o. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rules for detecting usage of deprecated classes, methods, properties, constants and traits. 2 | 3 | [![Build](https://github.com/phpstan/phpstan-deprecation-rules/workflows/Build/badge.svg)](https://github.com/phpstan/phpstan-deprecation-rules/actions) 4 | [![Latest Stable Version](https://poser.pugx.org/phpstan/phpstan-deprecation-rules/v/stable)](https://packagist.org/packages/phpstan/phpstan-deprecation-rules) 5 | [![License](https://poser.pugx.org/phpstan/phpstan-deprecation-rules/license)](https://packagist.org/packages/phpstan/phpstan-deprecation-rules) 6 | 7 | * [PHPStan](https://phpstan.org/) 8 | 9 | ## Installation 10 | 11 | To use this extension, require it in [Composer](https://getcomposer.org/): 12 | 13 | ``` 14 | composer require --dev phpstan/phpstan-deprecation-rules 15 | ``` 16 | 17 | If you also install [phpstan/extension-installer](https://github.com/phpstan/extension-installer) then you're all set! 18 | 19 |
20 | Manual installation 21 | 22 | If you don't want to use `phpstan/extension-installer`, include rules.neon in your project's PHPStan config: 23 | 24 | ``` 25 | includes: 26 | - vendor/phpstan/phpstan-deprecation-rules/rules.neon 27 | ``` 28 |
29 | 30 | ## Deprecating code you don't own 31 | 32 | This extension emits deprecation warnings on code, which uses properties/functions/methods/classes which are annotated as `@deprecated`. 33 | 34 | 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: 35 | ``` 36 | /** @deprecated */ 37 | class ThirdPartyClass {} 38 | ``` 39 | 40 | ## Custom deprecation markers 41 | 42 | You can implement extensions to support even e.g. custom `#[MyDeprecated]` attribute. [Learn more](https://phpstan.org/developing-extensions/custom-deprecations). 43 | 44 | 45 | ## Custom deprecated scopes 46 | 47 | Usage of deprecated code is not reported in code that is also deprecated: 48 | 49 | ```php 50 | /** @deprecated */ 51 | function doFoo(): void 52 | { 53 | // not reported: 54 | anotherDeprecatedFunction(); 55 | } 56 | ``` 57 | 58 | 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. 59 | 60 | For example if you mark your PHPUnit tests that test deprecated code with `@group legacy`, you can implement the extension this way: 61 | 62 | ```php 63 | class GroupLegacyScopeResolver implements DeprecatedScopeResolver 64 | { 65 | 66 | public function isScopeDeprecated(Scope $scope): bool 67 | { 68 | $function = $scope->getFunction(); 69 | return $function !== null 70 | && $function->getDocComment() !== null 71 | && strpos($function->getDocComment(), '@group legacy') !== false; 72 | } 73 | 74 | } 75 | ``` 76 | 77 | And register it in your [configuration file](https://phpstan.org/config-reference): 78 | 79 | ```neon 80 | services: 81 | - 82 | class: GroupLegacyScopeResolver 83 | tags: 84 | - phpstan.deprecations.deprecatedScopeResolver 85 | ``` 86 | 87 | [Learn more about Scope](https://phpstan.org/developing-extensions/scope), a core concept for implementing custom PHPStan extensions. 88 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phpstan/phpstan-deprecation-rules", 3 | "type": "phpstan-extension", 4 | "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", 5 | "license": [ 6 | "MIT" 7 | ], 8 | "require": { 9 | "php": "^7.4 || ^8.0", 10 | "phpstan/phpstan": "^2.1.15" 11 | }, 12 | "require-dev": { 13 | "php-parallel-lint/php-parallel-lint": "^1.2", 14 | "phpstan/phpstan-phpunit": "^2.0", 15 | "phpunit/phpunit": "^9.6" 16 | }, 17 | "config": { 18 | "platform": { 19 | "php": "7.4.6" 20 | }, 21 | "sort-packages": true 22 | }, 23 | "extra": { 24 | "phpstan": { 25 | "includes": [ 26 | "rules.neon" 27 | ] 28 | } 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "PHPStan\\": "src/" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "classmap": [ 37 | "tests/" 38 | ] 39 | }, 40 | "minimum-stability": "dev", 41 | "prefer-stable": true 42 | } 43 | -------------------------------------------------------------------------------- /phpstan-baseline.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - 4 | message: '#^Accessing PHPStan\\Rules\\Classes\\ClassConstantRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 5 | identifier: phpstanApi.classConstant 6 | count: 1 7 | path: tests/Rules/ClassConstantRuleTest.php 8 | 9 | - 10 | message: '#^Accessing PHPStan\\Rules\\Classes\\ExistingClassesInClassImplementsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 11 | identifier: phpstanApi.classConstant 12 | count: 1 13 | path: tests/Rules/Deprecations/ImplementationOfDeprecatedInterfaceRuleTest.php 14 | 15 | - 16 | message: '#^Accessing PHPStan\\Rules\\Classes\\ExistingClassInClassExtendsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 17 | identifier: phpstanApi.classConstant 18 | count: 1 19 | path: tests/Rules/Deprecations/InheritanceOfDeprecatedClassRuleTest.php 20 | 21 | - 22 | message: '#^Accessing PHPStan\\Rules\\Classes\\ExistingClassesInInterfaceExtendsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 23 | identifier: phpstanApi.classConstant 24 | count: 1 25 | path: tests/Rules/Deprecations/InheritanceOfDeprecatedInterfaceRuleTest.php 26 | 27 | - 28 | message: '#^Accessing PHPStan\\Rules\\Classes\\InstantiationRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 29 | identifier: phpstanApi.classConstant 30 | count: 1 31 | path: tests/Rules/Deprecations/InstantiationOfDeprecatedClassRuleTest.php 32 | 33 | - 34 | message: '#^Accessing PHPStan\\Rules\\Methods\\CallStaticMethodsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 35 | identifier: phpstanApi.classConstant 36 | count: 1 37 | path: tests/Rules/Deprecations/RestrictedDeprecatedClassNameUsageExtensionTest.php 38 | 39 | - 40 | message: '#^Accessing PHPStan\\Rules\\Methods\\ExistingClassesInTypehintsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 41 | identifier: phpstanApi.classConstant 42 | count: 1 43 | path: tests/Rules/Deprecations/TypeHintDeprecatedInClassMethodSignatureRuleTest.php 44 | 45 | - 46 | message: '#^Accessing PHPStan\\Rules\\Functions\\ExistingClassesInClosureTypehintsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 47 | identifier: phpstanApi.classConstant 48 | count: 1 49 | path: tests/Rules/Deprecations/TypeHintDeprecatedInClosureSignatureRuleTest.php 50 | 51 | - 52 | message: '#^Accessing PHPStan\\Rules\\Functions\\ExistingClassesInTypehintsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 53 | identifier: phpstanApi.classConstant 54 | count: 1 55 | path: tests/Rules/Deprecations/TypeHintDeprecatedInFunctionSignatureRuleTest.php 56 | 57 | - 58 | message: '#^Accessing PHPStan\\Rules\\RestrictedUsage\\RestrictedUsageOfDeprecatedStringCastRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 59 | identifier: phpstanApi.classConstant 60 | count: 1 61 | path: tests/Rules/Deprecations/UsageOfDeprecatedCastRuleTest.php 62 | 63 | - 64 | message: '#^Accessing PHPStan\\Rules\\Classes\\ExistingClassInTraitUseRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' 65 | identifier: phpstanApi.classConstant 66 | count: 1 67 | path: tests/Rules/Deprecations/UsageOfDeprecatedTraitRuleTest.php 68 | -------------------------------------------------------------------------------- /rules.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | deprecationRulesInstalled: true 3 | 4 | services: 5 | - 6 | class: PHPStan\DependencyInjection\LazyDeprecatedScopeResolverProvider 7 | - 8 | class: PHPStan\Rules\Deprecations\DeprecatedScopeHelper 9 | factory: @PHPStan\DependencyInjection\LazyDeprecatedScopeResolverProvider::get 10 | 11 | - 12 | class: PHPStan\Rules\Deprecations\DefaultDeprecatedScopeResolver 13 | tags: 14 | - phpstan.deprecations.deprecatedScopeResolver 15 | 16 | - 17 | class: PHPStan\Rules\Deprecations\CallWithDeprecatedIniOptionRule 18 | 19 | - 20 | class: PHPStan\Rules\Deprecations\RestrictedDeprecatedClassConstantUsageExtension 21 | tags: 22 | - phpstan.restrictedClassConstantUsageExtension 23 | 24 | - 25 | class: PHPStan\Rules\Deprecations\RestrictedDeprecatedFunctionUsageExtension 26 | tags: 27 | - phpstan.restrictedFunctionUsageExtension 28 | 29 | - 30 | class: PHPStan\Rules\Deprecations\RestrictedDeprecatedMethodUsageExtension 31 | tags: 32 | - phpstan.restrictedMethodUsageExtension 33 | 34 | - 35 | class: PHPStan\Rules\Deprecations\RestrictedDeprecatedPropertyUsageExtension 36 | tags: 37 | - phpstan.restrictedPropertyUsageExtension 38 | 39 | - 40 | class: PHPStan\Rules\Deprecations\RestrictedDeprecatedClassNameUsageExtension 41 | arguments: 42 | bleedingEdge: %featureToggles.bleedingEdge% 43 | tags: 44 | - phpstan.restrictedClassNameUsageExtension 45 | 46 | rules: 47 | - PHPStan\Rules\Deprecations\FetchingDeprecatedConstRule 48 | 49 | conditionalTags: 50 | PHPStan\Rules\Deprecations\CallWithDeprecatedIniOptionRule: 51 | phpstan.rules.rule: %featureToggles.bleedingEdge% 52 | -------------------------------------------------------------------------------- /src/DependencyInjection/LazyDeprecatedScopeResolverProvider.php: -------------------------------------------------------------------------------- 1 | container = $container; 19 | } 20 | 21 | public function get(): DeprecatedScopeHelper 22 | { 23 | if ($this->scopeHelper === null) { 24 | $this->scopeHelper = new DeprecatedScopeHelper( 25 | $this->container->getServicesByTag(self::EXTENSION_TAG), 26 | ); 27 | } 28 | return $this->scopeHelper; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/Rules/Deprecations/CallWithDeprecatedIniOptionRule.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class CallWithDeprecatedIniOptionRule implements Rule 24 | { 25 | 26 | private const INI_FUNCTIONS = [ 27 | 'ini_get', 28 | 'ini_set', 29 | 'ini_alter', 30 | 'ini_restore', 31 | 'get_cfg_var', 32 | ]; 33 | 34 | private const DEPRECATED_OPTIONS = [ 35 | // deprecated since unknown version 36 | 'mbstring.http_input' => 0, 37 | 'mbstring.http_output' => 0, 38 | 'mbstring.internal_encoding' => 0, 39 | 'pdo_odbc.db2_instance_name' => 0, 40 | 'enable_dl' => 0, 41 | 42 | 'iconv.input_encoding' => 50600, 43 | 'iconv.output_encoding' => 50600, 44 | 'iconv.internal_encoding' => 50600, 45 | 46 | 'mbstring.func_overload' => 70200, 47 | 'track_errors' => 70200, 48 | 49 | 'allow_url_include' => 70400, 50 | 51 | 'assert.quiet_eval' => 80000, 52 | 53 | 'filter.default' => 80100, 54 | 'oci8.old_oci_close_semantics' => 80100, 55 | 56 | 'assert.active' => 80300, 57 | 'assert.exception' => 80300, 58 | 'assert.bail' => 80300, 59 | 'assert.warning' => 80300, 60 | 61 | 'session.sid_length' => 80400, 62 | 'session.sid_bits_per_character' => 80400, 63 | ]; 64 | 65 | private ReflectionProvider $reflectionProvider; 66 | 67 | private DeprecatedScopeHelper $deprecatedScopeHelper; 68 | 69 | private PhpVersion $phpVersion; 70 | 71 | public function __construct( 72 | ReflectionProvider $reflectionProvider, 73 | DeprecatedScopeHelper $deprecatedScopeHelper, 74 | PhpVersion $phpVersion 75 | ) 76 | { 77 | $this->reflectionProvider = $reflectionProvider; 78 | $this->deprecatedScopeHelper = $deprecatedScopeHelper; 79 | $this->phpVersion = $phpVersion; 80 | } 81 | 82 | public function getNodeType(): string 83 | { 84 | return FuncCall::class; 85 | } 86 | 87 | public function processNode(Node $node, Scope $scope): array 88 | { 89 | if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) { 90 | return []; 91 | } 92 | 93 | if (!($node->name instanceof Name)) { 94 | return []; 95 | } 96 | 97 | if (count($node->getArgs()) < 1) { 98 | return []; 99 | } 100 | 101 | try { 102 | $function = $this->reflectionProvider->getFunction($node->name, $scope); 103 | } catch (FunctionNotFoundException $e) { 104 | // Other rules will notify if the function is not found 105 | return []; 106 | } 107 | 108 | if (!in_array(strtolower($function->getName()), self::INI_FUNCTIONS, true)) { 109 | return []; 110 | } 111 | 112 | $phpVersionId = $this->phpVersion->getVersionId(); 113 | $iniType = $scope->getType($node->getArgs()[0]->value); 114 | foreach ($iniType->getConstantStrings() as $string) { 115 | if (!array_key_exists($string->getValue(), self::DEPRECATED_OPTIONS)) { 116 | continue; 117 | } 118 | 119 | if ($phpVersionId < self::DEPRECATED_OPTIONS[$string->getValue()]) { 120 | continue; 121 | } 122 | 123 | return [ 124 | RuleErrorBuilder::message(sprintf( 125 | "Call to function %s() with deprecated option '%s'.", 126 | $function->getName(), 127 | $string->getValue(), 128 | ))->identifier('function.deprecated')->build(), 129 | ]; 130 | } 131 | 132 | return []; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/Rules/Deprecations/DefaultDeprecatedScopeResolver.php: -------------------------------------------------------------------------------- 1 | getClassReflection(); 13 | if ($class !== null && $class->isDeprecated()) { 14 | return true; 15 | } 16 | 17 | $trait = $scope->getTraitReflection(); 18 | if ($trait !== null && $trait->isDeprecated()) { 19 | return true; 20 | } 21 | 22 | $function = $scope->getFunction(); 23 | if ($function !== null && $function->isDeprecated()->yes()) { 24 | return true; 25 | } 26 | 27 | return false; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/Rules/Deprecations/DeprecatedScopeHelper.php: -------------------------------------------------------------------------------- 1 | resolvers = $checkers; 19 | } 20 | 21 | public function isScopeDeprecated(Scope $scope): bool 22 | { 23 | foreach ($this->resolvers as $checker) { 24 | if ($checker->isScopeDeprecated($scope)) { 25 | return true; 26 | } 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/Rules/Deprecations/DeprecatedScopeResolver.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class FetchingDeprecatedConstRule implements Rule 17 | { 18 | 19 | private ReflectionProvider $reflectionProvider; 20 | 21 | private DeprecatedScopeHelper $deprecatedScopeHelper; 22 | 23 | public function __construct(ReflectionProvider $reflectionProvider, DeprecatedScopeHelper $deprecatedScopeHelper) 24 | { 25 | $this->reflectionProvider = $reflectionProvider; 26 | $this->deprecatedScopeHelper = $deprecatedScopeHelper; 27 | } 28 | 29 | public function getNodeType(): string 30 | { 31 | return ConstFetch::class; 32 | } 33 | 34 | public function processNode(Node $node, Scope $scope): array 35 | { 36 | if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) { 37 | return []; 38 | } 39 | 40 | if (!$this->reflectionProvider->hasConstant($node->name, $scope)) { 41 | return []; 42 | } 43 | 44 | $constantReflection = $this->reflectionProvider->getConstant($node->name, $scope); 45 | 46 | if ($constantReflection->isDeprecated()->yes()) { 47 | return [ 48 | RuleErrorBuilder::message(sprintf( 49 | $constantReflection->getDeprecatedDescription() ?? 'Use of constant %s is deprecated.', 50 | $constantReflection->getName(), 51 | ))->identifier('constant.deprecated')->build(), 52 | ]; 53 | } 54 | 55 | return []; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/Rules/Deprecations/RestrictedDeprecatedClassConstantUsageExtension.php: -------------------------------------------------------------------------------- 1 | deprecatedScopeHelper = $deprecatedScopeHelper; 20 | } 21 | 22 | public function isRestrictedClassConstantUsage( 23 | ClassConstantReflection $constantReflection, 24 | Scope $scope 25 | ): ?RestrictedUsage 26 | { 27 | if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) { 28 | return null; 29 | } 30 | 31 | if ($constantReflection->getDeclaringClass()->isDeprecated()) { 32 | $class = $constantReflection->getDeclaringClass(); 33 | $classDescription = $class->getDeprecatedDescription(); 34 | if ($classDescription === null) { 35 | return RestrictedUsage::create( 36 | sprintf( 37 | 'Fetching class constant %s of deprecated %s %s.', 38 | $constantReflection->getName(), 39 | strtolower($constantReflection->getDeclaringClass()->getClassTypeDescription()), 40 | $constantReflection->getDeclaringClass()->getName(), 41 | ), 42 | sprintf( 43 | 'classConstant.deprecated%s', 44 | $constantReflection->getDeclaringClass()->getClassTypeDescription(), 45 | ), 46 | ); 47 | } 48 | 49 | return RestrictedUsage::create( 50 | sprintf( 51 | "Fetching class constant %s of deprecated %s %s:\n%s", 52 | $constantReflection->getName(), 53 | strtolower($constantReflection->getDeclaringClass()->getClassTypeDescription()), 54 | $constantReflection->getDeclaringClass()->getName(), 55 | $classDescription, 56 | ), 57 | sprintf( 58 | 'classConstant.deprecated%s', 59 | $constantReflection->getDeclaringClass()->getClassTypeDescription(), 60 | ), 61 | ); 62 | } 63 | 64 | if (!$constantReflection->isDeprecated()->yes()) { 65 | return null; 66 | } 67 | 68 | $description = $constantReflection->getDeprecatedDescription(); 69 | if ($description === null) { 70 | return RestrictedUsage::create( 71 | sprintf( 72 | 'Fetching deprecated class constant %s of %s %s.', 73 | $constantReflection->getName(), 74 | strtolower($constantReflection->getDeclaringClass()->getClassTypeDescription()), 75 | $constantReflection->getDeclaringClass()->getName(), 76 | ), 77 | 'classConstant.deprecated', 78 | ); 79 | } 80 | 81 | return RestrictedUsage::create( 82 | sprintf( 83 | "Fetching deprecated class constant %s of %s %s:\n%s", 84 | $constantReflection->getName(), 85 | strtolower($constantReflection->getDeclaringClass()->getClassTypeDescription()), 86 | $constantReflection->getDeclaringClass()->getName(), 87 | $description, 88 | ), 89 | 'classConstant.deprecated', 90 | ); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/Rules/Deprecations/RestrictedDeprecatedClassNameUsageExtension.php: -------------------------------------------------------------------------------- 1 | deprecatedScopeHelper = $deprecatedScopeHelper; 31 | $this->reflectionProvider = $reflectionProvider; 32 | $this->bleedingEdge = $bleedingEdge; 33 | } 34 | 35 | public function isRestrictedClassNameUsage( 36 | ClassReflection $classReflection, 37 | Scope $scope, 38 | ClassNameUsageLocation $location 39 | ): ?RestrictedUsage 40 | { 41 | if (!$classReflection->isDeprecated()) { 42 | return null; 43 | } 44 | 45 | if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) { 46 | return null; 47 | } 48 | 49 | $currentClassName = $location->getCurrentClassName(); 50 | if ($currentClassName !== null && $this->reflectionProvider->hasClass($currentClassName)) { 51 | $currentClassReflection = $this->reflectionProvider->getClass($currentClassName); 52 | if ($currentClassReflection->isDeprecated()) { 53 | return null; 54 | } 55 | } 56 | 57 | $identifierPart = sprintf('deprecated%s', $classReflection->getClassTypeDescription()); 58 | $defaultUsage = RestrictedUsage::create( 59 | $this->addClassDescriptionToMessage($classReflection, $location->createMessage( 60 | sprintf('deprecated %s %s', strtolower($classReflection->getClassTypeDescription()), $classReflection->getDisplayName()), 61 | )), 62 | $location->createIdentifier($identifierPart), 63 | ); 64 | 65 | if ($location->value === ClassNameUsageLocation::CLASS_IMPLEMENTS) { 66 | return $defaultUsage; 67 | } 68 | 69 | if ($location->value === ClassNameUsageLocation::CLASS_EXTENDS) { 70 | return $defaultUsage; 71 | } 72 | 73 | if ($location->value === ClassNameUsageLocation::INTERFACE_EXTENDS) { 74 | return $defaultUsage; 75 | } 76 | 77 | if ($location->value === ClassNameUsageLocation::INSTANTIATION) { 78 | return $defaultUsage; 79 | } 80 | 81 | if ($location->value === ClassNameUsageLocation::TRAIT_USE) { 82 | return $defaultUsage; 83 | } 84 | 85 | if ($location->value === ClassNameUsageLocation::STATIC_METHOD_CALL) { 86 | $method = $location->getMethod(); 87 | if ($method !== null) { 88 | if ($method->isDeprecated()->yes() || $method->getDeclaringClass()->isDeprecated()) { 89 | return null; 90 | } 91 | } 92 | 93 | return $defaultUsage; 94 | } 95 | 96 | if ($location->value === ClassNameUsageLocation::STATIC_PROPERTY_ACCESS) { 97 | $property = $location->getProperty(); 98 | if ($property !== null) { 99 | if ($property->isDeprecated()->yes() || $property->getDeclaringClass()->isDeprecated()) { 100 | return null; 101 | } 102 | } 103 | 104 | return $defaultUsage; 105 | } 106 | 107 | if ($location->value === ClassNameUsageLocation::CLASS_CONSTANT_ACCESS) { 108 | $constant = $location->getClassConstant(); 109 | if ($constant !== null) { 110 | if ($constant->isDeprecated()->yes() || $constant->getDeclaringClass()->isDeprecated()) { 111 | return null; 112 | } 113 | } 114 | 115 | return $defaultUsage; 116 | } 117 | 118 | if ($location->value === ClassNameUsageLocation::PARAMETER_TYPE || $location->value === ClassNameUsageLocation::RETURN_TYPE) { 119 | return $defaultUsage; 120 | } 121 | 122 | if (!$this->bleedingEdge) { 123 | return null; 124 | } 125 | 126 | return $defaultUsage; 127 | } 128 | 129 | private function addClassDescriptionToMessage(ClassReflection $classReflection, string $message): string 130 | { 131 | if ($classReflection->getDeprecatedDescription() === null) { 132 | return $message; 133 | } 134 | 135 | return rtrim($message, '.') . ":\n" . $classReflection->getDeprecatedDescription(); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/Rules/Deprecations/RestrictedDeprecatedFunctionUsageExtension.php: -------------------------------------------------------------------------------- 1 | deprecatedScopeHelper = $deprecatedScopeHelper; 19 | } 20 | 21 | public function isRestrictedFunctionUsage( 22 | FunctionReflection $functionReflection, 23 | Scope $scope 24 | ): ?RestrictedUsage 25 | { 26 | if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) { 27 | return null; 28 | } 29 | 30 | if (!$functionReflection->isDeprecated()->yes()) { 31 | return null; 32 | } 33 | 34 | $description = $functionReflection->getDeprecatedDescription(); 35 | if ($description === null) { 36 | return RestrictedUsage::create( 37 | sprintf( 38 | 'Call to deprecated function %s().', 39 | $functionReflection->getName(), 40 | ), 41 | 'function.deprecated', 42 | ); 43 | } 44 | 45 | return RestrictedUsage::create( 46 | sprintf( 47 | "Call to deprecated function %s():\n%s", 48 | $functionReflection->getName(), 49 | $description, 50 | ), 51 | 'function.deprecated', 52 | ); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/Rules/Deprecations/RestrictedDeprecatedMethodUsageExtension.php: -------------------------------------------------------------------------------- 1 | deprecatedScopeHelper = $deprecatedScopeHelper; 20 | } 21 | 22 | public function isRestrictedMethodUsage( 23 | ExtendedMethodReflection $methodReflection, 24 | Scope $scope 25 | ): ?RestrictedUsage 26 | { 27 | if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) { 28 | return null; 29 | } 30 | 31 | if ($methodReflection->getDeclaringClass()->isDeprecated()) { 32 | $class = $methodReflection->getDeclaringClass(); 33 | $classDescription = $class->getDeprecatedDescription(); 34 | if ($classDescription === null) { 35 | return RestrictedUsage::create( 36 | sprintf( 37 | 'Call to method %s() of deprecated %s %s.', 38 | $methodReflection->getName(), 39 | strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()), 40 | $methodReflection->getDeclaringClass()->getName(), 41 | ), 42 | sprintf( 43 | '%s.deprecated%s', 44 | $methodReflection->isStatic() ? 'staticMethod' : 'method', 45 | $methodReflection->getDeclaringClass()->getClassTypeDescription(), 46 | ), 47 | ); 48 | } 49 | 50 | return RestrictedUsage::create( 51 | sprintf( 52 | "Call to method %s() of deprecated %s %s:\n%s", 53 | $methodReflection->getName(), 54 | strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()), 55 | $methodReflection->getDeclaringClass()->getName(), 56 | $classDescription, 57 | ), 58 | sprintf( 59 | '%s.deprecated%s', 60 | $methodReflection->isStatic() ? 'staticMethod' : 'method', 61 | $methodReflection->getDeclaringClass()->getClassTypeDescription(), 62 | ), 63 | ); 64 | } 65 | 66 | if (!$methodReflection->isDeprecated()->yes()) { 67 | return null; 68 | } 69 | 70 | $description = $methodReflection->getDeprecatedDescription(); 71 | if (strtolower($methodReflection->getName()) === '__tostring') { 72 | if ($description === null) { 73 | return RestrictedUsage::create( 74 | sprintf( 75 | 'Casting class %s to string is deprecated.', 76 | $methodReflection->getDeclaringClass()->getName(), 77 | ), 78 | 'class.toStringDeprecated', 79 | ); 80 | } 81 | 82 | return RestrictedUsage::create( 83 | sprintf( 84 | "Casting class %s to string is deprecated.:\n%s", 85 | $methodReflection->getDeclaringClass()->getName(), 86 | $description, 87 | ), 88 | 'class.toStringDeprecated', 89 | ); 90 | } 91 | 92 | if ($description === null) { 93 | return RestrictedUsage::create( 94 | sprintf( 95 | 'Call to deprecated method %s() of %s %s.', 96 | $methodReflection->getName(), 97 | strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()), 98 | $methodReflection->getDeclaringClass()->getName(), 99 | ), 100 | sprintf('%s.deprecated', $methodReflection->isStatic() ? 'staticMethod' : 'method'), 101 | ); 102 | } 103 | 104 | return RestrictedUsage::create( 105 | sprintf( 106 | "Call to deprecated method %s() of %s %s:\n%s", 107 | $methodReflection->getName(), 108 | strtolower($methodReflection->getDeclaringClass()->getClassTypeDescription()), 109 | $methodReflection->getDeclaringClass()->getName(), 110 | $description, 111 | ), 112 | sprintf('%s.deprecated', $methodReflection->isStatic() ? 'staticMethod' : 'method'), 113 | ); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/Rules/Deprecations/RestrictedDeprecatedPropertyUsageExtension.php: -------------------------------------------------------------------------------- 1 | deprecatedScopeHelper = $deprecatedScopeHelper; 20 | } 21 | 22 | public function isRestrictedPropertyUsage( 23 | ExtendedPropertyReflection $propertyReflection, 24 | Scope $scope 25 | ): ?RestrictedUsage 26 | { 27 | if ($this->deprecatedScopeHelper->isScopeDeprecated($scope)) { 28 | return null; 29 | } 30 | 31 | if ($propertyReflection->getDeclaringClass()->isDeprecated()) { 32 | $class = $propertyReflection->getDeclaringClass(); 33 | $classDescription = $class->getDeprecatedDescription(); 34 | if ($classDescription === null) { 35 | return RestrictedUsage::create( 36 | sprintf( 37 | 'Access to %sproperty $%s of deprecated %s %s.', 38 | $propertyReflection->isStatic() ? 'static ' : '', 39 | $propertyReflection->getName(), 40 | strtolower($propertyReflection->getDeclaringClass()->getClassTypeDescription()), 41 | $propertyReflection->getDeclaringClass()->getName(), 42 | ), 43 | sprintf( 44 | '%s.deprecated%s', 45 | $propertyReflection->isStatic() ? 'staticProperty' : 'property', 46 | $propertyReflection->getDeclaringClass()->getClassTypeDescription(), 47 | ), 48 | ); 49 | } 50 | 51 | return RestrictedUsage::create( 52 | sprintf( 53 | "Access to %sproperty $%s of deprecated %s %s:\n%s", 54 | $propertyReflection->isStatic() ? 'static ' : '', 55 | $propertyReflection->getName(), 56 | strtolower($propertyReflection->getDeclaringClass()->getClassTypeDescription()), 57 | $propertyReflection->getDeclaringClass()->getName(), 58 | $classDescription, 59 | ), 60 | sprintf( 61 | '%s.deprecated%s', 62 | $propertyReflection->isStatic() ? 'staticProperty' : 'property', 63 | $propertyReflection->getDeclaringClass()->getClassTypeDescription(), 64 | ), 65 | ); 66 | } 67 | 68 | if (!$propertyReflection->isDeprecated()->yes()) { 69 | return null; 70 | } 71 | 72 | $description = $propertyReflection->getDeprecatedDescription(); 73 | if ($description === null) { 74 | return RestrictedUsage::create( 75 | sprintf( 76 | 'Access to deprecated %sproperty $%s of %s %s.', 77 | $propertyReflection->isStatic() ? 'static ' : '', 78 | $propertyReflection->getName(), 79 | strtolower($propertyReflection->getDeclaringClass()->getClassTypeDescription()), 80 | $propertyReflection->getDeclaringClass()->getName(), 81 | ), 82 | sprintf('%s.deprecated', $propertyReflection->isStatic() ? 'staticProperty' : 'property'), 83 | ); 84 | } 85 | 86 | return RestrictedUsage::create( 87 | sprintf( 88 | "Access to deprecated %sproperty $%s of %s %s:\n%s", 89 | $propertyReflection->isStatic() ? 'static ' : '', 90 | $propertyReflection->getName(), 91 | strtolower($propertyReflection->getDeclaringClass()->getClassTypeDescription()), 92 | $propertyReflection->getDeclaringClass()->getName(), 93 | $description, 94 | ), 95 | sprintf('%s.deprecated', $propertyReflection->isStatic() ? 'staticProperty' : 'property'), 96 | ); 97 | } 98 | 99 | } 100 | --------------------------------------------------------------------------------