Skip to content

Commit 047bed0

Browse files
committed
Add CacheHandlerInstantiationRule
1 parent 9f1765e commit 047bed0

File tree

6 files changed

+140
-1
lines changed

6 files changed

+140
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This extension provides the following features:
1111

1212
* Provides precise return types for `config()` and `model()` functions.
1313
* Checks if the string argument passed to `config()` or `model()` function is a valid class string extending `CodeIgniter\Config\BaseConfig` or `CodeIgniter\Model`, respectively. This can be turned off by setting `codeigniter.checkArgumentTypeOfFactories: false` in your `phpstan.neon`.
14+
* Disallows instantiating cache handlers using `new` and suggests to use the `CacheFactory` class instead.
1415

1516
## Installation
1617

extension.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ services:
3535
conditionalTags:
3636
CodeIgniter\PHPStan\Rules\Functions\FactoriesFunctionArgumentTypeRule:
3737
phpstan.rules.rule: %codeigniter.checkArgumentTypeOfFactories%
38+
39+
rules:
40+
- CodeIgniter\PHPStan\Rules\Classes\CacheHandlerInstantiationRule

phpstan.neon.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ parameters:
1111
excludePaths:
1212
analyseAndScan:
1313
- src/ComposerScripts.php
14+
- tests/Fixtures
1415
tmpDir: build/phpstan
1516
bootstrapFiles:
1617
- vendor/codeigniter4/framework/system/Test/bootstrap.php
1718
codeigniter:
1819
additionalModelNamespaces:
1920
- CodeIgniter\PHPStan\Tests\Fixtures\Type
20-
checkArgumentTypeOfFactories: false
2121
checkTooWideReturnTypesInProtectedAndPublicMethods: true
2222
checkUninitializedProperties: true
2323
checkImplicitMixed: true
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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 <admin@codeigniter.com>
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\Rules\Classes;
15+
16+
use CodeIgniter\Cache\CacheFactory;
17+
use CodeIgniter\Cache\CacheInterface;
18+
use PhpParser\Node;
19+
use PHPStan\Analyser\Scope;
20+
use PHPStan\Rules\Rule;
21+
use PHPStan\Rules\RuleErrorBuilder;
22+
use PHPStan\Type\ObjectType;
23+
24+
/**
25+
* @implements Rule<Node\Expr\New_>
26+
*/
27+
final class CacheHandlerInstantiationRule implements Rule
28+
{
29+
public function getNodeType(): string
30+
{
31+
return Node\Expr\New_::class;
32+
}
33+
34+
/**
35+
* @param Node\Expr\New_ $node
36+
*/
37+
public function processNode(Node $node, Scope $scope): array
38+
{
39+
if (! $node->class instanceof Node\Name) {
40+
return [];
41+
}
42+
43+
$objectType = new ObjectType((string) $node->class);
44+
$reflection = $objectType->getClassReflection();
45+
46+
if ($reflection === null) {
47+
return [];
48+
}
49+
50+
if (! (new ObjectType(CacheInterface::class))->isSuperTypeOf($objectType)->yes()) {
51+
return [];
52+
}
53+
54+
if ($scope->isInClass() && $scope->getClassReflection()->getName() === CacheFactory::class) {
55+
return [];
56+
}
57+
58+
return [RuleErrorBuilder::message(sprintf(
59+
'Calling new %s() directly is not allowed. Use CacheFactory::getHandler() to get the cache instance.',
60+
$reflection->getNativeReflection()->getShortName(),
61+
))->identifier('codeigniter.cacheHandlerInstance')->build()];
62+
}
63+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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 <admin@codeigniter.com>
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+
use CodeIgniter\Cache\Handlers\FileHandler;
15+
use CodeIgniter\Cache\Handlers\RedisHandler;
16+
use Config\Cache;
17+
18+
$handler1 = new FileHandler(new Cache());
19+
$handler2 = new RedisHandler(new Cache());
20+
21+
$cache1 = $handler1->get('foo');
22+
$cache2 = $handler2->get('bar');
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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 <admin@codeigniter.com>
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\Rules\Classes;
15+
16+
use CodeIgniter\PHPStan\Rules\Classes\CacheHandlerInstantiationRule;
17+
use CodeIgniter\PHPStan\Tests\AdditionalConfigFilesTrait;
18+
use PHPStan\Rules\Rule;
19+
use PHPStan\Testing\RuleTestCase;
20+
use PHPUnit\Framework\Attributes\Group;
21+
22+
/**
23+
* @internal
24+
*
25+
* @extends RuleTestCase<CacheHandlerInstantiationRule>
26+
*/
27+
#[Group('Integration')]
28+
final class CacheHandlerInstantiationRuleTest extends RuleTestCase
29+
{
30+
use AdditionalConfigFilesTrait;
31+
32+
protected function getRule(): Rule
33+
{
34+
return new CacheHandlerInstantiationRule();
35+
}
36+
37+
public function testRule(): void
38+
{
39+
$this->analyse([__DIR__ . '/../../Fixtures/Rules/Classes/cache-handler.php'], [
40+
[
41+
'Calling new FileHandler() directly is not allowed. Use CacheFactory::getHandler() to get the cache instance.',
42+
18,
43+
],
44+
[
45+
'Calling new RedisHandler() directly is not allowed. Use CacheFactory::getHandler() to get the cache instance.',
46+
19,
47+
],
48+
]);
49+
}
50+
}

0 commit comments

Comments
 (0)