Skip to content

Commit 631ce99

Browse files
committed
Add FrameworkExceptionInstantiationRule
1 parent 2230c5f commit 631ce99

File tree

5 files changed

+135
-0
lines changed

5 files changed

+135
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This extension provides the following features:
1313
* Provides precise return types for `service()` and `single_service()` functions.
1414
* 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`.
1515
* Disallows instantiating cache handlers using `new` and suggests to use the `CacheFactory` class instead.
16+
* Disallows instantiating `FrameworkException` classes using `new`.
1617

1718
## Installation
1819

extension.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ conditionalTags:
5050

5151
rules:
5252
- CodeIgniter\PHPStan\Rules\Classes\CacheHandlerInstantiationRule
53+
- CodeIgniter\PHPStan\Rules\Classes\FrameworkExceptionInstantiationRule
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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\Exceptions\FrameworkException;
17+
use PhpParser\Node;
18+
use PHPStan\Analyser\Scope;
19+
use PHPStan\Rules\Rule;
20+
use PHPStan\Rules\RuleErrorBuilder;
21+
use PHPStan\ShouldNotHappenException;
22+
use PHPStan\Type\ObjectType;
23+
24+
/**
25+
* @implements Rule<Node\Expr\New_>
26+
*/
27+
final class FrameworkExceptionInstantiationRule 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+
$class = $node->class;
40+
41+
if (! $class instanceof Node\Name) {
42+
return [];
43+
}
44+
45+
$objectType = new ObjectType($class->toString());
46+
47+
if (! (new ObjectType(FrameworkException::class))->isSuperTypeOf($objectType)->yes()) {
48+
return [];
49+
}
50+
51+
if ($objectType->getClassReflection() === null) {
52+
throw new ShouldNotHappenException();
53+
}
54+
55+
return [RuleErrorBuilder::message(sprintf(
56+
'Instantiating %s using new is not allowed. Use one of its named constructors instead.',
57+
$objectType->getClassReflection()->getNativeReflection()->getShortName()
58+
))->identifier('codeigniter.frameworkExceptionInstance')->build()];
59+
}
60+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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\Exceptions\FrameworkException;
15+
use CodeIgniter\View\Exceptions\ViewException;
16+
17+
$e1 = new FrameworkException('Hello.');
18+
$e2 = new ViewException('Hi!');
19+
$e3 = new RuntimeException('Thanks.');
20+
$e4 = new \CodeIgniter\HTTP\Exceptions\HTTPException('Nice');
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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\FrameworkExceptionInstantiationRule;
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+
* @extends RuleTestCase<FrameworkExceptionInstantiationRule>
25+
*/
26+
#[Group('integration')]
27+
final class FrameworkExceptionInstantiationRuleTest extends RuleTestCase
28+
{
29+
use AdditionalConfigFilesTrait;
30+
31+
protected function getRule(): Rule
32+
{
33+
return new FrameworkExceptionInstantiationRule();
34+
}
35+
36+
public function testRule(): void
37+
{
38+
$this->analyse([__DIR__ . '/../../Fixtures/Rules/Classes/framework-exception.php'], [
39+
[
40+
'Instantiating FrameworkException using new is not allowed. Use one of its named constructors instead.',
41+
17,
42+
],
43+
[
44+
'Instantiating ViewException using new is not allowed. Use one of its named constructors instead.',
45+
18,
46+
],
47+
[
48+
'Instantiating HTTPException using new is not allowed. Use one of its named constructors instead.',
49+
20,
50+
],
51+
]);
52+
}
53+
}

0 commit comments

Comments
 (0)