为什么从不调用 ArrayIterator 子类的构造函数?
Why is ArrayIterator subclass's constructor never called?
我很困惑为什么 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
。为什么?
@Leggendario 说得对,问题出在 spl_array_object_new_ex
方法上。但是,如果这是一个错误,我不确定。然而,这里发生的事情并不是真正的标准。
构造函数中的 iteratorClass
变量(或通过 setIteratorClass
设置)表明每当我们从 ArrayObject
中检索迭代器时都会实例化此 class。但它不执行常规 "instantiation",因为这是不可能的。
它只会初始化迭代器(分配内存等),但不会调用构造函数。不调用构造函数是有意义的,因为 ArrayIterator
的构造函数有两个参数($array
和 $flags
),而你的 class 可能已经改变了它的签名,也许甚至添加更多(强制值)。
通常 ArrayIterator
(或 RecursiveArrayIterator
)是内部 classes 并且有一个内部结构附加到它们(基本上就像它自己的内部属性集,你无法获得直接从 PHP userland)。 spl_array_object_new_ex
将直接设置这些内部值(最值得注意的是 ce_get_iterator
和 ar_flags
)。所以基本上它接管了 ArrayIterator 构造函数的工作。
因为 PHP 检查给定的 class 是否是一个 ArrayIterator
,或者父 class 是否是一个。如果是这样,这意味着它最终可以使用这些内部值并因此直接设置它们,绕过对构造函数的任何需要。不利的是,您想要自己构建的任何内容都不会被调用。
方法 __construct 像往常一样在创建新实例时被调用:
$it = new FooIterator();
所以,这需要一些时间,我有一个解决方案:覆盖方法 getIterator() 在你的 class Foo (ArrayObject 的 subclass)在你的下一个例子中:
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);
}
}
通过此更正,您的代码将 'PASS'。
问题代码更改结果:http://3v4l.org/HnFQm
前面的代码无一例外,但显示迭代器在 class Foo 中的方法 getIterator() 的更改下运行良好(按索引添加和取消设置):http://3v4l.org/R8PHr
我很困惑为什么 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
。为什么?
@Leggendario 说得对,问题出在 spl_array_object_new_ex
方法上。但是,如果这是一个错误,我不确定。然而,这里发生的事情并不是真正的标准。
构造函数中的 iteratorClass
变量(或通过 setIteratorClass
设置)表明每当我们从 ArrayObject
中检索迭代器时都会实例化此 class。但它不执行常规 "instantiation",因为这是不可能的。
它只会初始化迭代器(分配内存等),但不会调用构造函数。不调用构造函数是有意义的,因为 ArrayIterator
的构造函数有两个参数($array
和 $flags
),而你的 class 可能已经改变了它的签名,也许甚至添加更多(强制值)。
通常 ArrayIterator
(或 RecursiveArrayIterator
)是内部 classes 并且有一个内部结构附加到它们(基本上就像它自己的内部属性集,你无法获得直接从 PHP userland)。 spl_array_object_new_ex
将直接设置这些内部值(最值得注意的是 ce_get_iterator
和 ar_flags
)。所以基本上它接管了 ArrayIterator 构造函数的工作。
因为 PHP 检查给定的 class 是否是一个 ArrayIterator
,或者父 class 是否是一个。如果是这样,这意味着它最终可以使用这些内部值并因此直接设置它们,绕过对构造函数的任何需要。不利的是,您想要自己构建的任何内容都不会被调用。
方法 __construct 像往常一样在创建新实例时被调用:
$it = new FooIterator();
所以,这需要一些时间,我有一个解决方案:覆盖方法 getIterator() 在你的 class Foo (ArrayObject 的 subclass)在你的下一个例子中:
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);
}
}
通过此更正,您的代码将 'PASS'。
问题代码更改结果:http://3v4l.org/HnFQm
前面的代码无一例外,但显示迭代器在 class Foo 中的方法 getIterator() 的更改下运行良好(按索引添加和取消设置):http://3v4l.org/R8PHr