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 79c938c

Browse files
committed
Merge branch 'get-session-type-specifying' of git://github.com/plotek/phpstan-symfony into plotek-get-session-type-specifying
2 parents 59bb0e0 + 84a19b9 commit 79c938c

File tree

9 files changed

+180
-2
lines changed

9 files changed

+180
-2
lines changed

‎composer.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
"squizlabs/php_codesniffer": "^3.3.2",
4343
"symfony/serializer": "^3.0 || ^4.0",
4444
"symfony/messenger": "^4.2",
45-
"symfony/console": "^3.0 || ^4.0"
45+
"symfony/console": "^3.0 || ^4.0",
46+
"symfony/http-foundation": "^3.0 || ^4.0"
4647
},
4748
"conflict": {
4849
"symfony/framework-bundle": "<3.0"

‎extension.neon‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ services:
5959
factory: PHPStan\Type\Symfony\RequestDynamicReturnTypeExtension
6060
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
6161

62+
# Request::getSession() type specification
63+
-
64+
factory: PHPStan\Type\Symfony\RequestTypeSpecifyingExtension
65+
tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension]
66+
6267
# HeaderBag::get() return type
6368
-
6469
factory: PHPStan\Type\Symfony\HeaderBagDynamicReturnTypeExtension

‎phpstan.neon‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ parameters:
1414
- tests/*/header_bag_get.php
1515
- tests/*/kernel_interface.php
1616
- tests/*/request_get_content.php
17+
- tests/*/request_get_session.php
1718
- tests/*/serializer.php
19+
ignoreErrors:
20+
- '~^Parameter \#1 \$node \(.*\) of method .*Rule::processNode\(\) should be contravariant with parameter \$node \(PhpParser\\Node\) of method PHPStan\\Rules\\Rule::processNode\(\)$~'
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Symfony;
4+
5+
use PhpParser\Node\Expr\MethodCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Analyser\SpecifiedTypes;
8+
use PHPStan\Analyser\TypeSpecifier;
9+
use PHPStan\Analyser\TypeSpecifierAwareExtension;
10+
use PHPStan\Analyser\TypeSpecifierContext;
11+
use PHPStan\Broker\Broker;
12+
use PHPStan\Reflection\MethodReflection;
13+
use PHPStan\Reflection\ParametersAcceptorSelector;
14+
use PHPStan\Type\MethodTypeSpecifyingExtension;
15+
use PHPStan\Type\TypeCombinator;
16+
17+
final class RequestTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
18+
{
19+
20+
private const REQUEST_CLASS = 'Symfony\Component\HttpFoundation\Request';
21+
private const HAS_METHOD_NAME = 'hasSession';
22+
private const GET_METHOD_NAME = 'getSession';
23+
24+
/** @var Broker */
25+
private $broker;
26+
27+
/** @var TypeSpecifier */
28+
private $typeSpecifier;
29+
30+
public function __construct(Broker $broker)
31+
{
32+
$this->broker = $broker;
33+
}
34+
35+
public function getClass(): string
36+
{
37+
return self::REQUEST_CLASS;
38+
}
39+
40+
public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool
41+
{
42+
return $methodReflection->getName() === self::HAS_METHOD_NAME && !$context->null();
43+
}
44+
45+
public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
46+
{
47+
$classReflection = $this->broker->getClass(self::REQUEST_CLASS);
48+
$methodVariants = $classReflection->getNativeMethod(self::GET_METHOD_NAME)->getVariants();
49+
50+
return $this->typeSpecifier->create(
51+
new MethodCall($node->var, self::GET_METHOD_NAME),
52+
TypeCombinator::removeNull(ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType()),
53+
$context
54+
);
55+
}
56+
57+
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
58+
{
59+
$this->typeSpecifier = $typeSpecifier;
60+
}
61+
62+
}

‎tests/Symfony/NeonTest.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public function testExtensionNeon(): void
4747

4848
self::assertCount(6, $container->findByTag('phpstan.rules.rule'));
4949
self::assertCount(12, $container->findByTag('phpstan.broker.dynamicMethodReturnTypeExtension'));
50-
self::assertCount(5, $container->findByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
50+
self::assertCount(6, $container->findByTag('phpstan.typeSpecifier.methodTypeSpecifyingExtension'));
5151
self::assertInstanceOf(ServiceMap::class, $container->getByType(ServiceMap::class));
5252
}
5353

‎tests/Symfony/config.neon‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,14 @@ parameters:
44

55
services:
66
- PhpParser\PrettyPrinter\Standard
7+
8+
-
9+
class: PHPStan\DependencyInjection\Container
10+
factory: PHPStan\DependencyInjection\Nette\NetteContainer
11+
12+
broker:
13+
class: PHPStan\Broker\Broker
14+
factory: @brokerFactory::create
15+
16+
brokerFactory:
17+
class: PHPStan\Broker\BrokerFactory
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\Type\Symfony;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
use PHPStan\Type\MethodTypeSpecifyingExtension;
8+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
9+
10+
final class RequestTypeSpecifyingExtensionTest extends RuleTestCase
11+
{
12+
13+
protected function getRule(): Rule
14+
{
15+
return new VariableTypeReportingRule();
16+
}
17+
18+
/** @return MethodTypeSpecifyingExtension[] */
19+
protected function getMethodTypeSpecifyingExtensions(): array
20+
{
21+
return [
22+
new RequestTypeSpecifyingExtension($this->createBroker()),
23+
];
24+
}
25+
26+
public function testGetSession(): void
27+
{
28+
$this->analyse([__DIR__ . '/request_get_session.php'], [
29+
[
30+
'Variable $session1 is: ' . SessionInterface::class . '|null',
31+
7,
32+
],
33+
[
34+
'Variable $session2 is: ' . SessionInterface::class,
35+
11,
36+
],
37+
]);
38+
}
39+
40+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Symfony;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\Variable;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Type\VerbosityLevel;
10+
11+
final class VariableTypeReportingRule implements Rule
12+
{
13+
14+
public function getNodeType(): string
15+
{
16+
return Variable::class;
17+
}
18+
19+
/**
20+
* @param \PhpParser\Node\Expr\Variable $node
21+
* @param \PHPStan\Analyser\Scope $scope
22+
* @return string[] errors
23+
*/
24+
public function processNode(Node $node, Scope $scope): array
25+
{
26+
if (!is_string($node->name)) {
27+
return [];
28+
}
29+
if (!$scope->isInFirstLevelStatement()) {
30+
return [];
31+
};
32+
if ($scope->isInExpressionAssign($node)) {
33+
return [];
34+
}
35+
return [
36+
sprintf(
37+
'Variable $%s is: %s',
38+
$node->name,
39+
$scope->getType($node)->describe(VerbosityLevel::value())
40+
),
41+
];
42+
}
43+
44+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php declare(strict_types = 1);
2+
3+
/** @var \Symfony\Component\HttpFoundation\Request $request */
4+
$request = doRequest();
5+
6+
$session1 = $request->getSession();
7+
$session1;
8+
9+
if ($request->hasSession()) {
10+
$session2 = $request->getSession();
11+
$session2;
12+
}

0 commit comments

Comments
(0)

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