Yeah I can imagine it getting different notations because PHP doesn't have an official standard - it is a pain, such is life in PHP-land ^^
For what it's worth, for my specific use case I don't need a general solution for all cases -- I would be happy with adding one more magic edge-case onto the pile of magic edge-cases, and I think it's an edge-case which makes some things better while not having any negative side effects:
- if the type of
...$a is a union, then don't unwrap arrays inside that union (or alternatively "only unwrap arrays if the type-hint is exactly array<...>")
eg, @param array<T> $a is equivalent to function foo(T ...$a) {} (unwrapping the array, as you do now - based on the guess of "the user is trying to type-hint $a within the body of the function, instead of type-hinting the parameter")
but @param array<T>|U $a is equivalent to function foo(array|U ...$a) {} (not unwrapping the array, because it doesn't make sense to unwrap something which is inside a union -- the assumption of "the user is probably trying to type-hint what $a looks like inside the function" doesn't apply here, because $a inside the function isn't array<T|U>, it is array<array<T>|U>)
(I hope that made sense ^^)