Home > front end >  Can a PHP class return an array of classes without calling methods or variables?
Can a PHP class return an array of classes without calling methods or variables?

Time:02-23

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;
    }
}
  • Related