Skip to content

Commit d352a08

Browse files
fix: Improve Yii2 integration component property handling. (#24)
1 parent 1700d59 commit d352a08

10 files changed

+63
-58
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Bug #22: Make `$configPath` optional in constructor `ServiceMap` class and update docs `README.md` (@terabytesoftw)
66
- Bug #23: Update the path to `Yii.php` in the `stubFiles` configuration for correct referencing (@terabytesoftw)
7+
- Bug #24: Improve `Yii2` integration component property handling.
78

89
## 0.2.1 June 03, 2025
910

README.md

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -126,21 +126,7 @@ includes:
126126
- vendor/yii2-extensions/phpstan/extension.neon
127127
128128
parameters:
129-
level: 8
130-
131-
paths:
132-
- src
133-
- controllers
134-
- models
135-
- widgets
136-
137-
excludePaths:
138-
- src/legacy
139-
- tests/_support
140-
- vendor
141-
142129
bootstrapFiles:
143-
- config/bootstrap.php
144130
- tests/bootstrap.php
145131
146132
# Complete dynamic constants list (extension defaults + custom)
@@ -151,7 +137,20 @@ parameters:
151137
- YII_ENV_PROD
152138
- YII_ENV_TEST
153139
- APP_VERSION
154-
- MAINTENANCE_MODE
140+
- MAINTENANCE_MODE
141+
142+
level: 8
143+
144+
paths:
145+
- src
146+
- controllers
147+
- models
148+
- widgets
149+
150+
excludePaths:
151+
- src/legacy
152+
- tests/_support
153+
- vendor
155154
156155
yii2:
157156
config_path: %currentWorkingDirectory%/config/web.php

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
},
3232
"extra": {
3333
"branch-alias": {
34-
"dev-main": "0.1-dev"
34+
"dev-main": "0.2.x-dev"
3535
}
3636
},
3737
"config": {

extension.neon

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ parameters:
99
yii2:
1010
config_path: ''
1111

12-
stubFiles:
13-
- stubs/BaseYii.stub
14-
- %currentWorkingDirectory%/vendor/yiisoft/yii2/Yii.php
15-
1612
parametersSchema:
1713
yii2: structure(
1814
[

phpstan.neon

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ includes:
55
- phar://phpstan.phar/conf/bleedingEdge.neon
66

77
parameters:
8+
bootstrapFiles:
9+
- tests/bootstrap.php
10+
811
ignoreErrors:
912
- '#Calling PHPStan\\Reflection\\Annotations\\AnnotationsPropertiesClassReflectionExtension\:\:(has|get)Property\(\) is not covered.+#'
1013
- '#Creating new PHPStan\\Reflection\\Dummy\\DummyPropertyReflection is not covered.+#'
@@ -13,9 +16,3 @@ parameters:
1316

1417
paths:
1518
- src
16-
17-
scanFiles:
18-
- vendor/yiisoft/yii2/Yii.php
19-
20-
yii2:
21-
config_path: tests/fixture/yii-config-valid.php

src/reflection/ApplicationPropertiesClassReflectionExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public function getProperty(ClassReflection $classReflection, string $propertyNa
9494
return new ComponentPropertyReflection(
9595
new DummyPropertyReflection($propertyName),
9696
new ObjectType($componentClass),
97+
$classReflection,
9798
);
9899
}
99100

src/reflection/ComponentPropertyReflection.php

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use PHPStan\TrinaryLogic;
99
use PHPStan\Type\Type;
1010

11+
use function sprintf;
12+
1113
/**
1214
* Property reflection wrapper for Yii application components in PHPStan analysis.
1315
*
@@ -38,9 +40,14 @@ final class ComponentPropertyReflection implements PropertyReflection
3840
* Creates a new instance of the {@see ComponentPropertyReflection} class.
3941
*
4042
* @param PropertyReflection $fallbackProperty Fallback property reflection instance for delegation.
41-
* @param Type $type Actual type of the dynamic component as resolved by the service map.
43+
* @param Type $type Type of the dynamic component as resolved by the service map or dependency injection.
44+
* @param ClassReflection $declaringClass Class reflection of the class declaring the dynamic property.
4245
*/
43-
public function __construct(private readonly PropertyReflection $fallbackProperty, private readonly Type $type) {}
46+
public function __construct(
47+
private readonly PropertyReflection $fallbackProperty,
48+
private readonly Type $type,
49+
private readonly ClassReflection $declaringClass,
50+
) {}
4451

4552
/**
4653
* Determines whether the type of the dynamic Yii application component property can change after assignment.
@@ -71,7 +78,7 @@ public function canChangeTypeAfterAssignment(): bool
7178
*/
7279
public function getDeclaringClass(): ClassReflection
7380
{
74-
return $this->fallbackProperty->getDeclaringClass();
81+
return $this->declaringClass;
7582
}
7683

7784
/**
@@ -99,11 +106,13 @@ public function getDeprecatedDescription(): string|null
99106
* This method allows static analysis tools and IDEs to display inline documentation for dynamic application
100107
* components, supporting accurate code completion, type checking, and developer guidance.
101108
*
102-
* @return string|null PHPDoc comment string if available, or `null` if no documentation is set.
109+
* @return string PHPDoc comment string for the property, or an empty string if no comment is set.
103110
*/
104-
public function getDocComment(): string|null
111+
public function getDocComment(): string
105112
{
106-
return $this->fallbackProperty->getDocComment();
113+
$componentTypeName = $this->type->describe(\PHPStan\Type\VerbosityLevel::typeOnly());
114+
115+
return sprintf("/**\n * @var %s\n */", $componentTypeName);
107116
}
108117

109118
/**
@@ -119,7 +128,7 @@ public function getDocComment(): string|null
119128
*/
120129
public function getReadableType(): Type
121130
{
122-
return $this->fallbackProperty->getReadableType();
131+
return $this->type;
123132
}
124133

125134
/**
@@ -151,7 +160,7 @@ public function getType(): Type
151160
*/
152161
public function getWritableType(): Type
153162
{
154-
return $this->fallbackProperty->getWritableType();
163+
return $this->type;
155164
}
156165

157166
/**
@@ -167,7 +176,7 @@ public function getWritableType(): Type
167176
*/
168177
public function isDeprecated(): TrinaryLogic
169178
{
170-
return $this->fallbackProperty->isDeprecated();
179+
return TrinaryLogic::createNo();
171180
}
172181

173182
/**
@@ -183,7 +192,7 @@ public function isDeprecated(): TrinaryLogic
183192
*/
184193
public function isInternal(): TrinaryLogic
185194
{
186-
return $this->fallbackProperty->isInternal();
195+
return TrinaryLogic::createNo();
187196
}
188197

189198
/**

src/reflection/UserPropertiesClassReflectionExtension.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
};
1313
use PHPStan\Reflection\Annotations\AnnotationsPropertiesClassReflectionExtension;
1414
use PHPStan\Reflection\Dummy\DummyPropertyReflection;
15-
use PHPStan\Type\MixedType;
15+
use PHPStan\Type\ObjectType;
1616
use yii\web\User;
17+
use yii2\extensions\phpstan\ServiceMap;
1718

1819
/**
1920
* Provides property reflection for a Yii user component in PHPStan analysis.
@@ -49,7 +50,10 @@ final class UserPropertiesClassReflectionExtension implements PropertiesClassRef
4950
* @param AnnotationsPropertiesClassReflectionExtension $annotationsProperties Extension for handling
5051
* annotation-based properties.
5152
*/
52-
public function __construct(private readonly AnnotationsPropertiesClassReflectionExtension $annotationsProperties) {}
53+
public function __construct(
54+
private readonly AnnotationsPropertiesClassReflectionExtension $annotationsProperties,
55+
private readonly ServiceMap $serviceMap,
56+
) {}
5357

5458
/**
5559
* Retrieves the property reflection for a given property on the Yii user component.
@@ -69,8 +73,15 @@ public function __construct(private readonly AnnotationsPropertiesClassReflectio
6973
*/
7074
public function getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection
7175
{
72-
if ($propertyName === 'identity') {
73-
return new ComponentPropertyReflection(new DummyPropertyReflection($propertyName), new MixedType());
76+
if (
77+
$propertyName === 'identity' &&
78+
($componentClass = $this->serviceMap->getComponentClassById($propertyName)) !== null
79+
) {
80+
return new ComponentPropertyReflection(
81+
new DummyPropertyReflection($propertyName),
82+
new ObjectType($componentClass),
83+
$classReflection,
84+
);
7485
}
7586

7687
if ($classReflection->hasNativeProperty($propertyName)) {
@@ -96,10 +107,16 @@ public function getProperty(ClassReflection $classReflection, string $propertyNa
96107
*/
97108
public function hasProperty(ClassReflection $classReflection, string $propertyName): bool
98109
{
99-
if ($classReflection->getName() !== User::class) {
110+
if (
111+
$classReflection->getName() !== User::class &&
112+
$classReflection->isSubclassOf(User::class) === false) {
100113
return false;
101114
}
102115

116+
if ($propertyName === 'identity' && $this->serviceMap->getComponentClassById($propertyName) !== null) {
117+
return true;
118+
}
119+
103120
return $classReflection->hasNativeProperty($propertyName)
104121
|| $this->annotationsProperties->hasProperty($classReflection, $propertyName);
105122
}

stubs/BaseYii.stub

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/bootstrap.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
define('YII_ENV', 'test');
1010

1111
// require composer autoloader if available
12-
require(__DIR__ . '/../vendor/autoload.php');
13-
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
12+
require(dirname(__DIR__) . '/vendor/autoload.php');
13+
require(dirname(__DIR__) . '/vendor/yiisoft/yii2/Yii.php');

0 commit comments

Comments
 (0)