Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

False positives in the function forward_static_call_array #10461

Closed
jorgsowa opened this issue Dec 7, 2023 · 10 comments · Fixed by #10805
Closed

False positives in the function forward_static_call_array #10461

jorgsowa opened this issue Dec 7, 2023 · 10 comments · Fixed by #10805

Comments

@jorgsowa
Copy link
Contributor

jorgsowa commented Dec 7, 2023

Hello,
The following code produces two false positives

<?php

/** @return class-string */
function getClassName(object $class): string {
    return $class::class;
};

function fromArrays(array $data, object $object)
{
    $className = getClassName($object);

    return forward_static_call_array([$className, 'from'], $data);
}

https://psalm.dev/r/ecc7244555

We know that the variable passed to forward_static_call_array is a class string, so it should be treated as a callable.

ERROR: [InvalidArgument](https://psalm.dev/004) - 12:38 - Argument 1 of forward_static_call_array expects callable, but list{class-string, 'from'} provided
ERROR: [ArgumentTypeCoercion](https://psalm.dev/193) - 12:60 - Argument 2 of forward_static_call_array expects list<mixed>, but parent type array<array-key, mixed> provided
Copy link

I found these snippets:

https://psalm.dev/r/ecc7244555
<?php

/** @return class-string */
function getClassName(object $class): string {
    return $class::class;
};

function fromArrays(array $data, object $object)
{
    $className = getClassName($object);

    return forward_static_call_array([$className, 'from'], $data);
}
Psalm output (using commit 0e43c44):

ERROR: InvalidArgument - 12:38 - Argument 1 of forward_static_call_array expects callable, but list{class-string, 'from'} provided

ERROR: ArgumentTypeCoercion - 12:60 - Argument 2 of forward_static_call_array expects list<mixed>, but parent type array<array-key, mixed> provided

INFO: MissingReturnType - 8:10 - Method fromArrays does not have a return type

@jorgsowa jorgsowa changed the title False positives on the function forward_static_call_array False positives in the function forward_static_call_array Dec 8, 2023
@weirdan
Copy link
Collaborator

weirdan commented Mar 9, 2024

We know that the variable passed to forward_static_call_array is a class string, so it should be treated as a callable.

we don't know if there's a method named from() on that class (because we don't know the class name in the first place), so we can't do that: https://3v4l.org/pb3Rr

The latter issue is a useful safeguard as lists work for every PHP version, but string-keyed arrays need to match the signature (and Psalm doesn't seem to be able to infer it): https://3v4l.org/LvpJt

@jorgsowa
Copy link
Contributor Author

That's true, but if we pass type list{class-string, string} to function forward_static_call_array we shouldn't have any warning, because this construct creates a callable which is expected. The code is valid and in this case, the only workaround is to add the warning to baseline.

@weirdan
Copy link
Collaborator

weirdan commented Mar 10, 2024

because this construct creates a callable which is expected

That's the thing with callables - they are only callable if they can be called. Otherwise, they are just arrays / strings.

With that said, this should work (but doesn't): https://psalm.dev/r/a1bdbc0e6b

Copy link

I found these snippets:

https://psalm.dev/r/a1bdbc0e6b
<?php

/** @return class-string */
function getClassName(object $class): string {
    return $class::class;
};

/** @param list $data */
function fromArrays(array $data, object $object): mixed
{
    $className = getClassName($object);
    $callback = [$className, 'from'];
    if (!is_callable($callback)) {
		throw new RuntimeException('bad callable');
    }
    /** @psalm-trace $callback */;

    return forward_static_call_array($callback, $data);
}
Psalm output (using commit ef3b018):

INFO: Trace - 16:34 - $callback: callable-array{class-string, 'from'}

ERROR: InvalidArgument - 18:38 - Argument 1 of forward_static_call_array expects callable, but callable-array{class-string, 'from'} provided

@kkmuffme
Copy link
Contributor

Isn't this code not invalid in the first place anyway, since forward_static_call_array can only be used inside a class and the code example produces a fatal? It should report an error for that.

With that said, this should work (but doesn't): https://psalm.dev/r/a1bdbc0e6b

This also produces a fatal error when run in PHP, doesn't it?

Copy link

I found these snippets:

https://psalm.dev/r/a1bdbc0e6b
<?php

/** @return class-string */
function getClassName(object $class): string {
    return $class::class;
};

/** @param list $data */
function fromArrays(array $data, object $object): mixed
{
    $className = getClassName($object);
    $callback = [$className, 'from'];
    if (!is_callable($callback)) {
		throw new RuntimeException('bad callable');
    }
    /** @psalm-trace $callback */;

    return forward_static_call_array($callback, $data);
}
Psalm output (using commit ef3b018):

INFO: Trace - 16:34 - $callback: callable-array{class-string, 'from'}

ERROR: InvalidArgument - 18:38 - Argument 1 of forward_static_call_array expects callable, but callable-array{class-string, 'from'} provided

@weirdan
Copy link
Collaborator

weirdan commented Mar 18, 2024

This also produces a fatal error when run in PHP, doesn't it?

No, it works just fine: https://3v4l.org/lTQWQ

@kkmuffme
Copy link
Contributor

Ah, I mixed it up with forward_static_call - that would produce a fatal. Weird thing is the PHP docs for both
https://www.php.net/manual/en/function.forward-static-call.php
https://www.php.net/manual/en/function.forward-static-call-array.php

state:

This function must be called within a method context, it can't be used outside a class.

But apparently forward_static_call_array also works outside of a class context as your example above shows?

@weirdan
Copy link
Collaborator

weirdan commented Mar 19, 2024

But apparently forward_static_call_array also works outside of a class context as your example above shows?

🤷‍♂️ I was confused by that passage in the docs too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants