Function return type hinting for an array of objects in PHP7

PhpPhp 7

Php Problem Overview


I am very happy with the new features in PHP 7. But I am confused on how to return an array of objects in PHP 7.

For example, we have a class Item, and we want to return an array of objects of this class from our function:

function getItems() : Item[] {
}

But it does not work this way.

Php Solutions


Solution 1 - Php

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(): iterable

PHPStorm also supports nested arrays:

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

Newer versions of PHPStorm support phpstan/psalm format:

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

Solution 2 - Php

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.

Solution 3 - Php

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

Solution 4 - Php

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRobert LimantoView Question on Stackoverflow
Solution 1 - PhpemixView Answer on Stackoverflow
Solution 2 - PhpJohnnyView Answer on Stackoverflow
Solution 3 - PhpRuslan OsmanovView Answer on Stackoverflow
Solution 4 - PhpsafrazikView Answer on Stackoverflow