Skip to content

Commit 15aa983

Browse files
authored
Add support to loading components on the fly (#31)
1 parent c5b3669 commit 15aa983

File tree

4 files changed

+103
-0
lines changed

4 files changed

+103
-0
lines changed

extension.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ services:
3131
- class: ARiddlestone\PHPStanCakePHP2\Service\SchemaService
3232
arguments:
3333
schemaPaths: %SchemaService.schemaPaths%
34+
- class: ARiddlestone\PHPStanCakePHP2\LoadComponentOnFlyMethodReturnTypeExtension
35+
tags:
36+
- phpstan.broker.dynamicMethodReturnTypeExtension
3437
parametersSchema:
3538
ModelBehaviorsExtension: structure([
3639
behaviorPaths: listOf(string())
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ARiddlestone\PHPStanCakePHP2;
6+
7+
use Component;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PhpParser\Node\Scalar\String_;
10+
use PHPStan\Analyser\Scope;
11+
use PHPStan\Reflection\MethodReflection;
12+
use PHPStan\Reflection\ReflectionProvider;
13+
use PHPStan\Type\DynamicMethodReturnTypeExtension;
14+
use PHPStan\Type\ObjectType;
15+
use PHPStan\Type\Type;
16+
17+
class LoadComponentOnFlyMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension
18+
{
19+
private ReflectionProvider $reflectionProvider;
20+
21+
public function __construct(ReflectionProvider $reflectionProvider)
22+
{
23+
$this->reflectionProvider = $reflectionProvider;
24+
}
25+
26+
public function getClass(): string
27+
{
28+
return \ComponentCollection::class;
29+
}
30+
31+
public function isMethodSupported(MethodReflection $methodReflection): bool
32+
{
33+
return $methodReflection->getName() === 'load';
34+
}
35+
36+
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
37+
{
38+
$arg = $methodCall->getArgs()[0]->value;
39+
40+
if (!$arg instanceof String_) {
41+
return null;
42+
}
43+
44+
$componentName = $arg->value . 'Component';
45+
46+
if (!$this->reflectionProvider->hasClass($componentName)) {
47+
return null;
48+
}
49+
50+
if (!$this->reflectionProvider->getClass($componentName)->is(Component::class)) {
51+
return null;
52+
}
53+
54+
return new ObjectType($componentName);
55+
}
56+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ARiddlestone\PHPStanCakePHP2\Test\Feature;
6+
7+
use PHPStan\Testing\TypeInferenceTestCase;
8+
9+
class LoadComponentOnFlyMethodReturnTypeExtensionTest extends TypeInferenceTestCase
10+
{
11+
/**
12+
* @return mixed[]
13+
*/
14+
public function dataFileAsserts(): iterable
15+
{
16+
yield from self::gatherAssertTypes(__DIR__ . '/data/loading_component_loaded_on_fly.php');
17+
}
18+
19+
/**
20+
* @dataProvider dataFileAsserts
21+
* @param mixed $args
22+
*/
23+
public function testControllerExtensions(string $assertType, string $file, ...$args): void
24+
{
25+
$this->assertFileAsserts($assertType, $file, ...$args);
26+
}
27+
28+
public static function getAdditionalConfigFiles(): array
29+
{
30+
return [
31+
__DIR__ . '/data/phpstan.neon',
32+
];
33+
}
34+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/** @var BasicController $controller */
8+
$component = $controller->Components->load('Basic');
9+
10+
assertType('BasicComponent', $component);

0 commit comments

Comments
 (0)