Skip to content

Commit badbbd6

Browse files
committed
DeprecationProviders: allow custom deprecation-marking logic
1 parent 375f68e commit badbbd6

20 files changed

+834
-27
lines changed

Diff for: conf/config.neon

+10
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,16 @@ services:
21572157
-
21582158
class: PHPStan\Reflection\BetterReflection\SourceStubber\ReflectionSourceStubberFactory
21592159

2160+
-
2161+
class: PHPStan\Reflection\Deprecation\DeprecationResolver
2162+
arguments:
2163+
propertyDeprecationProviders: tagged(phpstan.propertyDeprecationProvider)
2164+
methodDeprecationProviders: tagged(phpstan.methodDeprecationProvider)
2165+
classConstantDeprecationProviders: tagged(phpstan.classConstantDeprecationProvider)
2166+
classDeprecationProviders: tagged(phpstan.classDeprecationProvider)
2167+
functionDeprecationProviders: tagged(phpstan.functionDeprecationProvider)
2168+
constantDeprecationProviders: tagged(phpstan.constantDeprecationProvider)
2169+
21602170
php8Lexer:
21612171
class: PhpParser\Lexer\Emulative
21622172
factory: @PHPStan\Parser\LexerFactory::createEmulative()

Diff for: src/Analyser/NodeScopeResolver.php

