PHP7中针对对象数组的函数返回类型提示

93

我对PHP 7的新特性感到非常高兴。但是我对如何在PHP 7中返回对象数组感到困惑。

例如,我们有一个类Item,我们想从函数中返回该类的对象数组:

function getItems() : Item[] {
}

但事实并非如此。

4个回答

106
您可以使用文档块这种方式进行类型提示。
PhpStorm这样的PHP编辑器(IDE)非常好地支持此功能,并且在遍历这种数组时将正确解析类。
/**
 * @return YourClass[]
 */
public function getObjects(): array

PHPStorm 同时支持嵌套数组:

/**
 * @return YourClass[][]
 */
public function getObjects(): array

PHPStorm 的新版本支持 phpstan/psalm 格式:

/**
 * @return array<int, YourObject>
 */
public function getObjects(): array

甚至可以使用泛型:

/**
 * @template T of object
 * @param class-string<T> $className
 * @return array<array-key, T>
 */
public function getCollectionOf(string $className): array

2
是的,也被PHPactor支持。 - javier_domenech
3
也适用于 NetBeans。 - jor
4
知悉,此方法不适用于使用 VSCode + Intelliphense 扩展的情况。 - Vladimir Hidalgo
8
我只会使用 PhpStorm。 - Mike Doe
1
@VladimirHidalgo 我正在使用VSCode和Intelephense,效果非常好。 - ptyskju
3
虽然我为你的“使用phpdocs”点了赞,但这与“泛型”无关。因为泛型的主要原则是“类型将在稍后指定,现在请使用通用符号代替真实类型”。而在此表达的愿望中,编写代码时已知类型。 - Reloecc

89

我实际上理解你的意思,但不幸的是,你不能这样做。PHP7缺乏这种表达能力,所以你可以声明函数返回“array”(一般数组),或者你必须创建一个新类ItemArray,它是一个Item数组(但这意味着你需要自己编写代码)。

目前没有办法表达“我想要一个Item数组”实例。

编辑:作为一个额外的参考,这里是你想要做的"array of" RFC的链接,由于各种原因已经被拒绝。


20
我认为转换到 PHP 7 可以帮助我编写严格类型的代码 :/ - Robert Limanto
1
我知道,这很令人失望 :( 我添加了一个关于你所询问的功能的RFC参考文献。但是它被拒绝了。 - Johnny
1
@Johnny,我不理解PHP的RFC投票方法,他们为什么不经过第二次审视就驳回一些东西,这种直接投票的系统不适合这个。 - Jonathan DS
2
目前我们只能使用实现ArrayAccess和Iterator接口的自定义类以及一些自定义输入验证。此外,RFC是2014年的,应该重新审视。这将是迈向类型化数组的又一步,就像Java中的String[]、int[]一样。 - user4494077
3
那个RFC也非常古老,是为php5.6提出的。我认为现在我们都朝着严格类型化的方向前进,他们应该重新考虑它,因为它比最初提出时更有意义。 - Brad

13

当前版本的PHP不支持对“对象数组”进行内置类型提示,因为没有“对象数组”这样的数据类型。在某些情况下,类名可以被解释为类型,以及array,但不能同时使用。

实际上,你可以通过创建一个基于ArrayAccess接口的类来实现这种严格的类型提示,例如:

class Item
{
    protected $value;

    public function __construct($value)
    {
        $this->value = $value;
    }
}

class ItemsArray implements ArrayAccess
{
    private $container = [];

    public function offsetSet($offset, $value)
    {
        if (!$value instanceof Item) {
            throw new Exception('value must be an instance of Item');
        }

        if (is_null($offset)) {
            $this->container[] = $value;
        } else {
            $this->container[$offset] = $value;
        }
    }

    public function offsetExists($offset)
    {
        return isset($this->container[$offset]);
    }

    public function offsetUnset($offset)
    {
        unset($this->container[$offset]);
    }

    public function offsetGet($offset)
    {
        return isset($this->container[$offset]) ? $this->container[$offset] : null;
    }
}


function getItems() : ItemsArray
{
    $items = new ItemsArray();
    $items[0] = new Item(0);
    $items[1] = new Item(2);
    return $items;
}

var_dump((array)getItems());

输出

array(2) {
  ["ItemsArrayitems"]=>
  array(0) {
  }
  ["container"]=>
  array(2) {
    [0]=>
    object(Item)#2 (1) {
      ["value":protected]=>
      int(0)
    }
    [1]=>
    object(Item)#3 (1) {
      ["value":protected]=>
      int(2)
    }
  }
}

1
这是一个不错的方法,但我更喜欢自动化的方式,希望他们能在不久的将来实现这个功能,谢谢 :) - Robert Limanto
谢谢。我注意到一件事。应该是private $container = [];而不是private $items = [];吗? - kta
@kta,你是正确的。我已经编辑了答案。谢谢。 - Ruslan Osmanov

1

目前还不可能。但是您可以通过自定义数组类来实现所需的行为。


function getItems() : ItemArray {
  $items = new ItemArray();
  $items[] = new Item();
  return $items;
}

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

感谢这里的Bishop的回答


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接