Skip to content

Commit 89fe461

Browse files
committed
Add extension for Services::getSharedInstance()
1 parent 7145491 commit 89fe461

5 files changed

+173
-2
lines changed

docs/type-inference.md

+60-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
All type inference capabilities of this extension are summarised below:
44

5+
## Dynamic Function Return Type Extensions
6+
7+
### ServicesFunctionReturnTypeExtension
8+
9+
This extension provides precise return types for the `service()` and `single_service()` functions.
10+
11+
**Before:**
12+
```php
13+
\PHPStan\dumpType(service('cache')); // object|null
14+
15+
```
16+
17+
**After:**
18+
```php
19+
\PHPStan\dumpType(service('cache')); // CodeIgniter\Cache\CacheInterface
20+
```
21+
522
## Dynamic Static Method Return Type Extensions
623

724
### ReflectionHelperGetPrivateMethodInvokerReturnTypeExtension
@@ -20,9 +37,10 @@ class Foo
2037
return true;
2138
}
2239
}
40+
2341
```
2442

25-
**Before**
43+
**Before:**
2644
```php
2745
public function testSomePrivateMethod(): void
2846
{
@@ -32,7 +50,7 @@ public function testSomePrivateMethod(): void
3250

3351
```
3452

35-
**After**
53+
**After:**
3654
```php
3755
public function testSomePrivateMethod(): void
3856
{
@@ -57,3 +75,43 @@ public function testSomePrivateMethod(): void
5775
> class: <Fully qualified class name of class using ReflectionHelper>
5876
>
5977
> ```
78+
79+
### ServicesGetSharedInstanceReturnTypeExtension
80+
81+
This extension provides precise return type for the protected static method `getSharedInstance()` of `Services`.
82+
83+
**Before:**
84+
```php
85+
<?php
86+
class MyService extends \Config\Services
87+
{
88+
public static function bar(bool $getShared = true): Bar
89+
{
90+
if ($getShared) {
91+
\PHPStan\dumpType(static::getSharedInstance('bar')); // object
92+
return static::getSharedInstance('bar');
93+
}
94+
95+
return new Bar();
96+
}
97+
}
98+
99+
```
100+
101+
**After:**
102+
```php
103+
<?php
104+
class MyService extends \Config\Services
105+
{
106+
public static function bar(bool $getShared = true): Bar
107+
{
108+
if ($getShared) {
109+
\PHPStan\dumpType(static::getSharedInstance('bar')); // Bar
110+
return static::getSharedInstance('bar');
111+
}
112+
113+
return new Bar();
114+
}
115+
}
116+
117+
```

extension.neon

+5
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ services:
8585
arguments:
8686
class: CodeIgniter\Test\CIUnitTestCase
8787

88+
-
89+
class: CodeIgniter\PHPStan\Type\ServicesGetSharedInstanceReturnTypeExtension
90+
tags:
91+
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
92+
8893
# conditional rules
8994
-
9095
class: CodeIgniter\PHPStan\Rules\Functions\FactoriesFunctionArgumentTypeRule
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) 2023 CodeIgniter Foundation <[email protected]>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace CodeIgniter\PHPStan\Type;
15+
16+
use CodeIgniter\Config\BaseService;
17+
use PhpParser\Node\Expr\StaticCall;
18+
use PHPStan\Analyser\Scope;
19+
use PHPStan\Reflection\MethodReflection;
20+
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
21+
use PHPStan\Type\Type;
22+
23+
class ServicesGetSharedInstanceReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
24+
{
25+
public function __construct(
26+
private readonly ServicesReturnTypeHelper $servicesReturnTypeHelper,
27+
) {}
28+
29+
public function getClass(): string
30+
{
31+
return BaseService::class;
32+
}
33+
34+
public function isStaticMethodSupported(MethodReflection $methodReflection): bool
35+
{
36+
return $methodReflection->getName() === 'getSharedInstance';
37+
}
38+
39+
public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): ?Type
40+
{
41+
$arguments = $methodCall->getArgs();
42+
43+
if ($arguments === []) {
44+
return null;
45+
}
46+
47+
return $this->servicesReturnTypeHelper->check($scope->getType($arguments[0]->value), $scope);
48+
}
49+
}

tests/Type/DynamicStaticMethodReturnTypeExtensionTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,7 @@ public function testFileAsserts(string $assertType, string $file, mixed ...$args
3838
public static function provideFileAssertsCases(): iterable
3939
{
4040
yield from self::gatherAssertTypes(__DIR__ . '/data/reflection-helper.php');
41+
42+
yield from self::gatherAssertTypes(__DIR__ . '/data/services-get-shared-instance.php');
4143
}
4244
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) 2023 CodeIgniter Foundation <[email protected]>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace CodeIgniter\PHPStan\Tests\Fixtures\Type;
15+
16+
use CodeIgniter\Cache\CacheInterface;
17+
use CodeIgniter\CLI\Commands;
18+
use CodeIgniter\Config\Services;
19+
use CodeIgniter\Debug\Exceptions;
20+
use CodeIgniter\Debug\Iterator;
21+
use CodeIgniter\Debug\Toolbar;
22+
use CodeIgniter\Email\Email;
23+
use CodeIgniter\Filters\Filters;
24+
use CodeIgniter\HTTP\CLIRequest;
25+
use CodeIgniter\HTTP\ResponseInterface;
26+
use CodeIgniter\Log\Logger;
27+
use CodeIgniter\Pager\Pager;
28+
use CodeIgniter\Validation\ValidationInterface;
29+
use CodeIgniter\View\Cell;
30+
use CodeIgniter\View\Parser;
31+
use CodeIgniter\View\View;
32+
33+
use function PHPStan\Testing\assertType;
34+
35+
final class SharedServices extends Services
36+
{
37+
public static function forTesting(): void
38+
{
39+
assertType(CacheInterface::class, self::getSharedInstance('cache'));
40+
assertType(Commands::class, self::getSharedInstance('commands'));
41+
assertType(CLIRequest::class, self::getSharedInstance('clirequest'));
42+
assertType(Email::class, self::getSharedInstance('email'));
43+
assertType(Exceptions::class, self::getSharedInstance('exceptions'));
44+
assertType(Filters::class, self::getSharedInstance('filters'));
45+
assertType(Iterator::class, self::getSharedInstance('iterator'));
46+
assertType(Logger::class, self::getSharedInstance('logger'));
47+
assertType(Pager::class, self::getSharedInstance('pager'));
48+
assertType(Parser::class, self::getSharedInstance('parser'));
49+
assertType(View::class, self::getSharedInstance('renderer'));
50+
assertType('CodeIgniter\HTTP\CLIRequest|CodeIgniter\HTTP\IncomingRequest', self::getSharedInstance('request'));
51+
assertType(ResponseInterface::class, self::getSharedInstance('response'));
52+
assertType(Toolbar::class, self::getSharedInstance('toolbar'));
53+
assertType(ValidationInterface::class, self::getSharedInstance('validation'));
54+
assertType(Cell::class, self::getSharedInstance('viewcell'));
55+
assertType('null', self::getSharedInstance('createRequest'));
56+
}
57+
}

0 commit comments

Comments
 (0)