Skip to content

Commit 7c230e0

Browse files
authored
Merge pull request #25 from nextras/phpstan1.0
prepare for PHPStan 1.0
2 parents ddcf279 + a895744 commit 7c230e0

9 files changed

+132
-49
lines changed

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
],
88
"require": {
99
"php": "~7.1 || ~8.0",
10-
"phpstan/phpstan": "^0.12.29"
10+
"phpstan/phpstan": "^1.0"
1111
},
1212
"require-dev": {
13+
"phpstan/extension-installer": "^1.1",
14+
"phpstan/phpstan-deprecation-rules": "^1.0",
1315
"nextras/orm": "~4.0 || ~5.0@dev",
1416
"nette/tester": "^2.3.1"
1517
},
@@ -18,6 +20,7 @@
1820
"Nextras\\OrmPhpStan\\": "src/"
1921
}
2022
},
23+
"minimum-stability": "dev",
2124
"prefer-stable": true,
2225
"autoload-dev": {
2326
"classmap": [

extension.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ services:
2828
tags:
2929
- phpstan.rules.rule
3030

31-
- Nextras\OrmPhpStan\Types\Helpers\RepositoryEntityTypeHelper
31+
- Nextras\OrmPhpStan\Types\Helpers\RepositoryEntityTypeHelper(@currentPhpVersionSimpleDirectParser)

src/Reflection/Annotations/AnnotationPropertyReflection.php

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,29 @@
22

33
namespace Nextras\OrmPhpStan\Reflection\Annotations;
44

5-
use PHPStan\Reflection\Annotations\AnnotationPropertyReflection as PHPStanAnnotationPropertyReflection;
65
use PHPStan\Reflection\ClassReflection;
76
use PHPStan\Reflection\PropertyReflection;
7+
use PHPStan\TrinaryLogic;
88
use PHPStan\Type\Type;
99

1010

11-
class AnnotationPropertyReflection extends PHPStanAnnotationPropertyReflection implements PropertyReflection
11+
class AnnotationPropertyReflection implements PropertyReflection
1212
{
1313
/** @var Type */
1414
private $writableType;
1515

16+
/** @var ClassReflection */
17+
private $declaringClass;
18+
19+
/** @var Type */
20+
private $readableType;
21+
22+
/** @var bool */
23+
private $readable;
24+
25+
/** @var bool */
26+
private $writable;
27+
1628

1729
public function __construct(
1830
ClassReflection $declaringClass,
@@ -22,8 +34,11 @@ public function __construct(
2234
bool $writable = true
2335
)
2436
{
25-
parent::__construct($declaringClass, $readableType, $readable, $writable);
2637
$this->writableType = $writableType;
38+
$this->declaringClass = $declaringClass;
39+
$this->readableType = $readableType;
40+
$this->readable = $readable;
41+
$this->writable = $writable;
2742
}
2843

2944

@@ -37,4 +52,70 @@ public function canChangeTypeAfterAssignment(): bool
3752
{
3853
return false;
3954
}
55+
56+
57+
public function getDeclaringClass(): \PHPStan\Reflection\ClassReflection
58+
{
59+
return $this->declaringClass;
60+
}
61+
62+
63+
public function isStatic(): bool
64+
{
65+
return false;
66+
}
67+
68+
69+
public function isPrivate(): bool
70+
{
71+
return false;
72+
}
73+
74+
75+
public function isPublic(): bool
76+
{
77+
return true;
78+
}
79+
80+
81+
public function getDocComment(): ?string
82+
{
83+
return null;
84+
}
85+
86+
87+
public function getReadableType(): \PHPStan\Type\Type
88+
{
89+
return $this->readableType;
90+
}
91+
92+
93+
public function isReadable(): bool
94+
{
95+
return $this->readable;
96+
}
97+
98+
99+
public function isWritable(): bool
100+
{
101+
return $this->writable;
102+
}
103+
104+
105+
public function isDeprecated(): \PHPStan\TrinaryLogic
106+
{
107+
return TrinaryLogic::createNo();
108+
}
109+
110+
111+
public function getDeprecatedDescription(): ?string
112+
{
113+
return null;
114+
}
115+
116+
117+
public function isInternal(): \PHPStan\TrinaryLogic
118+
{
119+
return TrinaryLogic::createNo();
120+
}
40121
}

src/Reflection/EntityDateTimePropertyReflectionExtension.php

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,21 @@
44

55
use Nextras\Orm\Entity\IEntity;
66
use Nextras\OrmPhpStan\Reflection\Annotations\AnnotationPropertyReflection;
7-
use PHPStan\Reflection\Annotations\AnnotationsPropertiesClassReflectionExtension;
87
use PHPStan\Reflection\ClassReflection;
98
use PHPStan\Reflection\PropertiesClassReflectionExtension;
109
use PHPStan\Reflection\PropertyReflection;
10+
use PHPStan\ShouldNotHappenException;
1111
use PHPStan\Type\ObjectType;
1212
use PHPStan\Type\StringType;
1313
use PHPStan\Type\TypeCombinator;
1414

1515

1616
class EntityDateTimePropertyReflectionExtension implements PropertiesClassReflectionExtension
1717
{
18-
/** @var AnnotationsPropertiesClassReflectionExtension */
19-
private $annotationsExtension;
20-
21-
22-
public function __construct(AnnotationsPropertiesClassReflectionExtension $annotationsExtension)
23-
{
24-
$this->annotationsExtension = $annotationsExtension;
25-
}
26-
27-
2818
public function hasProperty(ClassReflection $classReflection, string $propertyName): bool
2919
{
30-
$hasProperty = $this->annotationsExtension->hasProperty($classReflection, $propertyName);
31-
if (!$hasProperty) {
20+
$property = $classReflection->getPropertyTags()[$propertyName] ?? null;
21+
if ($property === null) {
3222
return false;
3323
}
3424

@@ -39,8 +29,7 @@ public function hasProperty(ClassReflection $classReflection, string $propertyNa
3929
return false;
4030
}
4131

42-
$property = $this->annotationsExtension->getProperty($classReflection, $propertyName);
43-
$propertyType = TypeCombinator::removeNull($property->getReadableType()); // remove null to be properly match subtype
32+
$propertyType = TypeCombinator::removeNull($property->getType()); // remove null to properly match subtype
4433
$dateTimeType = new ObjectType(\DateTimeImmutable::class);
4534
$hasDateTime = $dateTimeType->isSuperTypeOf($propertyType)->yes();
4635

@@ -50,11 +39,15 @@ public function hasProperty(ClassReflection $classReflection, string $propertyNa
5039

5140
public function getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection
5241
{
53-
$property = $this->annotationsExtension->getProperty($classReflection, $propertyName);
42+
$property = $classReflection->getPropertyTags()[$propertyName] ?? null;
43+
if ($property === null) {
44+
throw new ShouldNotHappenException();
45+
}
46+
5447
return new AnnotationPropertyReflection(
55-
$property->getDeclaringClass(),
56-
$property->getReadableType(),
57-
TypeCombinator::union($property->getWritableType(), new StringType()),
48+
$classReflection,
49+
$property->getType(),
50+
TypeCombinator::union($property->getType(), new StringType()),
5851
$property->isReadable(),
5952
$property->isWritable()
6053
);

src/Reflection/EntityRelationshipPropertyReflectionExtension.php

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,19 @@
44

55
use Nextras\Orm\Entity\IEntity;
66
use Nextras\OrmPhpStan\Reflection\Annotations\AnnotationPropertyReflection;
7-
use PHPStan\Reflection\Annotations\AnnotationsPropertiesClassReflectionExtension;
87
use PHPStan\Reflection\ClassReflection;
98
use PHPStan\Reflection\PropertiesClassReflectionExtension;
109
use PHPStan\Reflection\PropertyReflection;
10+
use PHPStan\ShouldNotHappenException;
1111
use PHPStan\Type\IntegerType;
1212
use PHPStan\Type\TypeCombinator;
1313

1414

1515
class EntityRelationshipPropertyReflectionExtension implements PropertiesClassReflectionExtension
1616
{
17-
/** @var AnnotationsPropertiesClassReflectionExtension */
18-
private $annotationsExtension;
19-
20-
21-
public function __construct(AnnotationsPropertiesClassReflectionExtension $annotationsExtension)
22-
{
23-
$this->annotationsExtension = $annotationsExtension;
24-
}
25-
26-
2717
public function hasProperty(ClassReflection $classReflection, string $propertyName): bool
2818
{
29-
$hasProperty = $this->annotationsExtension->hasProperty($classReflection, $propertyName);
19+
$hasProperty = array_key_exists($propertyName, $classReflection->getPropertyTags());
3020
if (!$hasProperty) {
3121
return false;
3222
}
@@ -50,11 +40,15 @@ public function hasProperty(ClassReflection $classReflection, string $propertyNa
5040

5141
public function getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection
5242
{
53-
$property = $this->annotationsExtension->getProperty($classReflection, $propertyName);
43+
$property = $classReflection->getPropertyTags()[$propertyName] ?? null;
44+
if ($property === null) {
45+
throw new ShouldNotHappenException();
46+
}
47+
5448
return new AnnotationPropertyReflection(
55-
$property->getDeclaringClass(),
56-
$property->getReadableType(),
57-
TypeCombinator::union($property->getWritableType(), new IntegerType()),
49+
$classReflection,
50+
$property->getType(),
51+
TypeCombinator::union($property->getType(), new IntegerType()),
5852
$property->isReadable(),
5953
$property->isWritable()
6054
);

src/Rules/SetValueMethodRule.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Analyser\Scope;
99
use PHPStan\Broker\Broker;
1010
use PHPStan\Reflection\ClassReflection;
11+
use PHPStan\Reflection\ReflectionProvider;
1112
use PHPStan\Rules\Rule;
1213
use PHPStan\Type\TypeWithClassName;
1314
use PHPStan\Type\VerbosityLevel;
@@ -17,13 +18,13 @@
1718
*/
1819
class SetValueMethodRule implements Rule
1920
{
20-
/** @var Broker */
21-
private $broker;
21+
/** @var ReflectionProvider */
22+
private $reflectionProvider;
2223

2324

24-
public function __construct(Broker $broker)
25+
public function __construct(ReflectionProvider $reflectionProvider)
2526
{
26-
$this->broker = $broker;
27+
$this->reflectionProvider = $reflectionProvider;
2728
}
2829

2930

@@ -48,36 +49,46 @@ public function processNode(Node $node, Scope $scope): array
4849
if (!in_array($methodName, ['setValue', 'setReadOnlyValue'], true)) {
4950
return [];
5051
}
52+
5153
$args = $node->args;
5254
if (!isset($args[0], $args[1])) {
5355
return [];
5456
}
57+
if (!$args[0] instanceof Node\Arg || !$args[1] instanceof Node\Arg) {
58+
return [];
59+
}
60+
5561
$valueType = $scope->getType($args[1]->value);
5662
$varType = $scope->getType($node->var);
5763
if (!$varType instanceof TypeWithClassName) {
5864
return [];
5965
}
66+
6067
$firstValue = $args[0]->value;
6168
if (!$firstValue instanceof Node\Scalar\String_) {
6269
return [];
6370
}
71+
6472
$fieldName = $firstValue->value;
65-
$class = $this->broker->getClass($varType->getClassName());
73+
$class = $this->reflectionProvider->getClass($varType->getClassName());
6674
$interfaces = array_map(function (ClassReflection $interface) {
6775
return $interface->getName();
6876
}, $class->getInterfaces());
6977
if (!in_array(IEntity::class, $interfaces, true)) {
7078
return [];
7179
}
80+
7281
if (!$class->hasProperty($fieldName)) {
7382
return [sprintf(
7483
'Entity %s has no $%s property.',
7584
$varType->getClassName(),
7685
$fieldName
7786
)];
7887
}
88+
7989
$property = $class->getProperty($fieldName, $scope);
8090
$propertyType = $property->getWritableType();
91+
8192
if (!$propertyType->accepts($valueType, true)->yes()) {
8293
return [sprintf(
8394
'Entity %s: property $%s (%s) does not accept %s.',
@@ -87,13 +98,15 @@ public function processNode(Node $node, Scope $scope): array
8798
$valueType->describe(VerbosityLevel::typeOnly())
8899
)];
89100
}
101+
90102
if (!$property->isWritable() && $methodName !== 'setReadOnlyValue') {
91103
return [sprintf(
92104
'Entity %s: property $%s is read-only.',
93105
$varType->getClassName(),
94106
$fieldName
95107
)];
96108
}
109+
97110
return [];
98111
}
99112
}

src/Types/Helpers/RepositoryEntityTypeHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ private function parseEntityClassNameTypes(ClassReflection $repositoryReflection
4848
$className = $repositoryReflection->getName();
4949
$fileName = $repositoryReflection->getFileName();
5050

51-
assert($fileName !== false, sprintf('File for clsas "%s" does not exists.', $className));
51+
assert($fileName !== null, sprintf('File for class "%s" does not exists.', $className));
5252

5353
$ast = $this->parser->parseFile($fileName);
5454

src/Types/MapperMethodReturnTypeExtension.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,14 @@ public function getTypeFromMethodCall(
7171
}
7272

7373
$currentMapper = $this->reflectionProvider->getClass($mapper->getClassName());
74-
assert($currentMapper !== false);
7574

7675
do {
7776
$mapperClass = $currentMapper->getName();
7877
/** @phpstan-var class-string<\Nextras\Orm\Repository\Repository> $repositoryClass */
7978
$repositoryClass = \str_replace('Mapper', 'Repository', $mapperClass);
8079

8180
$currentMapper = $this->reflectionProvider->getClass($mapperClass)->getParentClass();
82-
if ($currentMapper === false) {
81+
if ($currentMapper === null) {
8382
break;
8483
}
8584
$mapperClass = $currentMapper->getName();

tests/config.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
parameters:
2-
autoload_directories:
2+
scanDirectories:
33
- %rootDir%/../../../tests/testbox
44

55
services:

0 commit comments

Comments
 (0)