heiglandreas,
@heiglandreas@phpc.social avatar

Question for the @phpstan pros:

What am I missing here?

final class stdClassList
{
/** @var array<int, stdClass> */
public readonly array $classes;

public function __construct(stdClass ...$classes)
{
$this->classes = $classes;
}
}

https://phpstan.org/r/5b86ba21-7486-4eb5-bccf-1ebd4327829e

PHPStan reports "Property stdClassList::$classes (array<int, stdClass>) does not accept array<int|string, stdClass>." but I would expect that $classes will always be array<int, stdClass> without any string-keys

Romm,
@Romm@mastodon.social avatar

@heiglandreas @phpstan I’m on my phone right now so can’t test easily but have you tried the no-named-argument annotation?

heiglandreas,
@heiglandreas@phpc.social avatar

@Romm Not yet! Will check! Thanks!

/cc @phpstan

heiglandreas,
@heiglandreas@phpc.social avatar

@Romm That does indeed work in @phpstan!

https://phpstan.org/r/535f8d0c-dd06-4912-834f-75b926e7f0a5

But it's "just" another way of telling phpstan that we do not expect named arguments here.

It doesn't prevent them.

We can still get them and therefore break our code at runtime...

https://3v4l.org/bSMle

Romm,
@Romm@mastodon.social avatar

@heiglandreas @phpstan you can break many things at runtime when not respecting PHPStan annotations 😛
Adding the no-named-arguments annotation will at least prevent explicit calls in your codebase when SA is enabled.

heiglandreas,
@heiglandreas@phpc.social avatar

@Romm That is true.

But the main issue is that one might actually want to use named arguments. But then the variadic contains the "remaining" stuff and that should just be a list.

Apart from that the no-named-arguments annotation declares that the function will not receive named arguments. But that is on the caller to decide.

/cc @phpstan

heiglandreas,
@heiglandreas@phpc.social avatar

@phpstan 🤦‍♂️ Right! Named arguments...

https://3v4l.org/jKiIE

Doesn't make any sense in that case though....

shochdoerfer,
@shochdoerfer@phpc.social avatar

@heiglandreas @phpstan add "/** @var array<int, stdClass> $classes */" before this assignment in the constructor.

heiglandreas,
@heiglandreas@phpc.social avatar
heiglandreas,
@heiglandreas@phpc.social avatar

@shochdoerfer

Oh! Before teh assignement! Yeah. That fixes the issue. But it actually is possible then that the array contains string-keys

I solved it by using an array_values during the assignement. Which is IMO much cleaner as it actually actively removes the string keys.

But should they not be removed in that case automatically by the engine?

@phpstan

bcremer,
@bcremer@phpc.social avatar

@heiglandreas @shochdoerfer @phpstan
The string keys are not removed when called by the spread operator: https://3v4l.org/P3EYE

So your explicit array_values call it the way to go here.

bcremer,
@bcremer@phpc.social avatar

@heiglandreas @shochdoerfer @phpstan
Still the phpdoc of the constructor should be treated as certain and overwrite that during inspection time.

heiglandreas,
@heiglandreas@phpc.social avatar

@bcremer the trouble is that the docs can in that case be wrong.

But I'm with you that the function signature shoud be treated equaly valid as the later var docblock...

/cc @shochdoerfer @phpstan

heiglandreas, (edited )
@heiglandreas@phpc.social avatar

@bcremer @dkreuer was thinking about a #[VariadicList] attribute that could be used to autmatically apply an array_values` to the content of the array before passing it to the function body.

https://3v4l.org/Tbc5p

Would that be something of interest for the engine?

/cc @shochdoerfer @phpstan

shochdoerfer,
@shochdoerfer@phpc.social avatar

@heiglandreas @bcremer @dkreuer @phpstan idk, never had such a requirement before :)

heiglandreas,
@heiglandreas@phpc.social avatar

Actually IMO that should not be necessary and the variadic should ALWAYS be a list. But that would be a BC break...

