Within PHP, I'd like to be able to iterate over a collection of classes to help with settings, inserting, and validating values. Using a class as a type in method args would make the code more strict which would help avoiding bugs.
I am able to access the collection but only through a public array or method ($values->array
or $values->get()
).
I would like to be able to use $values
directly for cleaner code. For example, to access a reference, I'd need to use $values->array[0]
or $values->get()[0]
instead of $values[0]
. How can this be achieved with PHP?
Expected usage:
$values = new Values(
new Value('foo', 'bar'),
new Value('foo2', 'bar2'),
);
function handleValues(Values $exampleValues): void
{
foreach ($exampleValues as $exampleValue) {
//do something with $exampleValue->field, $exampleValue->value
}
}
handleValues($values);
Classes:
class Values
{
public array $array;
public function __construct(Value... $value){
$this->array = $value;
}
}
class Value
{
public string $field;
public mixed $value;
public function __construct(string $field, mixed $value)
{
$this->field = $field;
$this->value = $value;
}
}
It sounds like what you really want is a typed array, but there is no such thing in PHP.
There is support for documenting typed arrays in a lot of static analysis tools and IDEs, using "PHPDoc syntax" like this:
/** @param Value[] $values */
function foo(array $values) {}
If you want an object that can be looped with foreach
, the simplest way is to implement the IteratorAggregate
interface, and use it to wrap the internal array in an ArrayIterator
object:
class Values implements IteratorAggregate
{
private array $array;
public function __construct(Value... $value){
$this->array = $value;
}
public function getIterator(): Iterator {
return new ArrayIterator($this->array);
}
}
$values = new Values(
new Value('foo', 'bar'),
new Value('foo2', 'bar2'),
);
foreach ( $values as $value ) {
var_dump($value);
}
If you want an object that can be referenced into with [...]
syntax, implement the ArrayAccess
interface. There are four methods, but each is trivial to implement for this case, and there's an example in the manual.
There's also a built-in ArrayObject
class that implements both these interfaces (and a few more), which you can extend to get a lot of array-like behaviour in one go.
On the other hand, if all you want is to validate that the array contains only a specific type, then just do that. A one-line version would be:
$valid = array_reduce($values, fn($valid, $next) => $valid && $next instanceof Value, true);
Or a slightly more efficient version for large arrays (because it stops looping completely when it finds an invalid item):
$valid = true;
foreach ( $values as $next ) {
if ( ! $next instanceof Value ) {
$valid = false;
break;
}
}