+3
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
use PHPStan\Reflection\Callables\SimpleImpurePoint;
138138
use PHPStan\Reflection\Callables\SimpleThrowPoint;
139139
use PHPStan\Reflection\ClassReflection;
140+
use PHPStan\Reflection\Deprecation\DeprecationResolver;
140141
use PHPStan\Reflection\ExtendedMethodReflection;
141142
use PHPStan\Reflection\ExtendedParameterReflection;
142143
use PHPStan\Reflection\ExtendedParametersAcceptor;
@@ -255,6 +256,7 @@ public function __construct(
255256
private readonly StubPhpDocProvider $stubPhpDocProvider,
256257
private readonly PhpVersion $phpVersion,
257258
private readonly SignatureMapProvider $signatureMapProvider,
259+
private readonly DeprecationResolver $deprecationResolver,
258260
private readonly AttributeReflectionFactory $attributeReflectionFactory,
259261
private readonly PhpDocInheritanceResolver $phpDocInheritanceResolver,
260262
private readonly FileHelper $fileHelper,
@@ -2140,6 +2142,7 @@ private function createAstClassReflection(Node\Stmt\ClassLike $stmt, string $cla
21402142
$this->phpDocInheritanceResolver,
21412143
$this->phpVersion,
21422144
$this->signatureMapProvider,
2145+
$this->deprecationResolver,
21432146
$this->attributeReflectionFactory,
21442147
$this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(),
21452148
$this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(),

Diff for: src/Reflection/BetterReflection/BetterReflectionProvider.php

+23-12
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use PHPStan\Reflection\ClassReflection;
3737
use PHPStan\Reflection\Constant\RuntimeConstantReflection;
3838
use PHPStan\Reflection\ConstantReflection;
39+
use PHPStan\Reflection\Deprecation\DeprecationResolver;
3940
use PHPStan\Reflection\FunctionReflection;
4041
use PHPStan\Reflection\FunctionReflectionFactory;
4142
use PHPStan\Reflection\InitializerExprContext;
@@ -85,6 +86,7 @@ public function __construct(
8586
private Reflector $reflector,
8687
private FileTypeMapper $fileTypeMapper,
8788
private PhpDocInheritanceResolver $phpDocInheritanceResolver,
89+
private DeprecationResolver $deprecationResolver,
8890
private PhpVersion $phpVersion,
8991
private NativeFunctionReflectionProvider $nativeFunctionReflectionProvider,
9092
private StubPhpDocProvider $stubPhpDocProvider,
@@ -148,6 +150,7 @@ public function getClass(string $className): ClassReflection
148150
$this->phpDocInheritanceResolver,
149151
$this->phpVersion,
150152
$this->signatureMapProvider,
153+
$this->deprecationResolver,
151154
$this->attributeReflectionFactory,
152155
$this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(),
153156
$this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(),
@@ -243,6 +246,7 @@ public function getAnonymousClassReflection(Node\Stmt\Class_ $classNode, Scope $
243246
$this->phpDocInheritanceResolver,
244247
$this->phpVersion,
245248
$this->signatureMapProvider,
249+
$this->deprecationResolver,
246250
$this->attributeReflectionFactory,
247251
$this->classReflectionExtensionRegistryProvider->getRegistry()->getPropertiesClassReflectionExtensions(),
248252
$this->classReflectionExtensionRegistryProvider->getRegistry()->getMethodsClassReflectionExtensions(),
@@ -305,8 +309,11 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection
305309
$phpDocParameterTypes = [];
306310
$phpDocReturnTag = null;
307311
$phpDocThrowsTag = null;
308-
$deprecatedTag = null;
309-
$isDeprecated = false;
312+
313+
$deprecation = $this->deprecationResolver->getFunctionDeprecation($reflectionFunction);
314+
$deprecationDescription = $deprecation?->getDescription();
315+
$isDeprecated = $deprecation !== null;
316+
310317
$isInternal = false;
311318
$isPure = null;
312319
$asserts = Assertions::createEmpty();
@@ -327,8 +334,10 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection
327334
$phpDocParameterTypes = array_map(static fn ($tag) => $tag->getType(), $resolvedPhpDoc->getParamTags());
328335
$phpDocReturnTag = $resolvedPhpDoc->getReturnTag();
329336
$phpDocThrowsTag = $resolvedPhpDoc->getThrowsTag();
330-
$deprecatedTag = $resolvedPhpDoc->getDeprecatedTag();
331-
$isDeprecated = $resolvedPhpDoc->isDeprecated();
337+
if (!$isDeprecated) {
338+
$deprecationDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : $deprecationDescription;
339+
$isDeprecated = $resolvedPhpDoc->isDeprecated();
340+
}
332341
$isInternal = $resolvedPhpDoc->isInternal();
333342
$isPure = $resolvedPhpDoc->isPure();
334343
$asserts = Assertions::createFromResolvedPhpDocBlock($resolvedPhpDoc);
@@ -347,7 +356,7 @@ private function getCustomFunction(string $functionName): PhpFunctionReflection
347356
$phpDocParameterTypes,
348357
$phpDocReturnTag !== null ? $phpDocReturnTag->getType() : null,
349358
$phpDocThrowsTag !== null ? $phpDocThrowsTag->getType() : null,
350-
$deprecatedTag !== null ? $deprecatedTag->getMessage() : null,
359+
$deprecationDescription,
351360
$isDeprecated,
352361
$isInternal,
353362
$reflectionFunction->getFileName() !== false ? $reflectionFunction->getFileName() : null,
@@ -407,13 +416,15 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn
407416
$constantValueType = $this->initializerExprTypeResolver->getType($constantReflection->getValueExpression(), InitializerExprContext::fromGlobalConstant($constantReflection));
408417
$docComment = $constantReflection->getDocComment();
409418

410-
$isDeprecated = TrinaryLogic::createNo();
411-
$deprecatedDescription = null;
412-
if ($docComment !== null) {
419+
$deprecation = $this->deprecationResolver->getConstantDeprecation($constantReflection);
420+
$isDeprecated = $deprecation !== null;
421+
$deprecatedDescription = $deprecation?->getDescription();
422+
423+
if ($isDeprecated === false && $docComment !== null) {
413424
$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc($fileName, null, null, null, $docComment);
414-
$isDeprecated = TrinaryLogic::createFromBoolean($resolvedPhpDoc->isDeprecated());
425+
$isDeprecated = $resolvedPhpDoc->isDeprecated();
415426

416-
if ($resolvedPhpDoc->isDeprecated() && $resolvedPhpDoc->getDeprecatedTag() !== null) {
427+
if ($isDeprecated && $resolvedPhpDoc->getDeprecatedTag() !== null) {
417428
$deprecatedMessage = $resolvedPhpDoc->getDeprecatedTag()->getMessage();
418429

419430
$matches = Strings::match($deprecatedMessage ?? '', '#^(\d+)\.(\d+)(?:\.(\d+))?$#');
@@ -423,7 +434,7 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn
423434
$patch = $matches[3] ?? 0;
424435
$versionId = sprintf('%d%02d%02d', $major, $minor, $patch);
425436

426-
$isDeprecated = TrinaryLogic::createFromBoolean($this->phpVersion->getVersionId() >= $versionId);
437+
$isDeprecated = $this->phpVersion->getVersionId() >= $versionId;
427438
} else {
428439
// filter raw version number messages like in
429440
// https://github.com/JetBrains/phpstorm-stubs/blob/9608c953230b08f07b703ecfe459cc58d5421437/filter/filter.php#L478
@@ -436,7 +447,7 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn
436447
$constantName,
437448
$constantValueType,
438449
$fileName,
439-
$isDeprecated,
450+
TrinaryLogic::createFromBoolean($isDeprecated),
440451
$deprecatedDescription,
441452
);
442453
}

Diff for: src/Reflection/ClassReflection.php

+40-9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use PHPStan\PhpDoc\Tag\TemplateTag;
2727
use PHPStan\PhpDoc\Tag\TypeAliasImportTag;
2828
use PHPStan\PhpDoc\Tag\TypeAliasTag;
29+
use PHPStan\Reflection\Deprecation\DeprecationResolver;
2930
use PHPStan\Reflection\Php\PhpClassReflectionExtension;
3031
use PHPStan\Reflection\Php\PhpPropertyReflection;
3132
use PHPStan\Reflection\Php\UniversalObjectCratesClassReflectionExtension;
@@ -161,6 +162,7 @@ public function __construct(
161162
private PhpDocInheritanceResolver $phpDocInheritanceResolver,
162163
private PhpVersion $phpVersion,
163164
private SignatureMapProvider $signatureMapProvider,
165+
private DeprecationResolver $deprecationResolver,
164166
private AttributeReflectionFactory $attributeReflectionFactory,
165167
private array $propertiesClassReflectionExtensions,
166168
private array $methodsClassReflectionExtensions,
@@ -1079,6 +1081,10 @@ public function getConstant(string $name): ClassConstantReflection
10791081
throw new MissingConstantFromReflectionException($this->getName(), $name);
10801082
}
10811083

1084+
$deprecation = $this->deprecationResolver->getClassConstantDeprecation($reflectionConstant);
1085+
$deprecatedDescription = $deprecation?->getDescription();
1086+
$isDeprecated = $deprecation !== null;
1087+
10821088
$declaringClass = $this->reflectionProvider->getClass($reflectionConstant->getDeclaringClass()->getName());
10831089
$fileName = $declaringClass->getFileName();
10841090
$phpDocType = null;
@@ -1099,8 +1105,10 @@ public function getConstant(string $name): ClassConstantReflection
10991105
);
11001106
}
11011107

1102-
$deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null;
1103-
$isDeprecated = $resolvedPhpDoc->isDeprecated();
1108+
if (!$isDeprecated) {
1109+
$deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null;
1110+
$isDeprecated = $resolvedPhpDoc->isDeprecated();
1111+
}
11041112
$isInternal = $resolvedPhpDoc->isInternal();
11051113
$isFinal = $resolvedPhpDoc->isFinal();
11061114
$varTags = $resolvedPhpDoc->getVarTags();
@@ -1210,11 +1218,8 @@ public function getTypeAliases(): array
12101218

12111219
public function getDeprecatedDescription(): ?string
12121220
{
1213-
if ($this->deprecatedDescription === null && $this->isDeprecated()) {
1214-
$resolvedPhpDoc = $this->getResolvedPhpDoc();
1215-
if ($resolvedPhpDoc !== null && $resolvedPhpDoc->getDeprecatedTag() !== null) {
1216-
$this->deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag()->getMessage();
1217-
}
1221+
if ($this->isDeprecated === null) {
1222+
$this->resolveDeprecation();
12181223
}
12191224

12201225
return $this->deprecatedDescription;
@@ -1223,13 +1228,36 @@ public function getDeprecatedDescription(): ?string
12231228
public function isDeprecated(): bool
12241229
{
12251230
if ($this->isDeprecated === null) {
1226-
$resolvedPhpDoc = $this->getResolvedPhpDoc();
1227-
$this->isDeprecated = $resolvedPhpDoc !== null && $resolvedPhpDoc->isDeprecated();
1231+
$this->resolveDeprecation();
12281232
}
12291233

12301234
return $this->isDeprecated;
12311235
}
12321236

1237+
/**
1238+
* @phpstan-assert bool $this->isDeprecated
1239+
*/
1240+
private function resolveDeprecation(): void
1241+
{
1242+
$deprecation = $this->deprecationResolver->isClassDeprecated($this->reflection);
1243+
if ($deprecation !== null) {
1244+
$this->isDeprecated = true;
1245+
$this->deprecatedDescription = $deprecation->getDescription();
1246+
return;
1247+
}
1248+
1249+
$resolvedPhpDoc = $this->getResolvedPhpDoc();
1250+
1251+
if ($resolvedPhpDoc !== null && $resolvedPhpDoc->isDeprecated()) {
1252+
$this->isDeprecated = true;
1253+
$this->deprecatedDescription = $resolvedPhpDoc->getDeprecatedTag() !== null ? $resolvedPhpDoc->getDeprecatedTag()->getMessage() : null;
1254+
return;
1255+
}
1256+
1257+
$this->isDeprecated = false;
1258+
$this->deprecatedDescription = null;
1259+
}
1260+
12331261
public function isBuiltin(): bool
12341262
{
12351263
return $this->reflection->isInternal();
@@ -1559,6 +1587,7 @@ public function withTypes(array $types): self
15591587
$this->phpDocInheritanceResolver,
15601588
$this->phpVersion,
15611589
$this->signatureMapProvider,
1590+
$this->deprecationResolver,
15621591
$this->attributeReflectionFactory,
15631592
$this->propertiesClassReflectionExtensions,
15641593
$this->methodsClassReflectionExtensions,
@@ -1590,6 +1619,7 @@ public function withVariances(array $variances): self
15901619
$this->phpDocInheritanceResolver,
15911620
$this->phpVersion,
15921621
$this->signatureMapProvider,
1622+
$this->deprecationResolver,
15931623
$this->attributeReflectionFactory,
15941624
$this->propertiesClassReflectionExtensions,
15951625
$this->methodsClassReflectionExtensions,
@@ -1631,6 +1661,7 @@ public function asFinal(): self
16311661
$this->phpDocInheritanceResolver,
16321662
$this->phpVersion,
16331663
$this->signatureMapProvider,
1664+
$this->deprecationResolver,
16341665
$this->attributeReflectionFactory,
16351666
$this->propertiesClassReflectionExtensions,
16361667
$this->methodsClassReflectionExtensions,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection\Deprecation;
4+
5+
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClassConstant;
6+
7+
/**
8+
* This interface allows you to provide custom deprecation information
9+
*
10+
* To register it in the configuration file use the following tag:
11+
*
12+
* ```
13+
* services:
14+
* -
15+
* class: App\PHPStan\MyProvider
16+
* tags:
17+
* - phpstan.classConstantDeprecationProvider
18+
* ```
19+
*
20+
* @api
21+
*/
22+
interface ClassConstantDeprecationProvider
23+
{
24+
25+
public function getClassConstantDeprecation(ReflectionClassConstant $reflection): ?Deprecation;
26+
27+
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection\Deprecation;
4+
5+
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass;
6+
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnum;
7+
8+
/**
9+
* This interface allows you to provide custom deprecation information
10+
*
11+
* To register it in the configuration file use the following tag:
12+
*
13+
* ```
14+
* services:
15+
* -
16+
* class: App\PHPStan\MyProvider
17+
* tags:
18+
* - phpstan.classDeprecationProvider
19+
* ```
20+
*
21+
* @api
22+
*/
23+
interface ClassDeprecationProvider
24+
{
25+
26+
public function getClassDeprecation(ReflectionClass|ReflectionEnum $reflection): ?Deprecation;
27+
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection\Deprecation;
4+
5+
use PHPStan\BetterReflection\Reflection\ReflectionConstant;
6+
7+
/**
8+
* This interface allows you to provide custom deprecation information
9+
*
10+
* To register it in the configuration file use the following tag:
11+
*
12+
* ```
13+
* services:
14+
* -
15+
* class: App\PHPStan\MyProvider
16+
* tags:
17+
* - phpstan.constantDeprecationProvider
18+
* ```
19+
*
20+
* @api
21+
*/
22+
interface ConstantDeprecationProvider
23+
{
24+
25+
public function getConstantDeprecation(ReflectionConstant $reflection): ?Deprecation;
26+
27+
}

Diff for: src/Reflection/Deprecation/Deprecation.php

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection\Deprecation;
4+
5+
/**
6+
* @api
7+
*/
8+
final class Deprecation
9+
{
10+
11+
private ?string $description = null;
12+
13+
private function __construct()
14+
{
15+
}
16+
17+
public static function create(): self
18+
{
19+
return new self();
20+
}
21+
22+
public function getDescription(): ?string
23+
{
24+
return $this->description;
25+
}
26+
27+
public function withDescription(?string $description): self
28+
{
29+
$clone = clone $this;
30+
$clone->description = $description;
31+
32+
return $clone;
33+
}
34+
35+
}

0 commit comments

Comments
 (0)