1414use Efabrica \PHPStanLatte \Compiler \NodeVisitor \Behavior \ScopeNodeVisitorInterface ;
1515use Efabrica \PHPStanLatte \Resolver \NameResolver \NameResolver ;
1616use Efabrica \PHPStanLatte \Template \Variable ;
17+ use Latte \Runtime \Template ;
1718use PhpParser \Comment \Doc ;
1819use PhpParser \Node ;
1920use PhpParser \Node \Arg ;
3435use PhpParser \Node \Stmt \Expression ;
3536use PhpParser \Node \VariadicPlaceholder ;
3637use PhpParser \NodeVisitorAbstract ;
38+ use PHPStan \BetterReflection \BetterReflection ;
39+ use PHPStan \BetterReflection \Reflection \ReflectionFunction as BetterReflectionFunction ;
40+ use PHPStan \BetterReflection \Reflection \ReflectionMethod as BetterReflectionMethod ;
41+ use PHPStan \BetterReflection \Reflection \ReflectionNamedType as BetterReflectionNamedType ;
42+ use PHPStan \BetterReflection \Reflection \ReflectionParameter as BetterReflectionParameter ;
3743use PHPStan \Broker \ClassNotFoundException ;
3844use PHPStan \PhpDoc \TypeStringResolver ;
3945use PHPStan \PhpDocParser \Ast \ConstExpr \ConstExprStringNode ;
4450use PHPStan \Type \ClosureTypeFactory ;
4551use PHPStan \Type \ObjectType ;
4652use PHPStan \Type \ThisType ;
53+ use ReflectionFunction ;
54+ use ReflectionNamedType ;
55+ use ReflectionParameter ;
4756
4857final class ChangeFunctionsNodeVisitor extends NodeVisitorAbstract implements FunctionsNodeVisitorInterface, ExprTypeNodeVisitorInterface, ScopeNodeVisitorInterface
4958{
@@ -237,13 +246,17 @@ private function createFunctionCallNode(string $functionName, array $args): ?Nod
237246 }
238247
239248 if ($ function instanceof Closure || $ this ->isCallableString ($ function )) {
249+ if ($ function instanceof Closure) {
250+ $ args = $ this ->updateArgs (new ReflectionFunction ($ function ), $ args );
251+ }
240252 return new FuncCall (new VariableExpr ($ this ->createFunctionVariableName ($ functionName )), $ args );
241253 }
242254
243255 if (is_string ($ function )) {
244256 if (str_contains ($ function , ':: ' )) {
245257 $ function = explode (':: ' , $ function );
246258 } else {
259+ $ args = $ this ->updateArgs ((new BetterReflection ())->reflector ()->reflectFunction ($ function ), $ args );
247260 return new FuncCall (new FullyQualified ($ function ), $ args );
248261 }
249262 }
@@ -257,19 +270,15 @@ private function createFunctionCallNode(string $functionName, array $args): ?Nod
257270 /** @var non-empty-string $methodName */
258271 $ methodName = $ function [1 ];
259272
260- try {
261- $ classReflection = $ this ->reflectionProvider ->getClass ($ className );
262- } catch (ClassNotFoundException $ e ) {
263- return null ;
264- }
273+ $ reflectionClass = (new BetterReflection ())->reflector ()->reflectClass ($ className );
274+ $ reflectionMethod = $ reflectionClass ->getMethod ($ methodName );
265275
266- try {
267- $ methodReflection = $ classReflection ->getMethod ($ methodName , $ this ->getScope ());
268- } catch (MissingMethodFromReflectionException $ e ) {
276+ if ($ reflectionMethod === null ) {
269277 return null ;
270278 }
271279
272- if ($ methodReflection ->isStatic ()) {
280+ $ args = $ this ->updateArgs ($ reflectionMethod , $ args );
281+ if ($ reflectionMethod ->isStatic ()) {
273282 return new StaticCall (
274283 new FullyQualified ($ className ),
275284 new Identifier ($ methodName ),
@@ -284,4 +293,30 @@ private function createFunctionCallNode(string $functionName, array $args): ?Nod
284293 $ args
285294 );
286295 }
296+
297+ /**
298+ * @param BetterReflectionFunction|BetterReflectionMethod|ReflectionFunction $reflection
299+ * @param Arg[]|VariadicPlaceholder[] $args
300+ * @return Arg[]|VariadicPlaceholder[]
301+ */
302+ private function updateArgs ($ reflection , array $ args ): array
303+ {
304+ $ parameter = $ reflection ->getParameters ()[0 ] ?? null ;
305+ if ($ parameter instanceof BetterReflectionParameter && $ parameter ->getType () instanceof BetterReflectionNamedType) {
306+ $ parameterType = $ parameter ->getType ()->getName ();
307+ } elseif ($ parameter instanceof ReflectionParameter && $ parameter ->getType () instanceof ReflectionNamedType) {
308+ $ parameterType = $ parameter ->getType ()->getName ();
309+ } else {
310+ $ parameterType = null ;
311+ }
312+ if ($ parameterType !== Template::class &&
313+ isset ($ args [0 ]) &&
314+ $ args [0 ] instanceof Arg &&
315+ $ args [0 ]->value instanceof VariableExpr &&
316+ $ args [0 ]->value ->name === 'this '
317+ ) {
318+ $ args = array_slice ($ args , 1 );
319+ }
320+ return $ args ;
321+ }
287322}
0 commit comments