Skip to content

Commit d7c49b7

Browse files
[FEATURE] Add GeneralUtilityDynamicReturnTypeExtension
1 parent ee443c0 commit d7c49b7

3 files changed

+161
-0
lines changed

extension.neon

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
services:
2+
-
3+
class: FriendsOfTYPO3\PHPStan\TYPO3\Type\GeneralUtilityDynamicReturnTypeExtension
4+
tags:
5+
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FriendsOfTYPO3\PHPStan\TYPO3\Type;
6+
7+
use PhpParser\Node\Arg;
8+
use PhpParser\Node\Expr;
9+
use PhpParser\Node\Expr\ClassConstFetch;
10+
use PhpParser\Node\Expr\StaticCall;
11+
use PhpParser\Node\Name;
12+
use PHPStan\Analyser\Scope;
13+
use PHPStan\Reflection\MethodReflection;
14+
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
15+
use PHPStan\Type\ObjectType;
16+
use PHPStan\Type\ObjectWithoutClassType;
17+
use PHPStan\Type\Type;
18+
use TYPO3\CMS\Core\Utility\GeneralUtility;
19+
20+
class GeneralUtilityDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
21+
{
22+
public function getClass(): string
23+
{
24+
return GeneralUtility::class;
25+
}
26+
27+
public function isStaticMethodSupported(MethodReflection $methodReflection): bool
28+
{
29+
return $methodReflection->getName() === 'makeInstance';
30+
}
31+
32+
public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): Type
33+
{
34+
if (empty($methodCall->args)) {
35+
return new ObjectWithoutClassType();
36+
}
37+
38+
/** @var Arg $argument */
39+
$argument = $methodCall->args[0];
40+
41+
/** @var Expr $argumentValue */
42+
$argumentValue = $argument->value;
43+
44+
if (!$argumentValue instanceof ClassConstFetch) {
45+
return new ObjectWithoutClassType();
46+
}
47+
/** @var ClassConstFetch $argumentValue */
48+
49+
$class = $argumentValue->class;
50+
51+
if (!$class instanceof Name) {
52+
return new ObjectWithoutClassType();
53+
}
54+
/** @var Name $class */
55+
56+
$className = $class->toString();
57+
58+
return new ObjectType($className);
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FriendsOfTYPO3\PHPStan\TYPO3\Tests\Unit\Type;
6+
7+
use FriendsOfTYPO3\PHPStan\TYPO3\Type\GeneralUtilityDynamicReturnTypeExtension;
8+
9+
use PhpParser\Lexer\Emulative;
10+
use PhpParser\Node\Expr\Assign;
11+
use PhpParser\Node\Expr\StaticCall;
12+
use PhpParser\Node\Stmt\Expression;
13+
use PhpParser\ParserFactory;
14+
use PHPStan\Analyser\Scope;
15+
use PHPStan\Reflection\MethodReflection;
16+
use PHPStan\Type\ObjectType;
17+
use PHPUnit\Framework\TestCase;
18+
use TYPO3\CMS\Core\DataHandling\DataHandler;
19+
use TYPO3\CMS\Core\Utility\GeneralUtility;
20+
21+
class GeneralUtilityDynamicReturnTypeExtensionTest extends TestCase
22+
{
23+
/**
24+
* @var GeneralUtilityDynamicReturnTypeExtension
25+
*/
26+
private $extension;
27+
28+
protected function setUp(): void
29+
{
30+
$this->extension = new GeneralUtilityDynamicReturnTypeExtension();
31+
}
32+
33+
public function testGetClass(): void
34+
{
35+
static::assertSame(GeneralUtility::class, $this->extension->getClass());
36+
}
37+
38+
/**
39+
* @return \Generator<int,array<string|bool>>
40+
*/
41+
public function dataIsMethodSupported(): \Generator
42+
{
43+
yield ['makeInstance', true];
44+
yield ['foo', false];
45+
}
46+
47+
/**
48+
* @dataProvider dataIsMethodSupported
49+
* @param string $method
50+
* @param bool $expectedResult
51+
*/
52+
public function testIsMethodSupported(string $method, bool $expectedResult): void
53+
{
54+
$methodReflection = $this->prophesize(MethodReflection::class);
55+
$methodReflection->getName()->willReturn($method);
56+
57+
self::assertSame($expectedResult, $this->extension->isStaticMethodSupported($methodReflection->reveal()));
58+
}
59+
60+
public function testGetTypeFromMethodCall(): void
61+
{
62+
$lexer = new Emulative(['usedAttributes' => []]);
63+
$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, $lexer);
64+
65+
$code = <<<CODE
66+
<?php
67+
68+
\$object = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
69+
CODE;
70+
71+
$expressions = $parser->parse($code);
72+
static::assertIsArray($expressions);
73+
74+
/** @var array $expressions */
75+
reset($expressions);
76+
77+
/** @var Expression $expression */
78+
$expression = current($expressions);
79+
80+
/** @var Assign $assignment */
81+
$assignment = $expression->expr;
82+
83+
/** @var StaticCall $staticCall */
84+
$staticCall = $assignment->expr;
85+
86+
/** @var ObjectType $type */
87+
$type = $this->extension->getTypeFromStaticMethodCall(
88+
$this->prophesize(MethodReflection::class)->reveal(),
89+
$staticCall,
90+
$this->prophesize(Scope::class)->reveal()
91+
);
92+
93+
static::assertInstanceOf(ObjectType::class, $type);
94+
static::assertSame(DataHandler::class, $type->getClassName());
95+
}
96+
}

0 commit comments

Comments
 (0)