77use PhpParser \Node ;
88use PhpParser \Node \Arg ;
99use PhpParser \Node \Expr ;
10+ use PhpParser \Node \Expr \BinaryOp ;
1011use PhpParser \Node \Expr \BinaryOp \Equal ;
1112use PhpParser \Node \Expr \BinaryOp \Identical ;
13+ use PhpParser \Node \Expr \BinaryOp \NotEqual ;
14+ use PhpParser \Node \Expr \BinaryOp \NotIdentical ;
15+ use PhpParser \Node \Expr \BooleanNot ;
16+ use PhpParser \Node \Expr \CallLike ;
17+ use PhpParser \Node \Expr \FuncCall ;
1218use PhpParser \Node \Expr \MethodCall ;
1319use PhpParser \Node \Expr \StaticCall ;
20+ use PhpParser \Node \Identifier ;
1421use PhpParser \Node \Scalar \String_ ;
1522use PHPStan \Type \ObjectType ;
1623use RectorLaravel \AbstractRector ;
@@ -25,82 +32,169 @@ class AppEnvironmentComparisonToParameterRector extends AbstractRector
2532 public function getRuleDefinition (): RuleDefinition
2633 {
2734 return new RuleDefinition (
28- 'Replace `$ app-> environment() === \' local \' ` with `$app->environment( \' local \' )` ' ,
35+ 'Replace app environment comparison with parameter or method call ' ,
2936 [
3037 new CodeSample (
3138 <<<'CODE_SAMPLE'
32- $app->environment() === 'production';
39+ $app->environment() === 'local';
40+ $app->environment() !== 'production';
41+ $app->environment() === 'testing';
42+ in_array($app->environment(), ['local', 'testing']);
3343CODE_SAMPLE
3444 ,
3545 <<<'CODE_SAMPLE'
36- $app->environment('production');
46+ $app->isLocal();
47+ ! $app->isProduction();
48+ $app->environment('testing');
49+ $app->environment(['local', 'testing']);
3750CODE_SAMPLE
3851 ),
3952 ]
4053 );
4154 }
4255
56+ /** @return list<class-string<Node>> */
4357 public function getNodeTypes (): array
4458 {
45- return [Expr ::class];
59+ return [FuncCall::class, Identical::class, Equal::class, NotIdentical::class, NotEqual ::class];
4660 }
4761
48- public function refactor (Node $ node ): MethodCall |StaticCall |null
62+ /** @param FuncCall|Identical|Equal|NotIdentical|NotEqual $node */
63+ public function refactor (Node $ node ): ?Expr
4964 {
50- if (! $ node instanceof Identical && ! $ node instanceof Equal) {
65+ if ($ node instanceof FuncCall) {
66+ [$ methodCall , $ otherNode ] = $ this ->handleFuncCall ($ node );
67+ } else {
68+ [$ methodCall , $ otherNode ] = $ this ->handleBinaryOp ($ node );
69+ }
70+
71+ if ($ methodCall === null || $ otherNode === null ) {
5172 return null ;
5273 }
5374
54- /** @var MethodCall|StaticCall|null $methodCall */
75+ $ methodName = null ;
76+
77+ if ($ otherNode instanceof String_) {
78+ $ methodName = $ this ->getMethodName ($ methodCall , $ otherNode ->value );
79+ }
80+
81+ if ($ methodName === null ) {
82+ $ methodCall ->args [] = new Arg ($ otherNode );
83+ } else {
84+ $ methodCall ->name = new Identifier ($ methodName );
85+ }
86+
87+ if ($ node instanceof NotIdentical || $ node instanceof NotEqual) {
88+ return new BooleanNot ($ methodCall );
89+ }
90+
91+ return $ methodCall ;
92+ }
93+
94+ /** @return array{0: MethodCall|StaticCall|null, 1: Expr|null} */
95+ private function handleFuncCall (FuncCall $ funcCall ): array
96+ {
97+ if (! $ this ->isName ($ funcCall ->name , 'in_array ' )) {
98+ return [null , null ];
99+ }
100+
101+ /** @todo remove and use ->getArg() instead when rector is bundled with php-parser 5.6 */
102+ $ methodCall = $ this ->getArg ($ funcCall , 'needle ' , 0 );
103+ $ haystack = $ this ->getArg ($ funcCall , 'haystack ' , 1 );
104+ // $methodCall = $funcCall->getArg('needle', 0);
105+ // $haystack = $funcCall->getArg('haystack', 1);
106+
107+ if (! $ haystack instanceof Arg || ! $ methodCall instanceof Arg || ! $ this ->validMethodCall ($ methodCall ->value )) {
108+ return [null , null ];
109+ }
110+
111+ return [$ methodCall ->value , $ haystack ->value ];
112+ }
113+
114+ /** @return array{0: MethodCall|StaticCall|null, 1: Expr|null} */
115+ private function handleBinaryOp (BinaryOp $ binaryOp ): array
116+ {
55117 $ methodCall = array_values (
56118 array_filter (
57- [$ node ->left , $ node ->right ],
58- fn ($ node ) => ($ node instanceof MethodCall || $ node instanceof StaticCall) && $ this ->isName (
59- $ node ->name ,
60- 'environment '
61- )
119+ [$ binaryOp ->left , $ binaryOp ->right ],
120+ fn ($ node ) => $ this ->validMethodCall ($ node ),
62121 )
63122 )[0 ] ?? null ;
64123
65- if ($ methodCall === null || ! $ this ->validMethodCall ($ methodCall )) {
66- return null ;
67- }
68-
69- /** @var Expr $otherNode */
70124 $ otherNode = array_values (
71- array_filter ([$ node ->left , $ node ->right ], static fn ($ node ) => $ node !== $ methodCall )
125+ array_filter ([$ binaryOp ->left , $ binaryOp ->right ], static fn ($ node ) => $ node !== $ methodCall )
72126 )[0 ] ?? null ;
73127
74- if (! $ otherNode instanceof String_) {
75- return null ;
76- }
128+ return [$ methodCall , $ otherNode ];
129+ }
77130
78- // make sure the method call has no arguments
79- if ($ methodCall ->getArgs () !== []) {
80- return null ;
131+ /** @phpstan-assert-if-true MethodCall|StaticCall $node */
132+ private function validMethodCall (Node $ node ): bool
133+ {
134+ if (! $ node instanceof MethodCall && ! $ node instanceof StaticCall) {
135+ return false ;
81136 }
82137
83- $ methodCall ->args [] = new Arg ($ otherNode );
138+ if (! $ this ->isName ($ node ->name , 'environment ' )) {
139+ return false ;
140+ }
84141
85- return $ methodCall ;
86- }
142+ // make sure the method call has no arguments
143+ if ($ node ->getArgs () !== []) {
144+ return false ;
145+ }
87146
88- private function validMethodCall (MethodCall |StaticCall $ methodCall ): bool
89- {
90147 return match (true ) {
91- $ methodCall instanceof MethodCall && $ this ->isObjectType (
92- $ methodCall ->var ,
148+ $ node instanceof MethodCall && $ this ->isObjectType (
149+ $ node ->var ,
93150 new ObjectType ('Illuminate\Contracts\Foundation\Application ' )
94151 ) => true ,
95- $ methodCall instanceof StaticCall && $ this ->isObjectType (
96- $ methodCall ->class ,
152+ $ node instanceof StaticCall && $ this ->isObjectType (
153+ $ node ->class ,
97154 new ObjectType ('Illuminate\Support\Facades\App ' )
98155 ) => true ,
99- $ methodCall instanceof StaticCall && $ this ->isObjectType (
100- $ methodCall ->class ,
156+ $ node instanceof StaticCall && $ this ->isObjectType (
157+ $ node ->class ,
101158 new ObjectType ('App ' )
102159 ) => true ,
103160 default => false ,
104161 };
105162 }
163+
164+ private function getMethodName (MethodCall |StaticCall $ methodCall , string $ environment ): ?string
165+ {
166+ if (
167+ $ methodCall instanceof MethodCall
168+ && ! $ this ->isObjectType ($ methodCall ->var , new ObjectType ('Illuminate\Foundation\Application ' ))
169+ ) {
170+ return null ;
171+ }
172+
173+ return match ($ environment ) {
174+ 'local ' => 'isLocal ' ,
175+ 'production ' => 'isProduction ' ,
176+ default => null ,
177+ };
178+ }
179+
180+ /** @todo remove and use ->getArg() instead when rector is bundled with php-parser 5.6 */
181+ private function getArg (CallLike $ callLike , string $ name , int $ position ): ?Arg
182+ {
183+ if ($ callLike ->isFirstClassCallable ()) {
184+ return null ;
185+ }
186+ foreach ($ callLike ->getArgs () as $ i => $ arg ) {
187+ if ($ arg ->unpack ) {
188+ continue ;
189+ }
190+ if (
191+ ($ arg ->name !== null && $ arg ->name ->toString () === $ name )
192+ || ($ arg ->name === null && $ i === $ position )
193+ ) {
194+ return $ arg ;
195+ }
196+ }
197+
198+ return null ;
199+ }
106200}
0 commit comments