- Notifications
You must be signed in to change notification settings - Fork 545
Add DynamicReturnType for call_user_func #219
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
Conversation
ondrejmirtes left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this extension needs to be registered in config.neon
You can test it by adding a new dataProvider to NodeScopeResolverTest::testFileAsserts().
Why it hasn't been implemented yet - because why would you call call_user_func($arg) when you can call $arg() :) So in case of call_user_func people most usually call it arguments that actually make it impossible to infer the return type... But I'd welcome this extension anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This solves only a very narrow usecase when you have callable $cb as a parameter typehint. But callables can also be: arrays, strings, objects. This extension could be implemented as:
$args = $functionCall->args; // TODO check $args isn't empty - to prevent crash on `call_user_func()` $firstArg = array_shift($args); return $scope->getType(new FuncCall($firstArg->value, $args));And all these cases would be taken care of thanks to this logic: https://github.com/phpstan/phpstan-src/blob/master/src/Analyser/MutatingScope.php#L1810-L1821
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is nice, thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These functions are not exactly 1:1. You need to provide different treatment to call_user_func_array. I'd prefer two different extensions for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. The arguments should be shifted. Only 1-to-last should be passed as actual arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where it matters - you might have some generic function called like this, and these arguments matter for correctly resolved type variable. There should be a test around that. See https://phpstan.org/blog/generics-in-php-using-phpdocs if you're not familiar with generics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I think I understand. I have one question about this: what would be proper way to prepare params for call_user_func_array from $args[1]? Should I map it somehow myself or is there some nice API for it :) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to do it only if $args[1]->value instanceof Array_, otherwise I'd return the default return type.
The logic could look like this:
$args = []; foreach ($args[1]->value->items as $item) { $args[] = new Arg($item->value, $item->byRef, $item->unpack); }There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will not work for some edge cases around functions with multiple variants. Best to call ParametersAcceptorSelector::selectFromArgs().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you drop just few words on what's the significance/meaning/purpose of that? Practically what should I pass as $parametersAcceptors? What I am trying to do with that call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some weird PHP functions like strtok have multiple variants, basically function overloading. If there's a function that would have different return types based on which variant gets chosen, then the logic that always selects [0] would be wrong.
$parametersAcceptors is $functionReflection->getVariants()
| @ondrejmirtes I made some improvements. But I am now getting lot of |
| Nope, I don't know. You should debug where the ErrorType is coming from. Also, coding standard and PHPStan will definitely fail on the code :) You can run it locally too. |
| @ondrejmirtes One more specific question :). When I see that first argument is |
| @vojtech-dobes You don't have to care about that. You're delegating the type inference of There's the AST world and the type system world. The type system can know that even a I recommended you to check for the $args = [1, 2, 3]; call_user_func_array('foo', $args); |
| @ondrejmirtes Well, I am currently struggling with normal named functions passed as string. I have following code: $callback = array_shift($args)->value; $params = $args; return $scope->getType(new FuncCall($callback, $params));I am getting I am trying to get inspired by function fooReturnsBool(): bool { return TRUE; }/** * @return bool */ $returnsBoolInPhpDoc2 = function () { return TRUE; };/** * @template T * @param T $a * @return T */ function returnsGeneric($a) { return $a; } assertType('int', call_user_func('returnsGeneric', 1)); assertType('string', call_user_func('returnsGeneric', 'a'));Not sure whether I should expect these to be actually supported :). |
| This works so I'm not sure what's wrong: https://phpstan.org/r/cd789039-8f3a-4bb0-9582-6d509d5b3476 |
| @ondrejmirtes I've added test case for And based on that I wonder, would current stay be enough :). Or should I strive for some more improvements? |
| This would suggest that it doesn't work specifically in tests, is there possible reason for that? https://phpstan.org/r/f92e5ce6-0620-4c98-8ab5-c902e9718e70# |
| I'm not sure, it might be:
|
| Hi, do you plan to finish this PR? :) |
| @ondrejmirtes I would love to, but I haven't really figured out yet where to properly start looking into why there is some issue with named functions and my time budget for this is tight :). |
f54fd5f to eca550a Compare d45166a to 3526237 Compare | Feel free to open a new PR if you decide to work on this again, thanks! |
Honestly, I wonder 2 things :)
I will appreciate a bit of help, but I would like to polish this to highest standard. I would like to also include
call_user_func_array.