可以 PHP class return 一个 classes 的数组而不调用方法或变量吗?

Can a PHP class return an array of classes without calling methods or variables?

在 PHP 中,我希望能够遍历 class 的集合以帮助设置、插入和验证值。在方法参数中使用 class 作为类型会使代码更严格,这有助于避免错误。

我能够访问该集合,但只能通过 public 数组或方法($values->array$values->get())。 我希望能够直接使用 $values 以获得更清晰的代码。例如,要访问引用,我需要使用 $values->array[0]$values->get()[0] 而不是 $values[0]。 PHP 如何实现?

预期用途:

$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);

类:

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

听起来你真正想要的是类型化数组,但PHP中没有这样的东西。

在很多静态分析工具和 IDE 中记录类型数组的支持,使用“PHPDoc语法”像这样:

/** @param Value[] $values */
function foo(array $values) {}

如果你想要一个可以用foreach循环的对象,最简单的方法是实现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);
}

如果您想要一个可以用 [...] 语法引用的对象,请实现 the ArrayAccess interface。有四种方法,但对于这种情况,每种方法实施起来都很简单,手册中有一个示例。


还有一个 built-in ArrayObject class 实现了这两个接口(以及更多接口),您可以扩展它以一次性获得很多 array-like 行为。


另一方面,如果您只想验证数组是否仅包含特定类型,那么就这样做吧。 one-line 版本为:

$valid = array_reduce($values, fn($valid, $next) => $valid && $next instanceof Value, true);

或者对于大型数组稍微更有效的版本(因为它在发现无效项时完全停止循环):

$valid = true;
foreach ( $values as $next ) {
    if ( ! $next instanceof Value ) {
         $valid = false;
         break;
    }
}