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 8279b9c

Browse files
Add dynamic return type extension for get_object_vars
1 parent 606bcbf commit 8279b9c

File tree

7 files changed

+182
-0
lines changed

7 files changed

+182
-0
lines changed

‎build/enum-adapter-errors.neon‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ parameters:
105105
count: 8
106106
path: ../src/Reflection/ClassReflection.php
107107

108+
-
109+
message: "#^Call to method getProperties\\(\\) on an unknown class PHPStan\\\\BetterReflection\\\\Reflection\\\\Adapter\\\\ReflectionEnum\\.$#"
110+
count: 1
111+
path: ../src/Reflection/ClassReflection.php
112+
108113
-
109114
message: "#^Call to method getReflectionConstant\\(\\) on an unknown class PHPStan\\\\BetterReflection\\\\Reflection\\\\Adapter\\\\ReflectionEnum\\.$#"
110115
count: 2

‎conf/config.neon‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,11 @@ services:
14291429
tags:
14301430
- phpstan.broker.dynamicFunctionReturnTypeExtension
14311431

1432+
-
1433+
class: PHPStan\Type\Php\GetObjectVarsFunctionReturnTypeExtension
1434+
tags:
1435+
- phpstan.broker.dynamicFunctionReturnTypeExtension
1436+
14321437
-
14331438
class: PHPStan\Type\Php\GetParentClassDynamicFunctionReturnTypeExtension
14341439
tags:

‎src/Reflection/ClassReflection.php‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
use PHPStan\Type\TypehintHelper;
4242
use PHPStan\Type\VerbosityLevel;
4343
use ReflectionException;
44+
use ReflectionProperty;
4445
use stdClass;
4546
use function array_diff;
4647
use function array_filter;
@@ -528,6 +529,17 @@ public function evictPrivateSymbols(): void
528529
$this->getPhpExtension()->evictPrivateSymbols($this->getCacheKey());
529530
}
530531

532+
/**
533+
* @return array<string>
534+
*/
535+
public function getPropertyNames(): array
536+
{
537+
return array_map(
538+
static fn (ReflectionProperty $property) => $property->getName(),
539+
$this->getNativeReflection()->getProperties(),
540+
);
541+
}
542+
531543
public function getProperty(string $propertyName, ClassMemberAccessAnswerer $scope): PropertyReflection
532544
{
533545
if ($this->isEnum()) {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\FunctionReflection;
8+
use PHPStan\Type\Constant\ConstantArrayType;
9+
use PHPStan\Type\Constant\ConstantStringType;
10+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
11+
use PHPStan\Type\Type;
12+
use PHPStan\Type\TypeCombinator;
13+
use function count;
14+
15+
class GetObjectVarsFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
16+
{
17+
18+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
19+
{
20+
return $functionReflection->getName() === 'get_object_vars';
21+
}
22+
23+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type
24+
{
25+
$args = $functionCall->getArgs();
26+
27+
if (count($args) === 0) {
28+
return null;
29+
}
30+
31+
$argType = $scope->getType($args[0]->value);
32+
33+
if (!$argType->isObject()->yes()) {
34+
return null;
35+
}
36+
37+
$objectTypes = $argType->getObjectClassReflections();
38+
39+
if (count($objectTypes) === 0) {
40+
return null;
41+
}
42+
43+
$types = [];
44+
foreach ($objectTypes as $objectType) {
45+
$keys = [];
46+
$values = [];
47+
foreach ($objectType->getPropertyNames() as $propertyName) {
48+
$property = $objectType->getProperty(
49+
$propertyName,
50+
$scope,
51+
);
52+
53+
if (!$scope->canAccessProperty($property)) {
54+
continue;
55+
}
56+
57+
$keys[] = new ConstantStringType($propertyName);
58+
$values[] = $property->getReadableType();
59+
}
60+
61+
$types[] = new ConstantArrayType(
62+
$keys,
63+
$values,
64+
);
65+
}
66+
67+
return TypeCombinator::union(
68+
...$types,
69+
);
70+
}
71+
72+
}

‎tests/PHPStan/Analyser/NodeScopeResolverTest.php‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,12 @@ public function dataFileAsserts(): iterable
12141214
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8956.php');
12151215
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8917.php');
12161216
yield from $this->gatherAssertTypes(__DIR__ . '/data/ds-copy.php');
1217+
1218+
if (PHP_VERSION_ID > 80100) {
1219+
yield from $this->gatherAssertTypes(__DIR__ . '/data/get-object-vars-81.php');
1220+
}
1221+
1222+
yield from $this->gatherAssertTypes(__DIR__ . '/data/get-object-vars.php');
12171223
}
12181224

12191225
/**
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace GetObjectVars81;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
enum Suit
8+
{
9+
case Hearts;
10+
case Diamonds;
11+
case Clubs;
12+
case Spades;
13+
}
14+
15+
enum SuitBacked: string
16+
{
17+
case Hearts = 'H';
18+
case Diamonds = 'D';
19+
case Clubs = 'C';
20+
case Spades = 'S';
21+
}
22+
23+
enum NumBacked: int
24+
{
25+
case One = 1;
26+
case Two = 2;
27+
case Three = 3;
28+
}
29+
30+
function check(Suit $suit, SuitBacked $backed, NumBacked $num): void
31+
{
32+
assertType("array{name: 'Clubs'|'Diamonds'|'Hearts'|'Spades'}", get_object_vars($suit));
33+
assertType("array{name: 'Clubs'|'Diamonds'|'Hearts'|'Spades', value: 'C'|'D'|'H'|'S'}", get_object_vars($backed));
34+
assertType("array{name: 'One'|'Three'|'Two', value: 1|2|3}", get_object_vars($num));
35+
}
36+
37+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace GetObjectVars;
4+
5+
use function PHPStan\Testing\assertType;
6+
class Base
7+
{
8+
9+
public int $a;
10+
public string $b;
11+
protected string $c;
12+
private bool $d;
13+
14+
public function getVars(): void
15+
{
16+
assertType('array{a: int, b: string, c: string, d: bool}', get_object_vars($this));
17+
}
18+
19+
}
20+
21+
class Other {
22+
public int $one;
23+
private bool $two;
24+
public bool $three;
25+
}
26+
27+
class Extended extends Base
28+
{
29+
30+
public function getExtendedVars(): void
31+
{
32+
assertType('array{a: int, b: string, c: string}', get_object_vars($this));
33+
}
34+
35+
}
36+
37+
function check(Base $base, Base|Other $class, string $input): void
38+
{
39+
assertType('array{a: int, b: string}', get_object_vars($base));
40+
assertType('array{a: int, b: string}|array{one: int, three: bool}', get_object_vars($class));
41+
assertType('array<string, mixed>', get_object_vars($input));
42+
assertType('array<string, mixed>', get_object_vars());
43+
}
44+
45+

0 commit comments

Comments
(0)

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