为什么从未调用过ArrayIterator子类的构造函数?

时间:2015-05-22 23:47:53

标签: php iterator

我很困惑为什么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。为什么呢?

2 个答案:

答案 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_iteratorar_flags)。所以基本上它接管了ArrayIterator构造函数的工作。

因为PHP会检查给定的类是ArrayIterator,还是其中一个父类是一个。这样,它意味着它最终可以使用这些内部值,从而直接设置它们,绕过对构造函数的任何需求。作为一个缺点,任何你想自己构建的东西都不会被称为。