77use Nette \Utils \Strings ;
88use PhpParser \Node ;
99use PhpParser \Node \Expr \ClassConstFetch ;
10+ use PhpParser \Node \Expr \Closure ;
1011use PhpParser \Node \Expr \MethodCall ;
12+ use PhpParser \NodeFinder ;
1113use PHPStan \Analyser \Scope ;
1214use PHPStan \Rules \Rule ;
1315use PHPStan \Rules \RuleError ;
1416use PHPStan \Rules \RuleErrorBuilder ;
1517use Symplify \PHPStanRules \Enum \RuleIdentifier \SymfonyRuleIdentifier ;
1618use Symplify \PHPStanRules \Helper \NamingHelper ;
19+ use Symplify \PHPStanRules \Symfony \NodeAnalyzer \SymfonyClosureDetector ;
1720
1821/**
19- * @implements Rule<MethodCall >
22+ * @implements Rule<Closure >
2023 *
2124 * @see \Symplify\PHPStanRules\Tests\Rules\Symfony\ConfigClosure\NoServiceSameNameSetClassRule\NoServiceSameNameSetClassRuleTest
2225 */
@@ -27,62 +30,93 @@ final class NoServiceSameNameSetClassRule implements Rule
2730 */
2831 public const ERROR_MESSAGE = 'No need to duplicate service class and name. Use only "$services->set(%s::class)" instead ' ;
2932
33+ private NodeFinder $ nodeFinder ;
34+
35+ public function __construct ()
36+ {
37+ $ this ->nodeFinder = new NodeFinder ();
38+ }
39+
3040 public function getNodeType (): string
3141 {
32- return MethodCall ::class;
42+ return Closure ::class;
3343 }
3444
3545 /**
36- * @param MethodCall $node
46+ * @param Closure $node
3747 * @return RuleError[]
3848 */
3949 public function processNode (Node $ node , Scope $ scope ): array
4050 {
41- if ($ node-> isFirstClassCallable ( )) {
51+ if (! SymfonyClosureDetector:: detect ( $ node )) {
4252 return [];
4353 }
4454
45- if (! NamingHelper::isName ($ node ->name , 'set ' )) {
46- return [];
47- }
55+ /** @var MethodCall[] $methodCalls */
56+ $ methodCalls = $ this ->nodeFinder ->findInstanceOf ($ node , MethodCall::class);
4857
49- if (count ($ node ->getArgs ()) !== 2 ) {
50- return [];
58+ $ ruleErrors = [];
59+
60+ foreach ($ methodCalls as $ methodCall ) {
61+ if ($ methodCall ->isFirstClassCallable ()) {
62+ continue ;
63+ }
64+
65+ if (! NamingHelper::isName ($ methodCall ->name , 'set ' )) {
66+ continue ;
67+ }
68+
69+ if (count ($ methodCall ->getArgs ()) !== 2 ) {
70+ continue ;
71+ }
72+
73+ $ serviceNameValue = $ this ->matchTwoArgsOfSameClassConstName ($ methodCall );
74+ if (! is_string ($ serviceNameValue )) {
75+ continue ;
76+ }
77+
78+ if (str_contains ($ serviceNameValue , '\\' )) {
79+ $ serviceNameValue = Strings::after ($ serviceNameValue , '\\' , -1 );
80+ }
81+
82+ $ identifierRuleError = RuleErrorBuilder::message (sprintf (self ::ERROR_MESSAGE , $ serviceNameValue ))
83+ ->identifier (SymfonyRuleIdentifier::NO_SERVICE_SAME_NAME_SET_CLASS )
84+ ->line ($ methodCall ->getStartLine ())
85+ ->build ();
86+
87+ $ ruleErrors [] = $ identifierRuleError ;
5188 }
5289
53- $ serviceName = $ node ->getArgs ()[0 ]->value ;
54- $ serviceType = $ node ->getArgs ()[1 ]->value ;
90+ return $ ruleErrors ;
91+ }
92+
93+ private function matchTwoArgsOfSameClassConstName (MethodCall $ methodCall ): ?string
94+ {
95+ $ serviceName = $ methodCall ->getArgs ()[0 ]->value ;
96+ $ serviceType = $ methodCall ->getArgs ()[1 ]->value ;
5597
5698 if (! $ serviceName instanceof ClassConstFetch) {
57- return [] ;
99+ return null ;
58100 }
59101
60102 if (! $ serviceType instanceof ClassConstFetch) {
61- return [] ;
103+ return null ;
62104 }
63105
64106 $ serviceNameValue = NamingHelper::getName ($ serviceName ->class );
65107 if (! is_string ($ serviceNameValue )) {
66- return [] ;
108+ return null ;
67109 }
68110
69111 $ serviceTypeValue = NamingHelper::getName ($ serviceType ->class );
70112 if (! is_string ($ serviceTypeValue )) {
71- return [] ;
113+ return null ;
72114 }
73115
74116 if ($ serviceNameValue !== $ serviceTypeValue ) {
75- return [] ;
117+ return null ;
76118 }
77119
78- if (str_contains ($ serviceNameValue , '\\' )) {
79- $ serviceNameValue = Strings::after ($ serviceNameValue , '\\' , -1 );
80- }
81-
82- $ identifierRuleError = RuleErrorBuilder::message (sprintf (self ::ERROR_MESSAGE , $ serviceNameValue ))
83- ->identifier (SymfonyRuleIdentifier::NO_SERVICE_SAME_NAME_SET_CLASS )
84- ->build ();
85-
86- return [$ identifierRuleError ];
120+ return $ serviceNameValue ;
87121 }
88122}
0 commit comments