/cc @bcremer @dkreuer @shochdoerfer @phpstan

shochdoerfer,
@shochdoerfer@phpc.social avatar

@heiglandreas @phpstan I added it before the assignment in the constructor, that seemed to work. But I saw your toot regarding named arguments so that's probably not the ideal solution anyway :P

theseer,
@theseer@phpc.social avatar

@shochdoerfer @heiglandreas @phpstan The problem is the variadic.

If you destruct an associative array when passing the key is not an int.

heiglandreas,
@heiglandreas@phpc.social avatar

@theseer Yeah. But at that point it should not be an associative array any more.

I get that the associative array makes sense for named parameters. But this one is for all the other - non-named - parameters.
/cc @shochdoerfer @phpstan

theseer,
@theseer@phpc.social avatar

@shochdoerfer @heiglandreas @phpstan The variadic doesn't care. All phpstan is telling you that if you do new StdClassList( ...['a' =&gt; new StdClass]); the key "a" is kept in the params array. And does not magically turn into an int. If you'd do $this-&gt;classes = array_values($classes); the problem is gone.

heiglandreas,
@heiglandreas@phpc.social avatar

@theseer Yeah! That's (by now) clear 😁

Nevertheless should PHP itself convert the content of the variadic (after extracting all the named parameters) into a list as anything else doesn'T make sense. When I pass items as function arguments I do not have any string keys.

That has nothing to do with PHPStan at that point.

/cc @shochdoerfer @phpstan

theseer,
@theseer@phpc.social avatar

@heiglandreas @shochdoerfer @phpstan You didn't ask whether or not the behavior of PHP makes sense though ;-p

heiglandreas,
@heiglandreas@phpc.social avatar
gmazzap,
@gmazzap@phpc.social avatar

@heiglandreas PHP can't know if a key you pass without a named param match is because you want to ignore it or it was a mistake, e.g a typo in the key. So it emits an error, and it makes sense.
I blame implementation of named parameters... that was so problematic. Named params should have been opt-in. The person declaring the function should have been able to enable named params at function level. Native functions/methods would have been all opted-in.

@theseer @shochdoerfer @phpstan

heiglandreas,
@heiglandreas@phpc.social avatar

@gmazzap Currently no error is thrown at all. The array is passed as-is.

In this example https://3v4l.org/kogWt the parameter that was used for a named parameter is removed from the variadic array but the array still contains string keys that are no longer of any use here.

Yes: Opt-In via i.e. #[VariadicArray] might have been a better solution.

/cc @theseer @shochdoerfer @phpstan

gmazzap,
@gmazzap@phpc.social avatar

@heiglandreas

Yes, my bad. PhpStan/Palm trigger error, not PHP.

Regarding opt-in, it could have been also a syntax change like:

foo($x, $y) {} // no named arguments allowed

foo:($x, $y) {} // named arguments allowed

Very little verbosity. Easy for IDEs and static analyzers to pick up. Zero BC break.

This could have avoided a lot of headaches. But we have what we have...

@theseer @shochdoerfer @phpstan

heiglandreas,
@heiglandreas@phpc.social avatar

@gmazzap

Yes, an error is thrown when one tries to overwrite an already passed argument via the named parameters. But that is totally unrelated to the Array/List issue after checking for the named arguments.

/cc @theseer @shochdoerfer @phpstan

  • All
  • Subscribed
  • Moderated
  • Favorites
  • random
  • ethstaker
  • thenastyranch
  • GTA5RPClips
  • everett
  • Durango
  • rosin
  • InstantRegret
  • DreamBathrooms
  • magazineikmin
  • Youngstown
  • mdbf
  • slotface
  • cisconetworking
  • kavyap
  • JUstTest
  • normalnudes
  • modclub
  • cubers
  • ngwrru68w68
  • khanakhh
  • tacticalgear
  • tester
  • provamag3
  • Leos
  • osvaldo12
  • anitta
  • megavids
  • lostlight
  • All magazines