anon
anon

Reputation:

Function return type hinting for an array of objects in PHP7

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.

Upvotes: 96

Views: 95428

Answers (4)

Mike Doe
Mike Doe

Reputation: 17566

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

Or even generics:

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

Upvotes: 112

safrazik
safrazik

Reputation: 1601

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

Upvotes: 0

Ruslan Osmanov
Ruslan Osmanov

Reputation: 21492

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

Upvotes: 13

Johnny
Johnny

Reputation: 1798

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.

Upvotes: 91

Related Questions