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

我对 PHP7中的新特性非常满意。但是我对如何在 PHP7中返回对象数组感到困惑。

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

function getItems() : Item[] {
}

但事实并非如此。

68662 次浏览

I actually understand what you mean, but the answer unfortunately is that you can't do that. PHP7 lacks that kind of expressivity, so you can either declare your function to return "array" (a generic array) or you have to create a new class ItemArray which is an array of Item (but that meaning you will have to code it yourself).

There is currently no way to express "I want an array of Item" instances.

EDIT: As an added reference, here the "array of" RFC of what you wanted to do, it has been declined due to various reasons.

The current version of PHP doesn't support a built-in type hinting for an array of objects, as there is no such data type as "an array of objects". A class name can be interpreted as a type in certain contexts, as well as array, but not both at a time.

Actually you can implement such kind of strict type hinting by creating a class based on the ArrayAccess interface, e.g.:

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());

Output

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

You can type hint this way using docblocks.

PHP editor (IDE) like PhpStorm supports this very well and will properly resolve the class when iterating over such array.

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

PHPStorm also supports nested arrays:

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

Newer versions of PHPStorm support phpstan/psalm format:

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

It is not possible at the moment. But you can achieve your intended behavior with a custom array class


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');
}
}

Thanks to bishop's answer here