Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ee435ce

Browse files
committed
Add deprecations errors for functions typehints
1 parent 826a846 commit ee435ce

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Deprecations;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Broker\Broker;
8+
use PHPStan\Node\InFunctionNode;
9+
use PHPStan\Reflection\ClassReflection;
10+
use PHPStan\Reflection\FunctionReflection;
11+
use PHPStan\Reflection\ParameterReflection;
12+
use PHPStan\Reflection\ParametersAcceptorSelector;
13+
14+
class TypeHintDeprecatedInFunctionSignatureRule implements \PHPStan\Rules\Rule
15+
{
16+
17+
/** @var Broker */
18+
private $broker;
19+
20+
public function __construct(Broker $broker)
21+
{
22+
$this->broker = $broker;
23+
}
24+
25+
public function getNodeType(): string
26+
{
27+
return InFunctionNode::class;
28+
}
29+
30+
/**
31+
* @param InFunctionNode $node
32+
* @param \PHPStan\Analyser\Scope $scope
33+
* @return string[] errors
34+
*/
35+
public function processNode(Node $node, Scope $scope): array
36+
{
37+
if (DeprecatedScopeHelper::isScopeDeprecated($scope)) {
38+
return [];
39+
}
40+
41+
/** @var FunctionReflection $function */
42+
$function = $scope->getFunction();
43+
if (!$function instanceof FunctionReflection) {
44+
throw new \PHPStan\ShouldNotHappenException();
45+
}
46+
$functionSignature = ParametersAcceptorSelector::selectSingle($function->getVariants());
47+
48+
$errors = [];
49+
foreach ($functionSignature->getParameters() as $i => $parameter) {
50+
/** @var ParameterReflection $parameter */
51+
$deprecatedClasses = $this->filterDeprecatedClasses($parameter->getType()->getReferencedClasses());
52+
foreach ($deprecatedClasses as $deprecatedClass) {
53+
$errors[] = sprintf(
54+
'Parameter $%s of function %s() has typehint with deprecated %s %s%s',
55+
$parameter->getName(),
56+
$function->getName(),
57+
$this->getClassType($deprecatedClass),
58+
$deprecatedClass->getName(),
59+
$this->getClassDeprecationDescription($deprecatedClass)
60+
);
61+
}
62+
}
63+
64+
$deprecatedClasses = $this->filterDeprecatedClasses($functionSignature->getReturnType()->getReferencedClasses());
65+
foreach ($deprecatedClasses as $deprecatedClass) {
66+
$errors[] = sprintf(
67+
'Return type of function %s() has typehint with deprecated %s %s%s',
68+
$function->getName(),
69+
$this->getClassType($deprecatedClass),
70+
$deprecatedClass->getName(),
71+
$this->getClassDeprecationDescription($deprecatedClass)
72+
);
73+
}
74+
75+
return $errors;
76+
}
77+
78+
private function getClassType(ClassReflection $class): string
79+
{
80+
if ($class->isInterface()) {
81+
return 'interface';
82+
}
83+
84+
return 'class';
85+
}
86+
87+
private function getClassDeprecationDescription(ClassReflection $class): string
88+
{
89+
$description = null;
90+
if (method_exists($class, 'getDeprecatedDescription')) {
91+
$description = $class->getDeprecatedDescription();
92+
}
93+
94+
if ($description === null) {
95+
return '.';
96+
}
97+
98+
return sprintf(":\n%s", $description);
99+
}
100+
101+
/**
102+
* @param string[] $referencedClasses
103+
* @return ClassReflection[]
104+
*/
105+
private function filterDeprecatedClasses(array $referencedClasses): array
106+
{
107+
$deprecatedClasses = [];
108+
foreach ($referencedClasses as $referencedClass) {
109+
try {
110+
$class = $this->broker->getClass($referencedClass);
111+
} catch (\PHPStan\Broker\ClassNotFoundException $e) {
112+
continue;
113+
}
114+
115+
if (!$class->isDeprecated()) {
116+
continue;
117+
}
118+
119+
$deprecatedClasses[] = $class;
120+
}
121+
122+
return $deprecatedClasses;
123+
}
124+
125+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Deprecations;
4+
5+
class TypeHintDeprecatedInFunctionSignatureRuleTest extends \PHPStan\Testing\RuleTestCase
6+
{
7+
8+
protected function getRule(): \PHPStan\Rules\Rule
9+
{
10+
$broker = $this->createBroker();
11+
12+
return new TypeHintDeprecatedInFunctionSignatureRule($broker);
13+
}
14+
15+
public function test(): void
16+
{
17+
require_once __DIR__ . '/data/typehint-function-deprecated-class-definition.php';
18+
$this->analyse(
19+
[__DIR__ . '/data/typehint-function-deprecated-class.php'],
20+
[
21+
['Parameter $property of function TypeHintDeprecatedInFunctionSignature\setProperties() has typehint with deprecated class TypeHintDeprecatedInFunctionSignature\DeprecatedProperty.', 8],
22+
['Parameter $property2 of function TypeHintDeprecatedInFunctionSignature\setProperties() has typehint with deprecated interface TypeHintDeprecatedInFunctionSignature\DeprecatedInterface.', 8],
23+
["Parameter \$property4 of function TypeHintDeprecatedInFunctionSignature\setProperties() has typehint with deprecated class TypeHintDeprecatedInFunctionSignature\VerboseDeprecatedProperty:\nI'll be back", 8],
24+
['Parameter $property6 of function TypeHintDeprecatedInFunctionSignature\setProperties() has typehint with deprecated class TypeHintDeprecatedInFunctionSignature\DeprecatedProperty.', 8],
25+
['Return type of function TypeHintDeprecatedInFunctionSignature\setProperties() has typehint with deprecated class TypeHintDeprecatedInFunctionSignature\DeprecatedProperty.', 8],
26+
]
27+
);
28+
}
29+
30+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace TypeHintDeprecatedInFunctionSignature;
4+
5+
/**
6+
* @deprecated
7+
*/
8+
class DeprecatedProperty
9+
{
10+
11+
}
12+
13+
/**
14+
* @deprecated I'll be back
15+
*/
16+
class VerboseDeprecatedProperty
17+
{
18+
19+
}
20+
21+
/**
22+
* @deprecated
23+
*/
24+
interface DeprecatedInterface
25+
{
26+
27+
}
28+
29+
class Property
30+
{
31+
32+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace TypeHintDeprecatedInFunctionSignature;
4+
5+
/**
6+
* @param DeprecatedProperty $property6
7+
*/
8+
function setProperties(
9+
DeprecatedProperty $property,
10+
?DeprecatedInterface $property2,
11+
$property3,
12+
VerboseDeprecatedProperty $property4,
13+
string $property5,
14+
$property6
15+
): DeprecatedProperty {
16+
return $property;
17+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /