Skip to content

Commit

Permalink
Add dynamic return type to ResponseHeaderBag::getCookies
Browse files Browse the repository at this point in the history
  • Loading branch information
franmomu authored Feb 1, 2022
1 parent 784e7a0 commit 11221af
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 0 deletions.
5 changes: 5 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,8 @@ services:
-
factory: PHPStan\Type\Symfony\CommandGetHelperDynamicReturnTypeExtension
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]

# ResponseHeaderBag::getCookies() return type
-
factory: PHPStan\Type\Symfony\ResponseHeaderBagDynamicReturnTypeExtension
tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
65 changes: 65 additions & 0 deletions src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Symfony;

use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\ArrayType;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\IntegerType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

final class ResponseHeaderBagDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{

public function getClass(): string
{
return ResponseHeaderBag::class;
}

public function isMethodSupported(MethodReflection $methodReflection): bool
{
return $methodReflection->getName() === 'getCookies';
}

public function getTypeFromMethodCall(
MethodReflection $methodReflection,
MethodCall $methodCall,
Scope $scope
): Type
{
if (isset($methodCall->getArgs()[0])) {
$node = $methodCall->getArgs()[0]->value;

if (
$node instanceof ClassConstFetch &&
$node->class instanceof Name &&
$node->name instanceof Identifier &&
$node->class->toString() === ResponseHeaderBag::class &&
$node->name->name === 'COOKIES_ARRAY'
) {
return new ArrayType(
new StringType(),
new ArrayType(
new StringType(),
new ArrayType(
new StringType(),
new ObjectType(Cookie::class)
)
)
);
}
}

return new ArrayType(new IntegerType(), new ObjectType(Cookie::class));
}

}
1 change: 1 addition & 0 deletions tests/Type/Symfony/ExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public function dataFileAsserts(): iterable
{
yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php');

if (class_exists('Symfony\Component\HttpFoundation\InputBag')) {
yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag.php');
Expand Down
12 changes: 12 additions & 0 deletions tests/Type/Symfony/data/response_header_bag_get_cookies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php declare(strict_types = 1);

use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use function PHPStan\Testing\assertType;

$headerBag = new ResponseHeaderBag();
$headerBag->setCookie(Cookie::create('cookie_name'));

assertType('array<int, Symfony\Component\HttpFoundation\Cookie>', $headerBag->getCookies());
assertType('array<int, Symfony\Component\HttpFoundation\Cookie>', $headerBag->getCookies(ResponseHeaderBag::COOKIES_FLAT));
assertType('array<string, array<string, array<string, Symfony\Component\HttpFoundation\Cookie>>>', $headerBag->getCookies(ResponseHeaderBag::COOKIES_ARRAY));

0 comments on commit 11221af

Please sign in to comment.