Type hinting - specify an array of objects

前端 未结 5 1913
不思量自难忘°
不思量自难忘° 2020-12-10 10:23

How can I specify the argument type as an array? Say I have a class named \'Foo\':

class Foo {}

and then I have a function that accepts tha

5条回答
  •  [愿得一人]
    2020-12-10 11:13

    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:

    1. 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).

    2. 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.

    3. 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
    

提交回复
热议问题