Skip to content

Commit 8372569

Browse files
committed
feat(rules): Add AbstractMixedTypeRule for node processing
- Introduced AbstractMixedTypeRule as a base class for handling mixed types in PHPStan rules. - Implemented methods for node type retrieval and processing. - Provides a structure for derived rules to define specific node types and processing logic.
1 parent 96ef5b1 commit 8372569

File tree

4 files changed

+70
-11
lines changed

4 files changed

+70
-11
lines changed

src/Rule/AbstractMixedTypeRule.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) 2026 guanguans<ityaozm@gmail.com>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*
11+
* @see https://github.com/guanguans/phpstan-rules
12+
*/
13+
14+
namespace Guanguans\PHPStanRules\Rule;
15+
16+
use PhpParser\Node;
17+
use PHPStan\Analyser\Scope;
18+
use PHPStan\Rules\IdentifierRuleError;
19+
use function Guanguans\PHPStanRules\Support\is_instance_of_any;
20+
21+
/**
22+
* @template TNodeType of Node
23+
*
24+
* @extends AbstractRule<Node>
25+
*/
26+
abstract class AbstractMixedTypeRule extends AbstractRule
27+
{
28+
/**
29+
* @return class-string<TNodeType>
30+
*/
31+
final public function getNodeType(): string
32+
{
33+
return Node::class;
34+
}
35+
36+
/**
37+
* @param TNodeType $node
38+
* @param \PHPStan\Analyser\NodeCallbackInvoker&\PHPStan\Analyser\Scope $scope
39+
*
40+
* @return list<IdentifierRuleError>
41+
*/
42+
final public function processNode(Node $node, Scope $scope): array
43+
{
44+
return is_instance_of_any($node, $this->getNodeTypes()) ? $this->rawProcessNode($node, $scope) : [];
45+
}
46+
47+
/**
48+
* @return list<class-string<TNodeType>>
49+
*/
50+
abstract protected function getNodeTypes(): array;
51+
52+
/**
53+
* @param TNodeType $node
54+
* @param \PHPStan\Analyser\NodeCallbackInvoker&\PHPStan\Analyser\Scope $scope
55+
*
56+
* @return list<IdentifierRuleError>
57+
*/
58+
abstract protected function rawProcessNode(Node $node, Scope $scope): array;
59+
}

src/Rule/Class_/ExceptionMustImplementNativeThrowableRule.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace Guanguans\PHPStanRules\Rule\Class_;
1515

16-
use Guanguans\PHPStanRules\Rule\AbstractRule;
16+
use Guanguans\PHPStanRules\Rule\AbstractMixedTypeRule;
1717
use PhpParser\Node;
1818
use PhpParser\Node\Expr\New_;
1919
use PhpParser\Node\Identifier;
@@ -29,9 +29,9 @@
2929
* @see https://github.com/symfony/ai/blob/main/.phpstan/ForbidNativeExceptionRule.php
3030
* @see https://github.com/thecodingmachine/phpstan-strict-rules/tree/master/src/Rules/Exceptions/
3131
*
32-
* @extends AbstractRule<Class_|New_>
32+
* @extends AbstractMixedTypeRule<Class_|New_>
3333
*/
34-
final class ExceptionMustImplementNativeThrowableRule extends AbstractRule
34+
final class ExceptionMustImplementNativeThrowableRule extends AbstractMixedTypeRule
3535
{
3636
private string $nativeThrowable;
3737

@@ -44,17 +44,20 @@ public function __construct(string $nativeThrowable)
4444
$this->nativeThrowable = $nativeThrowable;
4545
}
4646

47-
public function getNodeType(): string
47+
protected function getNodeTypes(): array
4848
{
49-
return Node::class;
49+
return [
50+
Class_::class,
51+
New_::class,
52+
];
5053
}
5154

5255
/**
5356
* @param \PhpParser\Node\Expr\New_|\PhpParser\Node\Stmt\Class_ $node
5457
*
5558
* @throws \PHPStan\ShouldNotHappenException
5659
*/
57-
public function processNode(Node $node, Scope $scope): array
60+
protected function rawProcessNode(Node $node, Scope $scope): array
5861
{
5962
/** @var list<null|Identifier|Name> $classNameNodes */
6063
$classNameNodes = [];
@@ -67,10 +70,6 @@ public function processNode(Node $node, Scope $scope): array
6770
$classNameNodes = array_merge($classNameNodes, [$node->class]);
6871
}
6972

70-
if ([] === $classNameNodes) {
71-
return [];
72-
}
73-
7473
return array_reduce(
7574
array_filter($classNameNodes),
7675
/**

tests/Rule/Class_/ExceptionMustImplementNativeThrowableRule/ExceptionMustImplementNativeThrowableRuleTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
use Guanguans\PHPStanRulesTests\Rule\Class_\ExceptionMustImplementNativeThrowableRule\Fixtures\NonImplementedNativeThrowableException;
2525

2626
/**
27+
* @covers \Guanguans\PHPStanRules\Rule\AbstractMixedTypeRule
28+
* @covers \Guanguans\PHPStanRules\Rule\AbstractRule
2729
* @covers \Guanguans\PHPStanRules\Rule\Class_\ExceptionMustImplementNativeThrowableRule
2830
*/
2931
final class ExceptionMustImplementNativeThrowableRuleTest extends AbstractRuleTestCase

tests/Rule/File/ForbiddenSideEffectsRule/ForbiddenSideEffectsRuleTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
use Guanguans\PHPStanRulesTests\Rule\AbstractRuleTestCase;
2424

2525
/**
26-
* @covers \Guanguans\PHPStanRules\Rule\AbstractRule
2726
* @covers \Guanguans\PHPStanRules\Rule\File\ForbiddenSideEffectsRule
2827
*/
2928
final class ForbiddenSideEffectsRuleTest extends AbstractRuleTestCase

0 commit comments

Comments
 (0)