Old post but variadic functions and array unpacking can be used (with some limitations) to accomplish typed array hinting, at least with PHP7. (I didn't test on earlier versions).
Example:
class Foo {
public function test(){
echo "foo";
}
};
class Bar extends Foo {
//override parent method
public function test(){
echo "bar";
}
}
function test(Foo ...$params){
foreach($params as $param){
$param->test();
}
}
$f = new Foo();
$b = new Bar();
$arrayOfFoo = [$f,$b];
test(...$arrayOfFoo);
//will output "foobar"
The Limitations:
This isn't technically a solution, as you aren't really passing a typed array. Instead, you use the array unpacking operator1 (the "..." in the function call) to convert your array to a list of parameters, each of which must be of the type hinted in the variadic declaration2 (which also employs an ellipsis).
The "..." in the function call is absolutely necessary (which isn't surprising, given the above). Trying to call
test($arrayOfFoo)
in the context of the above example will yield a type error, as the compiler expects parameter(s) of foo, not an array. See below for an, albeit hacky, solution to pass in an array of a given type directly, while preserving some type-hinting.
Variadic functions may only have one variadic parameter and it must be the last parameter (since otherwise how might the compiler determine where the variadic parameter ends and the next begins) meaning you couldn't declare functions along the lines of
function test(Foo ...$foos, Bar ...$bars){
//...
}
or
function test(Foo ...$foos, Bar $bar){
//...
}
An Only-Slightly-Better-Than-Just-Checking-Each-Element Alternative:
The following procedure is better than just checking the type of each element insofar as (1) it guarantees the parameters used in the functional body are of the correct type without cluttering the function with type checks, and (2) it throws the usual type exceptions.
Consider:
function alt(Array $foos){
return (function(Foo ...$fooParams){
//treat as regular function body
foreach($fooParams as $foo){
$foo->test();
}
})(...$foos);
}
The idea is define and return the result of an immediately invoked closure that takes care of all the variadic / unpacking business for you. (One could extend the principle further, defining a higher order function that generates functions of this structure, reducing boilerplate). In the above example:
alt($arrayOfFoo) // also outputs "foobar"
The issues with this approach include:
(1) Especially to inexperienced developers, it may be unclear.
(2) It may incur some performance overhead.
(3) It, much like just internally checking the array elements, treats the type checking as an implementational detail, insofar as one must inspect the function declaration (or enjoy type exceptions) to realize that only a specifically typed array is a valid parameter. In an interface or abstract function, the full type hint could not be encoded; all one could do is comment that an implementation of the above sort (or something similar) is expected.
Notes
[1]. In a nutshell: array unpacking renders equivalent
example_function($a,$b,$c);
and
example_function(...[$a,$b,$c]);
[2]. In a nutshell: variadic functions of the form
function example_function(Foo ...$bar){
//...
}
can be validly invoked in any of the following ways:
example_function();
example_function(new Foo());
example_function(new Foo(), new Foo());
example_function(new Foo(), new Foo(), new Foo());
//and so on