php 类型提示 - 指定一个对象数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20763744/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 03:31:30  来源:igfitidea点击:

Type hinting - specify an array of objects

phparraystype-hinting

提问by Yoav Kadosh

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

如何将参数类型指定为数组?假设我有一个名为“Foo”的类:

class Foo {}

and then I have a function that accepts that class type as an argument:

然后我有一个接受该类类型作为参数的函数:

function getFoo(Foo $f) {}

When I pass in an array of 'Foo's I get an error, saying:

当我传入一个 'Foo's 数组时,我收到一个错误,说:

Catchable fatal error: Argument 1 passed to getFoo() must be an instance of Foo, array given

可捕获的致命错误:传递给 getFoo() 的参数 1 必须是 Foo 的实例,给定数组

Is there a way to overcome this issue? maybe something like

有没有办法克服这个问题?也许像

function getFoo(Foo $f[]) {}

回答by bishop

If you want to ensure you are working with "Array of Foo" and you want to ensure methods receive "Array of Foo", you can:

如果您想确保您正在使用“Foo 数组”并且您想确保方法接收“Foo 数组”,您可以:

class ArrayOfFoo extends \ArrayObject {
    public function offsetSet($key, $val) {
        if ($val instanceof Foo) {
            return parent::offsetSet($key, $val);
        }
        throw new \InvalidArgumentException('Value must be a Foo');
    }
}

then:

然后:

function workWithFoo(ArrayOfFoo $foos) {
    foreach ($foos as $foo) {
        // etc.
    }
}

$foos = new ArrayOfFoos();
$foos[] = new Foo();
workWithFoo($foos);

The secret sauce is that you're defining a new "type" of "array of foo", then passing that "type" around using type hinting protection.

秘诀在于您正在定义“foo 数组”的新“类型”,然后使用类型提示保护传递该“类型”。



The Haldayne libraryhandles the boilerplate for membership requirement checks if you don't want to roll your own:

Haldayne库处理样板入会要求检查,如果你不希望推出自己的:

class ArrayOfFoo extends \Haldayne\Boost\MapOfObjects {
    protected function allowed($value) { return $value instanceof Foo; }
}

(Full-disclosure, I'm the author of Haldayne.)

(完全披露,我是 Haldayne 的作者。)



Historical note: the Array Of RFCproposed this feature back in 2014. The RFC was declined with 4 yay and 16 nay. The concept recently reappeared on the internals list, but the complaints have been much the same as levied against the original RFC: adding this check would significantly affect performance.

历史记录:RFC 阵列早在 2014 年就提出了此功能。RFC 以 4 yay 和 16 nay 被拒绝。这个概念最近重新出现在内部列表中,但抱怨与对原始 RFC 征收的投诉大致相同:添加此检查将显着影响性能

回答by Evan

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

旧帖子但可变参数函数和数组解包可用于(有一些限制)来完成类型化数组提示,至少在 PHP7 中。(我没有在早期版本上测试)。

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 sometype-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){
        //...
    }
    
  1. 这在技术上不是解决方案,因为您并没有真正传递类型化数组。相反,您使用数组解包运算符1(函数调用中的“...”)将数组转换为参数列表,每个参数都必须是可变参数声明2 中暗示的类型(它也使用省略)。

  2. 函数调用中的“...”是绝对必要的(鉴于上述情况,这并不奇怪)。试图打电话

    test($arrayOfFoo)
    

    在上述示例的上下文中,将产生类型错误,因为编译器需要 foo 的参数,而不是数组。请参阅下面的解决方案,尽管它很笨拙,但可以直接传入给定类型的数组,同时保留一些类型提示。

  3. 可变参数函数可能只有一个可变参数,并且它必须是最后一个参数(否则编译器如何确定可变参数的结束位置和下一个参数的开始位置),这意味着您不能按照以下方式声明函数

    function test(Foo ...$foos, Bar ...$bars){ 
        //...
    }
    

    或者

    function test(Foo ...$foos, Bar $bar){
        //...
    }
    


An Only-Slightly-Better-Than-Just-Checking-Each-Element Alternative:


一个比只检查每个元素略好的替代方案:

The following procedure isbetter 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.

下面的过程不是仅仅检查每个元素的类型,只要(1)它保证在功能体所使用的参数较好是正确的类型而不会扰乱与类型检查的功能,和(2)它抛出的通常类型的例外.

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.

(1) 特别是对于没有经验的开发者来说,可能不清楚。

(2) It may incur some performance overhead.

(2) 它可能会产生一些性能开销。

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

(3) 它就像只是在内部检查数组元素一样,将类型检查视为实现细节,因为必须检查函数声明(或享受类型异常)才能意识到只有特定类型的数组才是有效参数。在接口或抽象函数中,无法对完整类型提示进行编码;人们所能做的就是评论上述类型(或类似的东西)的实现是预期的。


