1111use PhpParser \Node \Expr \BinaryOp \NotIdentical ;
1212use PhpParser \Node \Expr \ConstFetch ;
1313use PhpParser \Node \Expr \FuncCall ;
14+ use PhpParser \Node \Identifier ;
1415use PhpParser \Node \Name ;
16+ use PHPStan \Analyser \Scope ;
17+ use PHPStan \Reflection \Native \NativeFunctionReflection ;
18+ use Rector \NodeAnalyzer \ArgsAnalyzer ;
1519use Rector \NodeManipulator \BinaryOpManipulator ;
20+ use Rector \NodeTypeResolver \Node \AttributeKey ;
21+ use Rector \NodeTypeResolver \PHPStan \ParametersAcceptorSelectorVariantsWrapper ;
1622use Rector \Php71 \ValueObject \TwoNodeMatch ;
1723use Rector \PhpParser \Node \Value \ValueResolver ;
1824use Rector \Rector \AbstractRector ;
25+ use Rector \Reflection \ReflectionResolver ;
1926use Rector \ValueObject \PhpVersionFeature ;
2027use Rector \ValueObject \PolyfillPackage ;
2128use Rector \VersionBonding \Contract \MinPhpVersionInterface ;
@@ -34,6 +41,8 @@ final class JsonValidateRector extends AbstractRector implements MinPhpVersionIn
3441
3542 public function __construct (
3643 private readonly BinaryOpManipulator $ binaryOpManipulator ,
44+ private readonly ReflectionResolver $ reflectionResolver ,
45+ private readonly ArgsAnalyzer $ argsAnalyzer ,
3746 private ValueResolver $ valueResolver ,
3847 ) {
3948 }
@@ -87,13 +96,21 @@ public function refactor(Node $node): ?Node
8796 return null ;
8897 }
8998
99+ $ scope = $ node ->getAttribute (AttributeKey::SCOPE );
100+ if (! $ scope instanceof Scope) {
101+ return null ;
102+ }
103+
90104 $ args = $ funcCall ->getArgs ();
105+ $ positions = $ this ->argsAnalyzer ->hasNamedArg ($ args )
106+ ? $ this ->resolveNamedPositions ($ args )
107+ : $ this ->resolveOriginalPositions ($ funcCall , $ scope );
91108
92- if (count ( $ args ) < 1 ) {
109+ if ($ positions === []) {
93110 return null ;
94111 }
95112
96- if (! $ this ->validateArgs ($ funcCall )) {
113+ if (! $ this ->validateArgs ($ args , $ positions )) {
97114 return null ;
98115 }
99116 $ funcCall ->name = new Name ('json_validate ' );
@@ -147,25 +164,81 @@ public function matchJsonValidateArg(BooleanAnd $booleanAnd): ?FuncCall
147164 return $ funcCall ;
148165 }
149166
150- protected function validateArgs (FuncCall $ funcCall ): bool
167+ /**
168+ * @param Arg[] $args
169+ * @param int[]|string[] $positions
170+ */
171+ protected function validateArgs (array $ args , array $ positions ): bool
172+ {
173+ foreach ($ positions as $ position ) {
174+ $ arg = $ args [$ position ] ?? '' ;
175+ if ($ arg instanceof Arg && $ arg ->name instanceof Identifier && $ arg ->name ->toString () === 'flags ' ) {
176+ $ flags = $ this ->valueResolver ->getValue ($ arg );
177+ if ($ flags !== JSON_INVALID_UTF8_IGNORE ) {
178+ return false ;
179+ }
180+ }
181+ if ($ arg instanceof Arg && $ arg ->name instanceof Identifier && $ arg ->name ->toString () === 'depth ' ) {
182+ $ depth = $ this ->valueResolver ->getValue ($ arg );
183+ if ($ depth <= 0 ) {
184+ return false ;
185+ }
186+ if ($ depth > self ::JSON_MAX_DEPTH ) {
187+ return false ;
188+ }
189+ }
190+ }
191+
192+ return true ;
193+ }
194+
195+ /**
196+ * @param Arg[] $args
197+ * @return int[]|string[]
198+ */
199+ private function resolveNamedPositions (array $ args ): array
151200 {
152- $ depth = $ funcCall ->getArg ('depth ' , 2 );
153- $ flags = $ funcCall ->getArg ('flags ' , 3 );
201+ $ positions = [];
154202
155- if ($ flags instanceof Arg) {
156- $ flagsValue = $ this ->valueResolver ->getValue ($ flags );
157- if ($ flagsValue !== JSON_INVALID_UTF8_IGNORE ) {
158- return false ;
203+ foreach ($ args as $ position => $ arg ) {
204+ if (! $ arg ->name instanceof Identifier) {
205+ continue ;
159206 }
207+
208+ if (! $ this ->isNames ($ arg ->name , self ::ARG_NAMES )) {
209+ continue ;
210+ }
211+
212+ $ positions [] = $ position ;
160213 }
161214
162- if ($ depth instanceof Arg) {
163- $ depthValue = $ this ->valueResolver ->getValue ($ depth );
164- if ($ depthValue <= 0 || $ depthValue > self ::JSON_MAX_DEPTH ) {
165- return false ;
215+ return $ positions ;
216+ }
217+
218+ /**
219+ * @return int[]|string[]
220+ */
221+ private function resolveOriginalPositions (FuncCall $ funcCall , Scope $ scope ): array
222+ {
223+ $ functionReflection = $ this ->reflectionResolver ->resolveFunctionLikeReflectionFromCall ($ funcCall );
224+ if (! $ functionReflection instanceof NativeFunctionReflection) {
225+ return [];
226+ }
227+
228+ $ parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select (
229+ $ functionReflection ,
230+ $ funcCall ,
231+ $ scope
232+ );
233+
234+ $ positions = [];
235+
236+ foreach ($ parametersAcceptor ->getParameters () as $ position => $ parameterReflection ) {
237+ if (in_array ($ parameterReflection ->getName (), self ::ARG_NAMES , true )) {
238+ $ positions [] = $ position ;
166239 }
167240 }
168241
169- return true ;
242+ return $ positions ;
170243 }
171- }
244+ }
0 commit comments