Skip to content

Commit 66d07c1

Browse files
Support Interop and PSR containers for $container->get() resolution
1 parent f5c0da2 commit 66d07c1

8 files changed

Lines changed: 144 additions & 22 deletions

File tree

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@
1010

1111
This extension provides following features:
1212

13-
1. Provide correct return for `\Laminas\ServiceManager\ServiceLocatorInterface::get()`
14-
1. Handle controller plugins that are called using magic `__call()` in subclasses of
13+
1. Provide correct return type for `$container->get()` calls on containers of type
14+
`\Laminas\ServiceManager\ServiceLocatorInterface`, `\Interop\Container\ContainerInterface` or `\Psr\Container\ContainerInterface`
15+
2. Handle controller plugins that are called using magic `__call()` in subclasses of
1516
`\Laminas\Mvc\Controller\AbstractController`
16-
1. Provide correct return type for `plugin` method of `AbstractController`, `FilterChain`, `PhpRenderer` and `ValidatorChain`
17-
1. `getApplication()`, `getRenderer()`, `getRequest()` and `getResponse()` methods on Controllers, MvcEvents, View,
17+
3. Provide correct return type for `plugin` method of `AbstractController`, `FilterChain`, `PhpRenderer` and `ValidatorChain`
18+
4. `getApplication()`, `getRenderer()`, `getRequest()` and `getResponse()` methods on Controllers, MvcEvents, View,
1819
ViewEvent and Application returns the real instance instead of type-hinted interfaces
19-
1. `getView()` method on `\Laminas\View\Helper\AbstractHelper` returns the real Renderer instance instead of type-hinted
20+
5. `getView()` method on `\Laminas\View\Helper\AbstractHelper` returns the real Renderer instance instead of type-hinted
2021
interface
21-
1. `\Laminas\Stdlib\ArrayObject` is configured as a [Universal object crate](https://phpstan.org/config-reference#universal-object-crates)
22-
1. Handle `\Laminas\Stdlib\AbstractOptions` magic properties
22+
6. `\Laminas\Stdlib\ArrayObject` is configured as a [Universal object crate](https://phpstan.org/config-reference#universal-object-crates)
23+
7. Handle `\Laminas\Stdlib\AbstractOptions` magic properties
2324

2425
## Installation
2526

composer.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
],
2020
"require": {
2121
"php": "^7.4 || ^8.0",
22-
"phpstan/phpstan": "^0.12.88"
22+
"phpstan/phpstan": "^0.12.99"
2323
},
2424
"conflict": {
2525
"laminas/laminas-cache": "<2.11",
@@ -35,20 +35,20 @@
3535
"laminas/laminas-validator": "<2.14"
3636
},
3737
"require-dev": {
38-
"laminas/laminas-cache": "^2.11.1",
39-
"laminas/laminas-filter": "^2.11.0",
40-
"laminas/laminas-form": "^2.16.3",
41-
"laminas/laminas-hydrator": "^4.1.0",
42-
"laminas/laminas-i18n": "^2.11.1",
38+
"laminas/laminas-cache": "^2.13.0",
39+
"laminas/laminas-filter": "^2.11.1",
40+
"laminas/laminas-form": "^2.17.0",
41+
"laminas/laminas-hydrator": "^4.3.1",
42+
"laminas/laminas-i18n": "^2.11.2",
4343
"laminas/laminas-inputfilter": "^2.12.0",
4444
"laminas/laminas-log": "^2.13.1",
45-
"laminas/laminas-mail": "^2.14.0",
45+
"laminas/laminas-mail": "^2.14.1",
4646
"laminas/laminas-mvc": "^3.2.0",
4747
"laminas/laminas-paginator": "^2.10.0",
48-
"laminas/laminas-validator": "^2.14.4",
48+
"laminas/laminas-validator": "^2.15.0",
4949
"malukenho/mcbumpface": "^1.1.5",
50-
"phpstan/phpstan-phpunit": "^0.12.19",
51-
"phpunit/phpunit": "^9.5.4",
50+
"phpstan/phpstan-phpunit": "^0.12.22",
51+
"phpunit/phpunit": "^9.5.9",
5252
"slam/php-cs-fixer-extensions": "^v3.0.1",
5353
"slam/php-debug-r": "^v1.7.0"
5454
},

src/Rules/Laminas/ServiceManagerGetMethodCallRule.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace LaminasPhpStan\Rules\Laminas;
66

7+
use Interop\Container\ContainerInterface as InteropContainerInterface;
78
use Laminas\ServiceManager\AbstractPluginManager;
89
use Laminas\ServiceManager\ServiceLocatorInterface;
910
use LaminasPhpStan\ServiceManagerLoader;
@@ -14,6 +15,7 @@
1415
use PHPStan\Rules\Rule;
1516
use PHPStan\Type\Constant\ConstantStringType;
1617
use PHPStan\Type\ObjectType;
18+
use Psr\Container\ContainerInterface as PsrContainerInterface;
1719
use ReflectionClass;
1820

1921
/**
@@ -53,7 +55,7 @@ public function processNode(Node $node, Scope $scope): array
5355
}
5456

5557
$calledOnType = $scope->getType($node->var);
56-
if (! $calledOnType instanceof ObjectType || ! $calledOnType->isInstanceOf(ServiceLocatorInterface::class)->yes()) {
58+
if (! $calledOnType instanceof ObjectType || ! $this->isTypeInstanceOfContainer($calledOnType)) {
5759
return [];
5860
}
5961

@@ -97,4 +99,11 @@ public function processNode(Node $node, Scope $scope): array
9799
$classDoesNotExistNote
98100
)];
99101
}
102+
103+
protected function isTypeInstanceOfContainer(ObjectType $type): bool
104+
{
105+
return $type->isInstanceOf(ServiceLocatorInterface::class)->yes()
106+
|| $type->isInstanceOf(InteropContainerInterface::class)->yes()
107+
|| $type->isInstanceOf(PsrContainerInterface::class)->yes();
108+
}
100109
}

tests/Rules/Laminas/PluginManagerGetMethodCallRuleTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
/**
1414
* @covers \LaminasPhpStan\Rules\Laminas\ServiceManagerGetMethodCallRule
15+
* @extends RuleTestCase<ServiceManagerGetMethodCallRule>
1516
*/
1617
final class PluginManagerGetMethodCallRuleTest extends RuleTestCase
1718
{
@@ -22,6 +23,9 @@ protected function setUp(): void
2223
$this->serviceManagerLoader = new ServiceManagerLoader(null);
2324
}
2425

26+
/**
27+
* @return Rule<\PhpParser\Node\Expr\MethodCall>
28+
*/
2529
protected function getRule(): Rule
2630
{
2731
return new ServiceManagerGetMethodCallRule($this->createBroker(), $this->serviceManagerLoader);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace LaminasPhpStan\Tests\Rules\Laminas\ServiceManagerGetMethodCallRule;
6+
7+
use Interop\Container\ContainerInterface;
8+
use Laminas\Form\FormElementManager;
9+
use Laminas\Mvc\Controller\ControllerManager;
10+
use stdClass;
11+
12+
final class InteropContainerFoo
13+
{
14+
private ContainerInterface $container;
15+
16+
public function __construct(ContainerInterface $container)
17+
{
18+
$this->container = $container;
19+
}
20+
21+
public function foo(): void
22+
{
23+
$this->container->get('non_existent_service');
24+
25+
$this->container->get('EventManager');
26+
$this->container->get('foo', 'bar');
27+
$this->container->get([]);
28+
29+
$getterName = 'get';
30+
$this->container->{$getterName}('EventManager');
31+
$this->container->has('EventManager');
32+
33+
$stdClass = new stdClass();
34+
$stdClass->get('non_existent_service');
35+
36+
$this->container->get(ControllerManager::class);
37+
$this->container->get(FormElementManager::class);
38+
}
39+
40+
public function get(string $foo): void
41+
{
42+
}
43+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace LaminasPhpStan\Tests\Rules\Laminas\ServiceManagerGetMethodCallRule;
6+
7+
use Laminas\Form\FormElementManager;
8+
use Laminas\Mvc\Controller\ControllerManager;
9+
use Psr\Container\ContainerInterface;
10+
use stdClass;
11+
12+
final class PsrContainerFoo
13+
{
14+
private ContainerInterface $container;
15+
16+
public function __construct(ContainerInterface $container)
17+
{
18+
$this->container = $container;
19+
}
20+
21+
public function foo(): void
22+
{
23+
$this->container->get('non_existent_service');
24+
25+
$this->container->get('EventManager');
26+
$this->container->get('foo', 'bar');
27+
$this->container->get([]);
28+
29+
$getterName = 'get';
30+
$this->container->{$getterName}('EventManager');
31+
$this->container->has('EventManager');
32+
33+
$stdClass = new stdClass();
34+
$stdClass->get('non_existent_service');
35+
36+
$this->container->get(ControllerManager::class);
37+
$this->container->get(FormElementManager::class);
38+
}
39+
40+
public function get(string $foo): void
41+
{
42+
}
43+
}

tests/Rules/Laminas/ServiceManagerGetMethodCallRule/Foo.php renamed to tests/Rules/Laminas/ServiceManagerGetMethodCallRule/ServiceManagerFoo.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use Laminas\ServiceManager\ServiceManager;
1010
use stdClass;
1111

12-
final class Foo
12+
final class ServiceManagerFoo
1313
{
1414
private ServiceManager $serviceManager;
1515

tests/Rules/Laminas/ServiceManagerGetMethodCallRuleTest.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,56 @@
44

55
namespace LaminasPhpStan\Tests\Rules\Laminas;
66

7+
use Interop\Container\ContainerInterface as InteropContainerInterface;
8+
use Laminas\ServiceManager\ServiceManager;
79
use LaminasPhpStan\Rules\Laminas\ServiceManagerGetMethodCallRule;
810
use LaminasPhpStan\ServiceManagerLoader;
911
use PHPStan\Rules\Rule;
1012
use PHPStan\Testing\RuleTestCase;
13+
use Psr\Container\ContainerInterface as PsrContainerInterface;
1114

1215
/**
1316
* @covers \LaminasPhpStan\Rules\Laminas\ServiceManagerGetMethodCallRule
1417
* @covers \LaminasPhpStan\UnmappedAliasServiceLocatorProxy
18+
* @extends RuleTestCase<ServiceManagerGetMethodCallRule>
1519
*/
1620
final class ServiceManagerGetMethodCallRuleTest extends RuleTestCase
1721
{
1822
private ServiceManagerLoader $serviceManagerLoader;
1923

24+
/**
25+
* @return string[][]
26+
*/
27+
public function provideContainerTypes(): array
28+
{
29+
return [
30+
'ServiceManager' => ['ServiceManagerFoo.php', ServiceManager::class],
31+
'Interop container' => ['InteropContainerFoo.php', InteropContainerInterface::class],
32+
'PSR container' => ['PsrContainerFoo.php', PsrContainerInterface::class],
33+
];
34+
}
35+
2036
protected function setUp(): void
2137
{
2238
$this->serviceManagerLoader = new ServiceManagerLoader(null);
2339
}
2440

41+
/**
42+
* @return Rule<\PhpParser\Node\Expr\MethodCall>
43+
*/
2544
protected function getRule(): Rule
2645
{
2746
return new ServiceManagerGetMethodCallRule($this->createBroker(), $this->serviceManagerLoader);
2847
}
2948

30-
public function testRule(): void
49+
/**
50+
* @dataProvider provideContainerTypes
51+
*/
52+
public function testRule(string $filename, string $containerClassname): void
3153
{
32-
$this->analyse([__DIR__ . '/ServiceManagerGetMethodCallRule/Foo.php'], [
54+
$this->analyse([__DIR__ . '/ServiceManagerGetMethodCallRule/' . $filename], [
3355
[
34-
'The service "non_existent_service" was not configured in Laminas\ServiceManager\ServiceManager.',
56+
'The service "non_existent_service" was not configured in ' . $containerClassname . '.',
3557
23,
3658
],
3759
]);

0 commit comments

Comments
 (0)