Skip to content

Commit 4cce4a3

Browse files
authored
Merge pull request #35 from shochdoerfer/feature/strict_dataobject
Make DataObject extension stricter
2 parents 2a648df + eaec4a3 commit 4cce4a3

File tree

3 files changed

+235
-30
lines changed

3 files changed

+235
-30
lines changed

src/bitExpert/PHPStan/Magento/Reflection/AbstractMagicMethodReflectionExtension.php

Lines changed: 128 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@
1717
use PHPStan\Reflection\MethodReflection;
1818
use PHPStan\Reflection\MethodsClassReflectionExtension;
1919
use PHPStan\Reflection\Php\DummyParameter;
20-
use PHPStan\Reflection\TrivialParametersAcceptor;
20+
use PHPStan\Type\ArrayType;
21+
use PHPStan\Type\BooleanType;
2122
use PHPStan\Type\Generic\TemplateTypeMap;
23+
use PHPStan\Type\IntegerType;
2224
use PHPStan\Type\MixedType;
25+
use PHPStan\Type\NullType;
26+
use PHPStan\Type\ObjectType;
27+
use PHPStan\Type\StringType;
28+
use PHPStan\Type\UnionType;
2329

2430
abstract class AbstractMagicMethodReflectionExtension implements MethodsClassReflectionExtension
2531
{
@@ -30,47 +36,143 @@ abstract class AbstractMagicMethodReflectionExtension implements MethodsClassRef
3036
*/
3137
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
3238
{
33-
if (strpos($methodName, 'get') === 0) {
34-
return $this->returnGetMagicMethodReflection($classReflection, $methodName);
39+
$methodPrefix = substr($methodName, 0, 3);
40+
switch ($methodPrefix) {
41+
case 'get':
42+
return $this->returnGetMagicMethod($classReflection, $methodName);
43+
case 'set':
44+
return $this->returnSetMagicMethod($classReflection, $methodName);
45+
case 'uns':
46+
return $this->returnUnsetMagicMethod($classReflection, $methodName);
47+
case 'has':
48+
return $this->returnHasMagicMethod($classReflection, $methodName);
49+
default:
50+
break;
3551
}
36-
37-
return $this->returnMagicMethodReflection($classReflection, $methodName);
3852
}
3953

4054
/**
41-
* Helper method to create magic method for get calls. The method call does not accept any parameter and will return
42-
* mixed type.
55+
* Helper method to create magic method reflection for get() calls.
4356
*
4457
* @param ClassReflection $classReflection
4558
* @param string $methodName
4659
* @return MethodReflection
4760
*/
48-
protected function returnGetMagicMethodReflection(
49-
ClassReflection $classReflection,
50-
string $methodName
51-
): MethodReflection {
52-
$variants = new TrivialParametersAcceptor();
61+
private function returnGetMagicMethod(ClassReflection $classReflection, string $methodName): MethodReflection
62+
{
63+
$params = [
64+
new DummyParameter(
65+
'key',
66+
new StringType(),
67+
true,
68+
null,
69+
false,
70+
null
71+
),
72+
new DummyParameter(
73+
'index',
74+
new UnionType([new StringType(), new IntegerType(), new NullType()]),
75+
true,
76+
null,
77+
false,
78+
null
79+
)
80+
];
81+
82+
$returnType = new MixedType();
83+
84+
$variants = new FunctionVariant(
85+
TemplateTypeMap::createEmpty(),
86+
null,
87+
$params,
88+
false,
89+
$returnType
90+
);
91+
5392
return new MagicMethodReflection($methodName, $classReflection, [$variants]);
5493
}
5594

56-
/**
57-
* Helper method to create magic reflection method for set, unset and has calls. Those method calls accept one
58-
* mixed parameter and return mixed type.
59-
*
60-
* @param ClassReflection $classReflection
61-
* @param string $methodName
62-
* @return MethodReflection
63-
*/
64-
protected function returnMagicMethodReflection(
65-
ClassReflection $classReflection,
66-
string $methodName
67-
): MethodReflection {
95+
private function returnSetMagicMethod(ClassReflection $classReflection, string $methodName): MethodReflection
96+
{
97+
$params = [
98+
new DummyParameter(
99+
'key',
100+
new UnionType([new StringType(), new ArrayType(new MixedType(), new MixedType())]),
101+
true,
102+
null,
103+
false,
104+
null
105+
),
106+
new DummyParameter(
107+
'value',
108+
new MixedType(),
109+
true,
110+
null,
111+
false,
112+
null
113+
)
114+
];
115+
116+
$returnType = new ObjectType($classReflection->getName());
117+
118+
$variants = new FunctionVariant(
119+
TemplateTypeMap::createEmpty(),
120+
null,
121+
$params,
122+
false,
123+
$returnType
124+
);
125+
126+
return new MagicMethodReflection($methodName, $classReflection, [$variants]);
127+
}
128+
129+
private function returnUnsetMagicMethod(ClassReflection $classReflection, string $methodName): MethodReflection
130+
{
131+
$params = [
132+
new DummyParameter(
133+
'key',
134+
new UnionType([new NullType(), new StringType(), new ArrayType(new MixedType(), new MixedType())]),
135+
true,
136+
null,
137+
false,
138+
null
139+
)
140+
];
141+
142+
$returnType = new ObjectType($classReflection->getName());
143+
144+
$variants = new FunctionVariant(
145+
TemplateTypeMap::createEmpty(),
146+
null,
147+
$params,
148+
false,
149+
$returnType
150+
);
151+
152+
return new MagicMethodReflection($methodName, $classReflection, [$variants]);
153+
}
154+
155+
private function returnHasMagicMethod(ClassReflection $classReflection, string $methodName): MethodReflection
156+
{
157+
$params = [
158+
new DummyParameter(
159+
'key',
160+
new StringType(),
161+
true,
162+
null,
163+
false,
164+
null
165+
)
166+
];
167+
168+
$returnType = new BooleanType();
169+
68170
$variants = new FunctionVariant(
69171
TemplateTypeMap::createEmpty(),
70172
null,
71-
[ new DummyParameter('name', new MixedType(), false, null, false, null),],
173+
$params,
72174
false,
73-
new MixedType()
175+
$returnType
74176
);
75177

76178
return new MagicMethodReflection($methodName, $classReflection, [$variants]);

src/bitExpert/PHPStan/Magento/Reflection/Framework/DataObjectMagicMethodReflectionExtension.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ class DataObjectMagicMethodReflectionExtension extends AbstractMagicMethodReflec
2626
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
2727
{
2828
$parentClasses = $classReflection->getParentClassesNames();
29+
$parentClasses[] = $classReflection->getName();
2930

30-
$isDataObject = $classReflection->getName() === DataObject::class ||
31-
in_array(DataObject::class, $parentClasses, true);
32-
33-
return $isDataObject &&
31+
return in_array(DataObject::class, $parentClasses, true) &&
3432
in_array(substr($methodName, 0, 3), ['get', 'set', 'uns', 'has']);
3533
}
3634
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the phpstan-magento package.
5+
*
6+
* (c) bitExpert AG
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
declare(strict_types=1);
12+
13+
namespace bitExpert\PHPStan\Magento\Reflection\Framework;
14+
15+
use PHPStan\Reflection\ClassReflection;
16+
use PHPStan\Type\BooleanType;
17+
use PHPStan\Type\MixedType;
18+
use PHPStan\Type\ObjectType;
19+
use PHPStan\Type\StringType;
20+
use PHPStan\Type\UnionType;
21+
use PHPUnit\Framework\TestCase;
22+
23+
class DataObjectMagicMethodReflectionExtensionUnitTest extends TestCase
24+
{
25+
/**
26+
* @var DataObjectMagicMethodReflectionExtension
27+
*/
28+
private $extension;
29+
/**
30+
* @var ClassReflection|\PHPUnit\Framework\MockObject\MockObject
31+
*/
32+
private $classReflection;
33+
34+
protected function setUp(): void
35+
{
36+
$this->extension = new DataObjectMagicMethodReflectionExtension();
37+
$this->classReflection = $this->createMock(ClassReflection::class);
38+
}
39+
40+
/**
41+
* @test
42+
*/
43+
public function returnMagicMethodReflectionForGetMethod(): void
44+
{
45+
$methodReflection = $this->extension->getMethod($this->classReflection, 'getTest');
46+
47+
$variants = $methodReflection->getVariants();
48+
$params = $variants[0]->getParameters();
49+
50+
$this->assertCount(1, $variants);
51+
$this->assertInstanceOf(MixedType::class, $variants[0]->getReturnType());
52+
$this->assertCount(2, $params);
53+
$this->assertInstanceOf(StringType::class, $params[0]->getType());
54+
$this->assertInstanceOf(UnionType::class, $params[1]->getType());
55+
}
56+
57+
/**
58+
* @test
59+
*/
60+
public function returnMagicMethodReflectionForSetMethod(): void
61+
{
62+
$methodReflection = $this->extension->getMethod($this->classReflection, 'setTest');
63+
64+
$variants = $methodReflection->getVariants();
65+
$params = $variants[0]->getParameters();
66+
67+
$this->assertCount(1, $variants);
68+
$this->assertInstanceOf(ObjectType::class, $variants[0]->getReturnType());
69+
$this->assertCount(2, $params);
70+
$this->assertInstanceOf(UnionType::class, $params[0]->getType());
71+
$this->assertInstanceOf(MixedType::class, $params[1]->getType());
72+
}
73+
74+
/**
75+
* @test
76+
*/
77+
public function returnMagicMethodReflectionForUnsetMethod(): void
78+
{
79+
$methodReflection = $this->extension->getMethod($this->classReflection, 'unsetTest');
80+
81+
$variants = $methodReflection->getVariants();
82+
$params = $variants[0]->getParameters();
83+
84+
$this->assertCount(1, $variants);
85+
$this->assertInstanceOf(ObjectType::class, $variants[0]->getReturnType());
86+
$this->assertCount(1, $params);
87+
$this->assertInstanceOf(UnionType::class, $params[0]->getType());
88+
}
89+
90+
/**
91+
* @test
92+
*/
93+
public function returnMagicMethodReflectionForHasMethod(): void
94+
{
95+
$methodReflection = $this->extension->getMethod($this->classReflection, 'hasTest');
96+
97+
$variants = $methodReflection->getVariants();
98+
$params = $variants[0]->getParameters();
99+
100+
$this->assertCount(1, $variants);
101+
$this->assertInstanceOf(BooleanType::class, $variants[0]->getReturnType());
102+
$this->assertCount(1, $params);
103+
$this->assertInstanceOf(StringType::class, $params[0]->getType());
104+
}
105+
}

0 commit comments

Comments
 (0)