Menu

Solving PHPStan error “No value type specified in iterable type”

February 2, 2021 · 5 min read

This problem is reported by PHPStan on level 6 when typehinting an array or an iterable object as a parameter type or a return type. It’s there to prevent values of unknown types to be used as a foreach value:

// Function foo() has parameter $items with no value type specified in iterable type array.
function foo(array $items): void
{
	foreach ($items as $item) {
		// type of $item is unknown here, static analysis can't find any bugs
	}
}

How to solve this error depends on what type has been used.

Arrays #

Solving this for arrays is straightforward - PHPStan needs to know what the array consists of. You can tell PHPStan the types of values and also keys in various ways in PHPDoc:

  • Type[]
  • array<Type>
  • array<int, Type>
  • non-empty-array<Type>
  • non-empty-array<int, Type>

An example:

/**
 * @param array<int, Item> $items
 */
function foo(array $items): void
{
	foreach ($items as $item) {
		// $item is Item
	}
}

If you have an array typehint and just want to make this error go away while keeping the possibility of the array holding any type, replace array with mixed[] or array<mixed>.

If your codebase makes use of arrays of various specific shapes passed around functions and methods, PHPStan can check that the values in specified keys have the correct types. This is different from general arrays that mandate that all the keys and values must be of a specific homogeneous type. Array shapes allow each key and value to be different.

  • array{'foo': int, "bar": string}
  • array{0: int, 1?: int} (key 1 is optional in the array)
  • array{int, int} (keys are 0 and 1)
  • array{foo: int, bar: string} (quotes around array keys aren’t necessary)

Iterable objects #

The error is also reported for these cases:

  • The typehint is a class that implements Iterator interface
  • The typehint is a class that implements IteratorAggregate interface
  • The typehint is an interface that extends Traversable interface

In case of Iterator, PHPStan looks at the return typehint of the current() method:

/** @return Item */
public function current()
{
	// ...
}

In case of IteratorAggregate, PHPStan reads the return typehint of the getIterator() method:

/** @return \ArrayIterator<int, Item> */
public function getIterator(): \Traversable
{
	// ...
}

The error can also be solved by using generics with the help of @extends, @implements and @use annotations. All three basic iterable types (Iterator, IteratorAggregate, Traversable) accept two type variables:

  • Iterator<TKey, TValue>
  • IteratorAggregate<TKey, TValue>
  • Traversable<TKey, TValue>

You need to decide whether the iterated value will always going to be the same with the class, or if it’s going to be different depending on the usage. You might have a DogCollection that always iterates over instances of Dog, and you might have a Collection where you want the user to always decide what the Collection consists of.

In case of DogCollection the usage would look like this:

/** @implements \IteratorAggregate<int, Dog> */
class DogCollection implements \IteratorAggregate
{

}

In case of general Collection it would look like this:

/**
 * @template TKey
 * @template TValue
 * @implements \IteratorAggregate<TKey, TValue>
 */
class Collection implements \IteratorAggregate
{

}

Or if the keys are always going to be the same:

/**
 * @template TValue
 * @implements \IteratorAggregate<int, TValue>
 */
class Collection implements \IteratorAggregate
{

}

Making the class generic with @template tags means that the user always needs to specify those types when typehinting the collection:

/** @param Collection<Dog> */
function foo(Collection $items): void
{

}

Read more about these features in the Generics guide.

Third party code #

The above-mentioned ways of solving this problem can’t be usually applied if the class you’re typehinting comes from 3rd party code. Fortunately PHPStan comes with the stub files feature which is designed to override PHPDocs in 3rd party code.


Do you like PHPStan and use it every day? Consider supporting further development of PHPStan on GitHub Sponsors. I’d really appreciate it!

Theme
A
© 2016–2024 Ondřej Mirtes