I'm baffled why a subclass to ArrayIterator
is never getting its __construct
method called. Consider this example:
<?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() ) );
}
In both PHP 5.4 an 5.5, the result is FAIL
. Why?
Method __construct was called, as usually, on creating new instance:
$it = new FooIterator();
So, it take some time and I have a solution: override method getIterator() in your class Foo (subclass of ArrayObject) in your example to next:
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);
}
}
With this correction your code will 'PASS'.
Result of changed code from question: http://3v4l.org/HnFQm
Previous code without exception, but showing that iterator work well with changes of method getIterator() in class Foo (adding by index and unsetting): http://3v4l.org/R8PHr
@Leggendario is right to say the problem lies in the spl_array_object_new_ex
method. However, if it's a bug i'm not sure. It is, however, not really standard what is going on here.
The iteratorClass
variable from the constructor (or set through setIteratorClass
) would suggest that this class gets instantiated whenever we retrieve the iterator from the ArrayObject
. But it does not do regular "instantiation", as this is not possible.
It will just initialize the iterator (allocate memory etc), but not call the constructor. It makes sense to not call the constructor, as the constructor of an ArrayIterator
takes two arguments ($array
and $flags
), and your class might have changed its signature, maybe even adding more (mandatory values).
Normally the ArrayIterator
(or RecursiveArrayIterator
), are internal classes and have a internal structure attached to them (basically like its own internal set of properties which you cannot get to directly from PHP userland). The spl_array_object_new_ex
will set these internal values directly (most notably, the ce_get_iterator
and ar_flags
). So basically it takes over the work of the ArrayIterator constructor.
Because PHP checks if the given class is an ArrayIterator
, or if one of the parent classes is one. When so, it means that it ultimately it can use these internal values and thus sets them directly, bypassing any need for a constructor. As a downside, anything that you would like to construct yourself, will not be called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With