Skip to content

Commit 01a3b7a

Browse files
adaamzondrejmirtes
authored andcommitted
Dynamic return type Nette\Forms\Container::getValues($asArray)
1 parent b442bdf commit 01a3b7a

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This extension provides following features:
1111

1212
* `Nette\ComponentModel\Container::getComponent()` knows type of the component because it reads the return type on `createComponent*` (this works best in presenters and controls)
1313
* `Nette\DI\Container::getByType` and `createInstance` return type based on first parameter (`Foo::class`).
14+
* `Nette\Forms\Container::getValues` return type based on `$asArray` parameter.
1415
* Dynamic methods of [Nette\Utils\Html](https://doc.nette.org/en/2.4/html-elements)
1516
* Magic [Nette\Object and Nette\SmartObject](https://doc.nette.org/en/2.4/php-language-enhancements) properties
1617
* Event listeners through the `on*` properties

extension.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@ services:
4747
class: PHPStan\Type\Nette\ServiceLocatorDynamicReturnTypeExtension
4848
tags:
4949
- phpstan.broker.dynamicMethodReturnTypeExtension
50+
51+
-
52+
class: PHPStan\Type\Nette\FormContainerValuesDynamicReturnTypeExtension
53+
tags:
54+
- phpstan.broker.dynamicMethodReturnTypeExtension
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Nette;
4+
5+
use PhpParser\Node\Expr\MethodCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\MethodReflection;
8+
use PHPStan\Type\ArrayType;
9+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
10+
use PHPStan\Type\FalseBooleanType;
11+
use PHPStan\Type\MixedType;
12+
use PHPStan\Type\ObjectType;
13+
use PHPStan\Type\TrueBooleanType;
14+
use PHPStan\Type\Type;
15+
16+
final class FormContainerValuesDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
17+
{
18+
19+
public static function getClass(): string
20+
{
21+
return \Nette\Forms\Container::class;
22+
}
23+
24+
public function isMethodSupported(MethodReflection $methodReflection): bool
25+
{
26+
return $methodReflection->getName() === 'getValues';
27+
}
28+
29+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
30+
{
31+
if (count($methodCall->args) === 0) {
32+
return new ObjectType(\Nette\Utils\ArrayHash::class);
33+
}
34+
35+
$arg = $methodCall->args[0]->value;
36+
$scopedType = $scope->getType($arg);
37+
38+
if ($scopedType instanceof FalseBooleanType) {
39+
return new ObjectType(\Nette\Utils\ArrayHash::class);
40+
}
41+
42+
if ($scopedType instanceof TrueBooleanType) {
43+
return new ArrayType(new MixedType());
44+
}
45+
46+
return $methodReflection->getReturnType();
47+
}
48+
49+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Nette;
4+
5+
use PhpParser\Node\Arg;
6+
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Reflection\MethodReflection;
10+
use PHPStan\Type\ArrayType;
11+
use PHPStan\Type\CommonUnionType;
12+
use PHPStan\Type\FalseBooleanType;
13+
use PHPStan\Type\IterableIterableType;
14+
use PHPStan\Type\MixedType;
15+
use PHPStan\Type\ObjectType;
16+
use PHPStan\Type\TrueBooleanType;
17+
18+
final class FormContainerValuesDynamicReturnTypeExtensionTest extends \PHPUnit_Framework_TestCase
19+
{
20+
21+
/** @var \PHPStan\Type\Nette\FormContainerValuesDynamicReturnTypeExtension */
22+
private $extension;
23+
24+
protected function setUp()
25+
{
26+
$this->extension = new FormContainerValuesDynamicReturnTypeExtension();
27+
}
28+
29+
public function testParameterAsArray()
30+
{
31+
$methodReflection = $this->createMock(MethodReflection::class);
32+
$methodReflection
33+
->method('getReturnType')
34+
->willReturn(new CommonUnionType([new ArrayType(new MixedType()), new IterableIterableType(new ObjectType(\Nette\Utils\ArrayHash::class))]));
35+
36+
$scope = $this->createMock(Scope::class);
37+
$scope->method('getType')->willReturn(new TrueBooleanType());
38+
39+
/** @var \PhpParser\Node\Expr\MethodCall $methodCall */
40+
$methodCall = $this->createMock(MethodCall::class);
41+
/** @var \PhpParser\Node\Arg $arg */
42+
$arg = $this->createMock(Arg::class);
43+
/** @var \PhpParser\Node\Expr $value */
44+
$value = $this->createMock(Expr::class);
45+
$arg->value = $value;
46+
$methodCall->args = [
47+
0 => $arg,
48+
];
49+
50+
$resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope);
51+
52+
$this->assertInstanceOf(ArrayType::class, $resultType);
53+
}
54+
55+
public function testParameterAsArrayHash()
56+
{
57+
$methodReflection = $this->createMock(MethodReflection::class);
58+
$methodReflection
59+
->method('getReturnType')
60+
->willReturn(new CommonUnionType([new ArrayType(new MixedType()), new IterableIterableType(new ObjectType(\Nette\Utils\ArrayHash::class))]));
61+
62+
$scope = $this->createMock(Scope::class);
63+
$scope->method('getType')->willReturn(new FalseBooleanType());
64+
65+
/** @var \PhpParser\Node\Expr\MethodCall $methodCall */
66+
$methodCall = $this->createMock(MethodCall::class);
67+
/** @var \PhpParser\Node\Arg $arg */
68+
$arg = $this->createMock(Arg::class);
69+
/** @var \PhpParser\Node\Expr $value */
70+
$value = $this->createMock(Expr::class);
71+
$arg->value = $value;
72+
$methodCall->args = [
73+
0 => $arg,
74+
];
75+
76+
$resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope);
77+
78+
$this->assertInstanceOf(ObjectType::class, $resultType);
79+
$this->assertSame(\Nette\Utils\ArrayHash::class, $resultType->getClass());
80+
}
81+
82+
public function testDefaultParameterIsArrayHash()
83+
{
84+
$methodReflection = $this->createMock(MethodReflection::class);
85+
$methodReflection
86+
->method('getReturnType')
87+
->willReturn(new CommonUnionType([new ArrayType(new MixedType()), new IterableIterableType(new ObjectType(\Nette\Utils\ArrayHash::class))]));
88+
89+
$scope = $this->createMock(Scope::class);
90+
$scope->method('getType')->willReturn(new FalseBooleanType());
91+
92+
/** @var \PhpParser\Node\Expr\MethodCall $methodCall */
93+
$methodCall = $this->createMock(MethodCall::class);
94+
$methodCall->args = [];
95+
96+
$resultType = $this->extension->getTypeFromMethodCall($methodReflection, $methodCall, $scope);
97+
98+
$this->assertInstanceOf(ObjectType::class, $resultType);
99+
$this->assertSame(\Nette\Utils\ArrayHash::class, $resultType->getClass());
100+
}
101+
102+
}

0 commit comments

Comments
 (0)