我很困惑为什么ArrayIterator
的子类永远不会调用它的__construct
方法。考虑这个例子:
<?php
class ConstructorException extends Exception {}
class Foo extends ArrayObject {
function __construct( $arr = array(), $flags = 0, $iterator = 'ArrayIterator' ) {
$iterator = 'FooIterator';
parent::__construct( $arr, $flags, $iterator );
}
}
class FooIterator extends ArrayIterator {
function __construct( $array = array(), $flags = 0 ) {
throw new ConstructorException( 'HELLO WORLD' ); // I AM NEVER CALLED.
parent::__construct( $array, $flags );
}
}
try {
$f = new Foo( array( 1, 2, 3 ) );
$it = $f->getIterator();
if ( get_class( $it ) !== 'FooIterator' ) {
throw new Exception( 'Expected iterator to be FooIterator.' );
}
die( "FAIL\n" );
} catch ( ConstructorException $e ) {
die( "PASS\n" );
} catch ( \Exception $e ) {
die( sprintf( "ERROR: %s\n", $e->getMessage() ) );
}
在PHP 5.4和5.5中,结果为FAIL
。为什么呢?
答案 0 :(得分:3)
通常,在创建新实例时调用方法 __ construct :
$it = new FooIterator();
所以,需要一些时间,我有一个解决方案:类Foo (ArrayObject的子类)中的覆盖方法getIterator()在下一个示例中:< / p>
class Foo extends ArrayObject {
public function __construct( $arr = array(), $flags = 0, $iterator = 'FooIterator' ) {
parent::__construct( $arr, $flags, $iterator );
}
/**
* @return ArrayIterator
*/
public function getIterator()
{
$class = $this->getIteratorClass();
return new $class($this);
}
}
通过此更正,您的代码将会通过&#39;
。更改了代码的结果:http://3v4l.org/HnFQm
以前的代码没有异常,但显示迭代器在Foo类中更改了方法getIterator()(通过索引和取消设置添加):http://3v4l.org/R8PHr
答案 1 :(得分:2)
@Leggendario是正确的,问题在于spl_array_object_new_ex
方法。但是,如果这是一个我不确定的错误。然而,这并不是真正的标准。
构造函数中的iteratorClass
变量(或通过setIteratorClass
设置)表明只要我们从ArrayObject
检索迭代器,就会实例化此类。但它没有定期“实例化”,因为这是不可能的。
它只会初始化迭代器(分配内存等),但不会调用构造函数。不调用构造函数是有意义的,因为ArrayIterator
的构造函数有两个参数($array
和$flags
),并且您的类可能已更改其签名,甚至可能添加更多(强制性值)。
通常ArrayIterator
(或RecursiveArrayIterator
)是内部类,并且附加了内部结构(基本上就像它自己的内部属性集,你无法直接从PHP用户区获得)。 spl_array_object_new_ex
将直接设置这些内部值(最值得注意的是ce_get_iterator
和ar_flags
)。所以基本上它接管了ArrayIterator构造函数的工作。
因为PHP会检查给定的类是ArrayIterator
,还是其中一个父类是一个。这样,它意味着它最终可以使用这些内部值,从而直接设置它们,绕过对构造函数的任何需求。作为一个缺点,任何你想自己构建的东西都不会被称为。