Notes


笔记

[1]. In a nutshell: array unpacking renders equivalent

[1]。简而言之:数组解包呈现等效

example_function($a,$b,$c);

and

example_function(...[$a,$b,$c]);


[2]. In a nutshell: variadic functions of the form


[2]。简而言之:形式的可变参数函数

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

回答by Rolf

Sorry, PHP does not work this way. It has been made for quick'n'easy programming, and so you are not bothered with strict typing, which leaves you in dynamic type hell without any help (like a type inferencing compiler). The PHP interpreter is completely clueless about what you have put into your array, so it must iterate over all array entries if it wanted to validate something like the following (which is not working in PHP, of course):

抱歉,PHP 不能以这种方式工作。它是为快速简单的编程而设计的,因此您不必为严格的类型而烦恼,这会让您在没有任何帮助的情况下陷入动态类型的地狱(如类型推断编译器)。PHP 解释器对您放入数组的内容一无所知,因此如果它想验证以下内容(当然,这在 PHP 中不起作用),它必须遍历所有数组条目:

function bar(Foo[] $myFoos)

This is a major impact on performance when the array gets large. I think that's the reason why PHP doesn't offer typed array hints.

当阵列变大时,这会对性能产生重大影响。我认为这就是 PHP 不提供类型化数组提示的原因。

The other answers here suggest that you create your strongly typed array wrapper. Wrappers are fine when you have a compiler with generic typing like Java or C#, but for PHP, I disagree. Here, those wrappers are tedious boilerplate code and you need to create one for every typed array. If you want to use array functions from the library, you need to extend your wrappers with type checking delegation functions and bloat your code. This can be done in an academic sample, but in a production system with many classes and collections, where developer time is costly and the MIPS in the web cluster are scarce? I think not.

此处的其他答案建议您创建强类型数组包装器。当你有一个像 Java 或 C# 这样的通用类型的编译器时,包装器很好,但对于 PHP,我不同意。在这里,这些包装器是乏味的样板代码,您需要为每个类型化数组创建一个。如果您想使用库中的数组函数,您需要使用类型检查委托函数来扩展您的包装器并膨胀您的代码。这可以在学术样本中完成,但在具有许多类和集合的生产系统中,开发人员的时间成本很高,而且 Web 集群中的 MIPS 稀缺?我想不是。

So, just to have PHP type validation in the function signature, I would refrain from strongly typed array wrappers. I don't believe that the give you enough of a ROI. The PHPDoc support of good PHP IDEs help you more, and in PHPDoc Tags, the Foo[] notation works.

因此,只是为了在函数签名中进行 PHP 类型验证,我会避免使用强类型数组包装器。我不相信它能给你足够的投资回报率。PHPDoc 对优秀 PHP IDE 的支持可以为您提供更多帮助,在 PHPDoc 标签中,Foo[] 表示法有效。

On the other hand, wrappers CAN make sense if you can concentrate some good business logic into them.

另一方面,如果您可以将一些良好的业务逻辑集中到包装器中,则包装器可以有意义。

Perhaps the day will come when PHP is extended with strongly typed arrays (or more precise: strongly typed dictionaries). I would like that. And with those, they can provide signature hints that don't punish you.

也许有一天,PHP 会被强类型数组(或更准确地说:强类型字典)扩展。我愿意。有了这些,他们可以提供不会惩罚你的签名提示。

回答by Hayden

function getFoo()

Generally, you would then have an add method that would typehint to Foo

通常,您将有一个 add 方法,该方法将 typehint Foo

function addFoo( Foo $f )

So, the getter would return an array of Foo's and the add method can ensure you only had Foo's to the array.

因此,getter 将返回一个 Foo 的数组,而 add 方法可以确保您只有 Foo 到该数组中。

EDITRemoved the argument from the getter. I don't know what I was thinking, you don't need an argument in a getter.

编辑从 getter 中删除了参数。我不知道我在想什么,你不需要在吸气剂中争论。

EDITjust to display the a full class example:

编辑只是为了显示一个完整的类示例:

class FooBar
{
    /**
     * @return array
     */
    private $foo;

    public function getFoo()
    {
        return $foo;
    }

    public function setFoo( array $f )
    {
        $this->foo = $f;

        return $this;
    }

    public function addFoo( Foo $f )
    {
        $this->foo[] = $f;

        return $this;
    }
}

You generally, and probably shouldn't, have the setter method since you have the add method to help ensure $foois an array of Foo's but it helps illustrate what is going on in the class.

您通常并且可能不应该拥有 setter 方法,因为您拥有 add 方法来帮助确保's$foo的数组,Foo但它有助于说明类中发生的事情。

回答by Shami

class Foo {
    /**
     * @param Foo[] $f
     */
    function getFoo($f) {

    }
}