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;
}
}
Given these two classes above, it is possible to use this pattern:
$values = new Values(
new Value('foo', 'bar'),
new Value('foo2', 'bar2'),
);
Which will output
object(Values)#5 (1) {
["array"]=>
array(2) {
[0]=>
object(Value)#6 (2) {
["field"]=>
string(3) "foo"
["value"]=>
string(3) "bar"
}
[1]=>
object(Value)#7 (2) {
["field"]=>
string(4) "foo2"
["value"]=>
string(4) "bar2"
}
}
}
Is there a way to get the value of ["array"]
directly in $values
without having to use $values->array
or adding another function to the class to do something like $values->get()
?
Or is there a better way to handle storing an array of classes for object validation?
Example of expected usage per comments:
function insert(Values $insertvalues): void
{
foreach ($insertvalues as $insertValue) {
//do something with $insertValue->field
//do something with $insertValue->value
}
}
For example, To access the values by reference, these are some of the possibilities I am seeing:
$values->array[0];
$values->get()[0];
//OR
$values = $values->array;
$values = $values->get();
$values[0]
It would be much cleaner to simply call $values[0]
without additional code and $values->get()[0]
is not as easy to read.
An alternative can be to not use a Values
class and define variables like $values = [new Value()]
but then a Values array type can't be added to the insert function as a type.
CodePudding user response:
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;
}
}