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 4e4353e

Browse files
Kocalondrejmirtes
authored andcommitted
Understand types returned by Container::getParameter() and similar methods
1 parent 2dad4de commit 4e4353e

21 files changed

+1020
-9
lines changed

‎README.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ This extension provides following features:
1010

1111
* Provides correct return type for `ContainerInterface::get()` and `::has()` methods.
1212
* Provides correct return type for `Controller::get()` and `::has()` methods.
13+
* Provides correct return type for `AbstractController::get()` and `::has()` methods.
14+
* Provides correct return type for `ContainerInterface::getParameter()` and `::hasParameter()` methods.
15+
* Provides correct return type for `ParameterBagInterface::get()` and `::has()` methods.
16+
* Provides correct return type for `Controller::getParameter()` method.
17+
* Provides correct return type for `AbstractController::getParameter()` method.
1318
* Provides correct return type for `Request::getContent()` method based on the `$asResource` parameter.
1419
* Provides correct return type for `HeaderBag::get()` method based on the `$first` parameter.
1520
* Provides correct return type for `Envelope::all()` method based on the `$stampFqcn` parameter.
@@ -57,7 +62,7 @@ You have to provide a path to `srcDevDebugProjectContainer.xml` or similar XML f
5762
parameters:
5863
symfony:
5964
container_xml_path: var/cache/dev/srcDevDebugProjectContainer.xml
60-
#or with Symfony 4.2+
65+
#or with Symfony 4.2+
6166
container_xml_path: var/cache/dev/srcApp_KernelDevDebugContainer.xml
6267
# or with Symfony 5+
6368
container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml

‎composer.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
"phpstan/phpstan-phpunit": "^0.12.16",
2727
"phpstan/phpstan-strict-rules": "^0.12.5",
2828
"phpunit/phpunit": "^7.5.20",
29-
"symfony/console": "^4.0 || ^5.0",
3029
"symfony/config": "^4.2 || ^5.0",
31-
"symfony/framework-bundle": "^4.0 || ^5.0",
30+
"symfony/console": "^4.0 || ^5.0",
31+
"symfony/framework-bundle": "^4.4 || ^5.0",
3232
"symfony/http-foundation": "^4.0 || ^5.0",
3333
"symfony/messenger": "^4.2 || ^5.0",
3434
"symfony/serializer": "^4.0 || ^5.0"

‎extension.neon‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ services:
6363
-
6464
factory: @symfony.serviceMapFactory::create()
6565

66+
# parameter map
67+
symfony.parameterMapFactory:
68+
class: PHPStan\Symfony\ParameterMapFactory
69+
factory: PHPStan\Symfony\XmlParameterMapFactory(%symfony.container_xml_path%)
70+
-
71+
factory: @symfony.parameterMapFactory::create()
72+
6673
# ControllerTrait::get()/has() return type
6774
-
6875
factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constant_hassers%)
@@ -215,3 +222,21 @@ services:
215222
-
216223
class: PHPStan\Type\Symfony\KernelInterfaceDynamicReturnTypeExtension
217224
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
225+
226+
# ParameterBagInterface::get()/has() return type
227+
-
228+
factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has', %symfony.constant_hassers%)
229+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
230+
231+
# ContainerInterface::getParameter()/hasParameter() return type
232+
-
233+
factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter', %symfony.constant_hassers%)
234+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
235+
236+
# (Abstract)Controller::getParameter() return type
237+
-
238+
factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null, %symfony.constant_hassers%)
239+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
240+
-
241+
factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constant_hassers%)
242+
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]

‎src/Symfony/DefaultParameterMap.php‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
use PhpParser\Node\Expr;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Type\TypeUtils;
8+
use function count;
9+
10+
final class DefaultParameterMap implements ParameterMap
11+
{
12+
13+
/** @var \PHPStan\Symfony\ParameterDefinition[] */
14+
private $parameters;
15+
16+
/**
17+
* @param \PHPStan\Symfony\ParameterDefinition[] $parameters
18+
*/
19+
public function __construct(array $parameters)
20+
{
21+
$this->parameters = $parameters;
22+
}
23+
24+
/**
25+
* @return \PHPStan\Symfony\ParameterDefinition[]
26+
*/
27+
public function getParameters(): array
28+
{
29+
return $this->parameters;
30+
}
31+
32+
public function getParameter(string $key): ?ParameterDefinition
33+
{
34+
return $this->parameters[$key] ?? null;
35+
}
36+
37+
public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
38+
{
39+
$strings = TypeUtils::getConstantStrings($scope->getType($node));
40+
return count($strings) === 1 ? $strings[0]->getValue() : null;
41+
}
42+
43+
}

‎src/Symfony/FakeParameterMap.php‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
use PhpParser\Node\Expr;
6+
use PHPStan\Analyser\Scope;
7+
8+
final class FakeParameterMap implements ParameterMap
9+
{
10+
11+
/**
12+
* @return \PHPStan\Symfony\ParameterDefinition[]
13+
*/
14+
public function getParameters(): array
15+
{
16+
return [];
17+
}
18+
19+
public function getParameter(string $key): ?ParameterDefinition
20+
{
21+
return null;
22+
}
23+
24+
public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string
25+
{
26+
return null;
27+
}
28+
29+
}

‎src/Symfony/Parameter.php‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
final class Parameter implements ParameterDefinition
6+
{
7+
8+
/** @var string */
9+
private $key;
10+
11+
/** @var array<mixed>|bool|float|int|string */
12+
private $value;
13+
14+
/**
15+
* @param string $key
16+
* @param array<mixed>|bool|float|int|string $value
17+
*/
18+
public function __construct(
19+
string $key,
20+
$value
21+
)
22+
{
23+
$this->key = $key;
24+
$this->value = $value;
25+
}
26+
27+
public function getKey(): string
28+
{
29+
return $this->key;
30+
}
31+
32+
/**
33+
* @return array<mixed>|bool|float|int|string
34+
*/
35+
public function getValue()
36+
{
37+
return $this->value;
38+
}
39+
40+
}

‎src/Symfony/ParameterDefinition.php‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
interface ParameterDefinition
6+
{
7+
8+
public function getKey(): string;
9+
10+
/**
11+
* @return array<mixed>|bool|float|int|string
12+
*/
13+
public function getValue();
14+
15+
}

‎src/Symfony/ParameterMap.php‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
use PhpParser\Node\Expr;
6+
use PHPStan\Analyser\Scope;
7+
8+
interface ParameterMap
9+
{
10+
11+
/**
12+
* @return \PHPStan\Symfony\ParameterDefinition[]
13+
*/
14+
public function getParameters(): array;
15+
16+
public function getParameter(string $key): ?ParameterDefinition;
17+
18+
public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string;
19+
20+
}

‎src/Symfony/ParameterMapFactory.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
interface ParameterMapFactory
6+
{
7+
8+
public function create(): ParameterMap;
9+
10+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Symfony;
4+
5+
use function sprintf;
6+
7+
final class XmlParameterMapFactory implements ParameterMapFactory
8+
{
9+
10+
/** @var string|null */
11+
private $containerXml;
12+
13+
public function __construct(?string $containerXml)
14+
{
15+
$this->containerXml = $containerXml;
16+
}
17+
18+
public function create(): ParameterMap
19+
{
20+
if ($this->containerXml === null) {
21+
return new FakeParameterMap();
22+
}
23+
24+
$fileContents = file_get_contents($this->containerXml);
25+
if ($fileContents === false) {
26+
throw new XmlContainerNotExistsException(sprintf('Container %s does not exist', $this->containerXml));
27+
}
28+
29+
$xml = @simplexml_load_string($fileContents);
30+
if ($xml === false) {
31+
throw new XmlContainerNotExistsException(sprintf('Container %s cannot be parsed', $this->containerXml));
32+
}
33+
34+
/** @var \PHPStan\Symfony\Parameter[] $parameters */
35+
$parameters = [];
36+
foreach ($xml->parameters->parameter as $def) {
37+
/** @var \SimpleXMLElement $attrs */
38+
$attrs = $def->attributes();
39+
40+
$parameter = new Parameter(
41+
(string) $attrs->key,
42+
$this->getNodeValue($def)
43+
);
44+
45+
$parameters[$parameter->getKey()] = $parameter;
46+
}
47+
48+
return new DefaultParameterMap($parameters);
49+
}
50+
51+
/**
52+
* @return array<mixed>|bool|float|int|string
53+
*/
54+
private function getNodeValue(\SimpleXMLElement $def)
55+
{
56+
/** @var \SimpleXMLElement $attrs */
57+
$attrs = $def->attributes();
58+
59+
$value = null;
60+
switch ((string) $attrs->type) {
61+
case 'collection':
62+
$value = [];
63+
foreach ($def->children() as $child) {
64+
/** @var \SimpleXMLElement $childAttrs */
65+
$childAttrs = $child->attributes();
66+
67+
if (isset($childAttrs->key)) {
68+
$value[(string) $childAttrs->key] = $this->getNodeValue($child);
69+
} else {
70+
$value[] = $this->getNodeValue($child);
71+
}
72+
}
73+
break;
74+
75+
case 'string':
76+
$value = (string) $def;
77+
break;
78+
79+
case 'binary':
80+
$value = base64_decode((string) $def, true);
81+
if ($value === false) {
82+
throw new \InvalidArgumentException(sprintf('Parameter "%s" of binary type is not valid base64 encoded string.', (string) $attrs->key));
83+
}
84+
85+
break;
86+
87+
default:
88+
$value = (string) $def;
89+
90+
if (is_numeric($value)) {
91+
if (strpos($value, '.') !== false) {
92+
$value = (float) $value;
93+
} else {
94+
$value = (int) $value;
95+
}
96+
} elseif ($value === 'true') {
97+
$value = true;
98+
} elseif ($value === 'false') {
99+
$value = false;
100+
}
101+
}
102+
103+
return $value;
104+
}
105+
106+
}

0 commit comments

Comments
(0)

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