not called.
#Merge to 5.3 pending (slight BC break on AppendIterator, as it's no
#longer possible to defer the call to the parent constructor until
#after the constructor is performed).
#Bugs fixed in an atypical way for SPL. The parent constructor call
#check is performed at construction time by using a wrapper constructor
#instead of a check on the beginning of each instance method.
#Perhaps this should be uniformized in trunk; this method was mainly
#applied only to the ones crashing, except a few iterators (at least
#AppendIterator and RecursiveIteratorIterator).
#define SPL_DEFAULT_FILE_EXTENSIONS ".inc,.php"
+static void construction_wrapper(INTERNAL_FUNCTION_PARAMETERS);
+
/* {{{ PHP_GINIT_FUNCTION
*/
static PHP_GINIT_FUNCTION(spl)
{
+ zend_function *cwf = &spl_globals->constr_wrapper_fun;
spl_globals->autoload_extensions = NULL;
spl_globals->autoload_extensions_len = 0;
spl_globals->autoload_functions = NULL;
spl_globals->autoload_running = 0;
+ spl_globals->validating_fun = NULL;
+
+ cwf->type = ZEND_INTERNAL_FUNCTION;
+ cwf->common.function_name = "internal_construction_wrapper";
+ cwf->common.scope = NULL; /* to be filled */
+ cwf->common.fn_flags = ZEND_ACC_PRIVATE;
+ cwf->common.prototype = NULL;
+ cwf->common.num_args = 0; /* not necessarily true but not enforced */
+ cwf->common.required_num_args = 0;
+ cwf->common.arg_info = NULL;
+
+ cwf->internal_function.handler = construction_wrapper;
+ cwf->internal_function.module = &spl_module_entry;
}
/* }}} */
return ZEND_HASH_APPLY_KEEP;
} /* }}} */
+zend_function *php_spl_get_constructor_helper(zval *object, int (*validating_fun)(void *object_data TSRMLS_DC) TSRMLS_DC) /* {{{ */
+{
+ if (Z_OBJCE_P(object)->type == ZEND_INTERNAL_CLASS) {
+ return std_object_handlers.get_constructor(object TSRMLS_CC);
+ } else {
+ SPL_G(validating_fun) = validating_fun;
+ SPL_G(constr_wrapper_fun).common.scope = Z_OBJCE_P(object);
+ return &SPL_G(constr_wrapper_fun);
+ }
+}
+/* }}} */
+
+static void construction_wrapper(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
+{
+ zval *this = getThis();
+ void *object_data;
+ zend_class_entry *this_ce;
+ zend_function *zf;
+ zend_fcall_info fci = {0};
+ zend_fcall_info_cache fci_cache = {0};
+ zval *retval_ptr = NULL;
+
+ object_data = zend_object_store_get_object(this TSRMLS_CC);
+ zf = zend_get_std_object_handlers()->get_constructor(this TSRMLS_CC);
+ this_ce = Z_OBJCE_P(this);
+
+ fci.size = sizeof(fci);
+ fci.function_table = &this_ce->function_table;
+ /* fci.function_name = ; not necessary */
+ /* fci.symbol_table = ; not necessary */
+ fci.retval_ptr_ptr = &retval_ptr;
+ fci.param_count = ZEND_NUM_ARGS();
+ if (fci.param_count > 0) {
+ fci.params = emalloc(fci.param_count * sizeof *fci.params);
+ if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), fci.params) == FAILURE) {
+ zend_throw_exception(NULL, "Unexpected error fetching arguments", 0 TSRMLS_CC);
+ return;
+ }
+ }
+ fci.object_ptr = this;
+ fci.no_separation = 0;
+
+ fci_cache.initialized = 1;
+ fci_cache.called_scope = EG(current_execute_data)->called_scope;
+ fci_cache.calling_scope = EG(current_execute_data)->current_scope;
+ fci_cache.function_handler = zf;
+ fci_cache.object_ptr = this;
+
+ if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == FAILURE) {
+ if (!EG(exception)) {
+ zend_throw_exception(NULL, "Error calling parent constructor", 0 TSRMLS_CC);
+ }
+ goto cleanup;
+ }
+ if (!EG(exception) && SPL_G(validating_fun)(object_data TSRMLS_CC) == 0)
+ zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC,
+ "In the constructor of %s, parent::__construct() must be called "
+ "and its exceptions cannot be cleared", this_ce->name);
+
+cleanup:
+ if (fci.params != NULL) {
+ efree(fci.params);
+ }
+ if (retval_ptr != NULL) {
+ zval_ptr_dtor(&retval_ptr);
+ }
+}
+/* }}} */
+
/* {{{ PHP_MINFO(spl)
*/
PHP_MINFO_FUNCTION(spl)
ZEND_BEGIN_MODULE_GLOBALS(spl)
- char * autoload_extensions;
- HashTable * autoload_functions;
- int autoload_running;
- int autoload_extensions_len;
- intptr_t hash_mask_handle;
- intptr_t hash_mask_handlers;
- int hash_mask_init;
+ char * autoload_extensions;
+ HashTable * autoload_functions;
+ int autoload_running;
+ int autoload_extensions_len;
+ intptr_t hash_mask_handle;
+ intptr_t hash_mask_handlers;
+ int hash_mask_init;
+ zend_function constr_wrapper_fun;
+ int (*validating_fun)(void *object_data TSRMLS_DC);
ZEND_END_MODULE_GLOBALS(spl)
#ifdef ZTS
PHPAPI void php_spl_object_hash(zval *obj, char* md5str TSRMLS_DC);
+zend_function *php_spl_get_constructor_helper(zval *object, int (*validating_fun)(void *object_data TSRMLS_DC) TSRMLS_DC);
+
#endif /* PHP_SPL_H */
/*
/* declare the class handlers */
static zend_object_handlers spl_filesystem_object_handlers;
+static zend_object_handlers spl_filesystem_object_constru_check_handlers;
-/* decalre the class entry */
+/* declare the class entry */
PHPAPI zend_class_entry *spl_ce_SplFileInfo;
PHPAPI zend_class_entry *spl_ce_DirectoryIterator;
PHPAPI zend_class_entry *spl_ce_FilesystemIterator;
- clone
- new
*/
-static zend_object_value spl_filesystem_object_new_ex(zend_class_entry *class_type, spl_filesystem_object **obj TSRMLS_DC)
+static zend_object_value spl_filesystem_object_new_ex(zend_class_entry *class_type, zend_object_handlers *handlers, spl_filesystem_object **obj TSRMLS_DC)
{
zend_object_value retval;
spl_filesystem_object *intern;
/* intern->type = SPL_FS_INFO; done by set 0 */
intern->file_class = spl_ce_SplFileObject;
intern->info_class = spl_ce_SplFileInfo;
- if (obj) *obj = intern;
+ if (obj) {
+ *obj = intern;
+ }
zend_object_std_init(&intern->std, class_type TSRMLS_CC);
object_properties_init(&intern->std, class_type);
retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) spl_filesystem_object_free_storage, NULL TSRMLS_CC);
- retval.handlers = &spl_filesystem_object_handlers;
+ if (!handlers) {
+ retval.handlers = &spl_filesystem_object_handlers;
+ } else {
+ retval.handlers = handlers;
+ }
return retval;
}
/* }}} */
/* See spl_filesystem_object_new_ex */
static zend_object_value spl_filesystem_object_new(zend_class_entry *class_type TSRMLS_DC)
{
- return spl_filesystem_object_new_ex(class_type, NULL TSRMLS_CC);
+ return spl_filesystem_object_new_ex(class_type, NULL /* spl_filesystem_object_handlers */, NULL TSRMLS_CC);
+}
+/* }}} */
+
+static zend_object_value spl_filesystem_object_constru_check_new(zend_class_entry *class_type TSRMLS_DC)
+{
+ return spl_filesystem_object_new_ex(class_type, &spl_filesystem_object_constru_check_handlers, NULL TSRMLS_CC);
}
/* }}} */
old_object = zend_objects_get_address(zobject TSRMLS_CC);
source = (spl_filesystem_object*)old_object;
- new_obj_val = spl_filesystem_object_new_ex(old_object->ce, &intern TSRMLS_CC);
+ new_obj_val = spl_filesystem_object_new_ex(old_object->ce, NULL, &intern TSRMLS_CC);
new_object = &intern->std;
intern->flags = source->flags;
zend_update_class_constants(ce TSRMLS_CC);
- return_value->value.obj = spl_filesystem_object_new_ex(ce, &intern TSRMLS_CC);
+ return_value->value.obj = spl_filesystem_object_new_ex(ce, NULL, &intern TSRMLS_CC);
Z_TYPE_P(return_value) = IS_OBJECT;
if (ce->constructor->common.scope != spl_ce_SplFileInfo) {
zend_update_class_constants(ce TSRMLS_CC);
- return_value->value.obj = spl_filesystem_object_new_ex(ce, &intern TSRMLS_CC);
+ return_value->value.obj = spl_filesystem_object_new_ex(ce, NULL, &intern TSRMLS_CC);
Z_TYPE_P(return_value) = IS_OBJECT;
spl_filesystem_object_get_file_name(source TSRMLS_CC);
zend_update_class_constants(ce TSRMLS_CC);
- return_value->value.obj = spl_filesystem_object_new_ex(ce, &intern TSRMLS_CC);
+ return_value->value.obj = spl_filesystem_object_new_ex(ce, NULL, &intern TSRMLS_CC);
Z_TYPE_P(return_value) = IS_OBJECT;
spl_filesystem_object_get_file_name(source TSRMLS_CC);
}
/* }}} */
+static int spl_filesystem_object_constructor_validator(void *object_data TSRMLS_DC) /* {{{ */
+{
+ spl_filesystem_object *fsobj = object_data;
+
+ /* check if GlobIterator and Spl[Temp]FileObject had their constructor
+ * and check if everything went smoothly/there was an exception not cleared
+ * or if there was an userspace class that did not call the parent
+ * constructor or cleared its exception */
+
+ return (fsobj->u.dir.entry.d_name[0] != '\0' /* GlobIterator */ ||
+ fsobj->orig_path != NULL /* Spl[Temp]FileObject */);
+}
+/* }}} */
+
+static zend_function *spl_filesystem_object_get_constructor(zval *object TSRMLS_DC) /* {{{ */
+{
+ return php_spl_get_constructor_helper(object,
+ spl_filesystem_object_constructor_validator TSRMLS_CC);
+}
+/* }}} */
+
#define DIT_CTOR_FLAGS 0x00000001
#define DIT_CTOR_GLOB 0x00000002
REGISTER_SPL_IMPLEMENTS(DirectoryIterator, SeekableIterator);
spl_ce_DirectoryIterator->get_iterator = spl_filesystem_dir_get_iterator;
-
+
REGISTER_SPL_SUB_CLASS_EX(FilesystemIterator, DirectoryIterator, spl_filesystem_object_new, spl_FilesystemIterator_functions);
REGISTER_SPL_CLASS_CONST_LONG(FilesystemIterator, "CURRENT_MODE_MASK", SPL_FILE_DIR_CURRENT_MODE_MASK);
REGISTER_SPL_SUB_CLASS_EX(RecursiveDirectoryIterator, FilesystemIterator, spl_filesystem_object_new, spl_RecursiveDirectoryIterator_functions);
REGISTER_SPL_IMPLEMENTS(RecursiveDirectoryIterator, RecursiveIterator);
+
+ /* These need the parent constructor call check if extended in userspace.
+ * The previous ones probably don't work very well if */
+ memcpy(&spl_filesystem_object_constru_check_handlers, &spl_filesystem_object_handlers, sizeof(zend_object_handlers));
+ spl_filesystem_object_constru_check_handlers.get_constructor = spl_filesystem_object_get_constructor;
#ifdef HAVE_GLOB
- REGISTER_SPL_SUB_CLASS_EX(GlobIterator, FilesystemIterator, spl_filesystem_object_new, spl_GlobIterator_functions);
+ REGISTER_SPL_SUB_CLASS_EX(GlobIterator, FilesystemIterator, spl_filesystem_object_constru_check_new, spl_GlobIterator_functions);
REGISTER_SPL_IMPLEMENTS(GlobIterator, Countable);
#endif
- REGISTER_SPL_SUB_CLASS_EX(SplFileObject, SplFileInfo, spl_filesystem_object_new, spl_SplFileObject_functions);
+ REGISTER_SPL_SUB_CLASS_EX(SplFileObject, SplFileInfo, spl_filesystem_object_constru_check_new, spl_SplFileObject_functions);
REGISTER_SPL_IMPLEMENTS(SplFileObject, RecursiveIterator);
REGISTER_SPL_IMPLEMENTS(SplFileObject, SeekableIterator);
REGISTER_SPL_CLASS_CONST_LONG(SplFileObject, "SKIP_EMPTY", SPL_FILE_OBJECT_SKIP_EMPTY);
REGISTER_SPL_CLASS_CONST_LONG(SplFileObject, "READ_CSV", SPL_FILE_OBJECT_READ_CSV);
- REGISTER_SPL_SUB_CLASS_EX(SplTempFileObject, SplFileObject, spl_filesystem_object_new, spl_SplTempFileObject_functions);
+ REGISTER_SPL_SUB_CLASS_EX(SplTempFileObject, SplFileObject, spl_filesystem_object_constru_check_new, spl_SplTempFileObject_functions);
return SUCCESS;
}
/* }}} */
return function_handler;
}
+static int spl_recursive_it_constructor_validator(void *object_data TSRMLS_DC) /* {{{ */
+{
+ spl_recursive_it_object *sobj = object_data;
+ return (sobj->iterators != NULL);
+}
+/* }}} */
+
+static zend_function *spl_recursive_it_get_constructor(zval *object TSRMLS_DC) /* {{{ */
+{
+ return php_spl_get_constructor_helper(object,
+ spl_recursive_it_constructor_validator TSRMLS_CC);
+}
+/* }}} */
+
+static int spl_dual_it_constructor_validator(void *object_data TSRMLS_DC) /* {{{ */
+{
+ spl_dual_it_object *dobj = object_data;
+ return (dobj->dit_type != DIT_Unknown);
+}
+/* }}} */
+
+static zend_function *spl_dual_it_get_constructor(zval *object TSRMLS_DC) /* {{{ */
+{
+ return php_spl_get_constructor_helper(object,
+ spl_dual_it_constructor_validator TSRMLS_CC);
+}
+/* }}} */
+
/* {{{ spl_RecursiveIteratorIterator_dtor */
static void spl_RecursiveIteratorIterator_dtor(zend_object *_object, zend_object_handle handle TSRMLS_DC)
{
}
#endif
-#define SPL_CHECK_CTOR(intern, classname) \
- if (intern->dit_type == DIT_Unknown) { \
- zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Classes derived from %s must call %s::__construct()", \
- (spl_ce_##classname)->name, (spl_ce_##classname)->name); \
- return; \
- }
-
-#define APPENDIT_CHECK_CTOR(intern) SPL_CHECK_CTOR(intern, AppendIterator)
-
static inline int spl_dual_it_fetch(spl_dual_it_object *intern, int check_more TSRMLS_DC);
static inline int spl_cit_check_flags(int flags)
zval *it;
intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
-
- APPENDIT_CHECK_CTOR(intern);
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "O", &it, zend_ce_iterator) == FAILURE) {
return;
}
intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
-
- APPENDIT_CHECK_CTOR(intern);
+
spl_array_iterator_key(intern->u.append.zarrayit, return_value TSRMLS_CC);
} /* }}} */
}
intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
-
- APPENDIT_CHECK_CTOR(intern);
+
RETURN_ZVAL(intern->u.append.zarrayit, 1, 0);
} /* }}} */
memcpy(&spl_handlers_rec_it_it, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
spl_handlers_rec_it_it.get_method = spl_recursive_it_get_method;
spl_handlers_rec_it_it.clone_obj = NULL;
+ spl_handlers_rec_it_it.get_constructor = spl_recursive_it_get_constructor;
memcpy(&spl_handlers_dual_it, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
spl_handlers_dual_it.get_method = spl_dual_it_get_method;
/*spl_handlers_dual_it.call_method = spl_dual_it_call_method;*/
spl_handlers_dual_it.clone_obj = NULL;
+ spl_handlers_dual_it.get_constructor = spl_dual_it_get_constructor;
spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator;
spl_ce_RecursiveIteratorIterator->iterator_funcs.funcs = &spl_recursive_it_iterator_funcs;
==DONE==
<?php exit(0); ?>
--EXPECTF--
-Fatal error: main(): The foo instance wasn't initialized properly in %s on line %d
+Fatal error: Uncaught exception 'LogicException' with message 'In the constructor of foo, parent::__construct() must be called and its exceptions cannot be cleared' in %s:%d
+Stack trace:
+#0 %s(%d): foo->internal_construction_wrapper('This is bar')
+#1 {main}
+ thrown in %s on line %d
?>
--EXPECTF--
-Fatal error: RecursiveIteratorIterator::rewind(): The RecursiveArrayIteratorIterator instance wasn't initialized properly in %s on line %d
+Fatal error: Uncaught exception 'LogicException' with message 'In the constructor of RecursiveArrayIteratorIterator, parent::__construct() must be called and its exceptions cannot be cleared' in %s:%d
+Stack trace:
+#0 %s(%d): RecursiveArrayIteratorIterator->internal_construction_wrapper(Object(RecursiveArrayIterator), 2)
+#1 {main}
+ thrown in %s on line %d
--- /dev/null
+--TEST--
+Bug #54384: Several SPL classes crash when the parent constructor is not called
+--FILE--
+<?php
+
+function test($f) {
+ try {
+ $f();
+ echo "ran normally (unexpected)\n\n";
+ } catch (LogicException $e) {
+ echo "exception (expected)\n";
+ }
+}
+
+echo "IteratorIterator... ";
+class IteratorIteratorTest extends IteratorIterator {
+ function __construct(){}
+}
+test( function() {
+ $o = new IteratorIteratorTest;
+ $o->rewind();
+} );
+
+echo "FilterIterator... ";
+class FilterIteratorTest extends FilterIterator {
+ function __construct(){}
+ function accept(){}
+}
+test( function() {
+ $o = new FilterIteratorTest;
+ $o->rewind();
+} );
+
+echo "RecursiveFilterIterator... ";
+class RecursiveFilterIteratorTest extends RecursiveFilterIterator {
+ function __construct(){}
+ function accept(){}
+}
+test( function() {
+$o = new RecursiveFilterIteratorTest;
+$o->hasChildren();
+} );
+
+echo "ParentIterator... ";
+class ParentIteratorTest extends ParentIterator {
+ function __construct(){}
+}
+test ( function() {
+$o = new ParentIteratorTest;
+$o->accept();
+} );
+
+echo "LimitIterator... ";
+class LimitIteratorTest extends LimitIterator {
+ function __construct(){}
+}
+test ( function() {
+$o = new LimitIteratorTest;
+$o->rewind();
+} );
+
+echo "CachingIterator... ";
+class CachingIteratorTest extends CachingIterator {
+ function __construct(){}
+}
+test ( function() {
+$o = new CachingIteratorTest;
+$o->rewind();
+} );
+
+echo "RecursiveCachingIterator... ";
+class RecursiveCachingIteratorTest extends RecursiveCachingIterator {
+ function __construct(){}
+}
+test ( function() {
+$o = new RecursiveCachingIteratorTest;
+$o->rewind();
+} );
+
+echo "NoRewindIterator... ";
+class NoRewindIteratorTest extends NoRewindIterator {
+ function __construct(){}
+}
+test ( function() {
+$o = new NoRewindIteratorTest;
+$o->valid();
+} );
+
+echo "RegexIterator... ";
+class RegexIteratorTest extends RegexIterator {
+ function __construct(){}
+}
+test ( function() {
+$o = new RegexIteratorTest;
+$o->rewind();
+} );
+
+echo "RecursiveRegexIterator... ";
+class RecursiveRegexIteratorTest extends RecursiveRegexIterator {
+ function __construct(){}
+}
+test ( function() {
+$o = new RecursiveRegexIteratorTest;
+$o->hasChildren();
+} );
+
+echo "GlobIterator... ";
+class GlobIteratorTest extends GlobIterator {
+ function __construct(){}
+}
+test ( function() {
+$o = new GlobIteratorTest;
+$o->count();
+} );
+
+echo "SplFileObject... ";
+class SplFileObjectTest extends SplFileObject {
+ function __construct(){}
+}
+test ( function() {
+$o = new SplFileObjectTest;
+$o->rewind();
+} );
+
+echo "SplTempFileObject... ";
+class SplTempFileObjectTest extends SplTempFileObject {
+ function __construct(){}
+}
+test ( function() {
+$o = new SplTempFileObjectTest;
+$o->rewind();
+} );
+
+
+
+function test2($f) {
+ try {
+ $f();
+ echo "ran normally (expected)\n";
+ } catch (LogicException $e) {
+ echo "exception (unexpected)\n\n";
+ }
+}
+
+echo "SplFileInfo... ";
+class SplFileInfoTest extends SplFileInfo {
+ function __construct(){}
+}
+test2 ( function() {
+$o = new SplFileInfoTest;
+/* handles with fatal error */
+/* echo $o->getMTime(), " : "; */
+} );
+
+echo "DirectoryIterator... ";
+class DirectoryIteratortest extends DirectoryIterator {
+ function __construct(){}
+}
+test2 ( function() {
+$o = new DirectoryIteratorTest;
+foreach ($o as $a) {
+echo $a,"\n";
+}
+} );
+
+echo "FileSystemIterator... ";
+class FileSystemIteratorTest extends DirectoryIterator {
+ function __construct(){}
+}
+test2 ( function() {
+$o = new FileSystemIteratorTest;
+foreach ($o as $a) {
+echo $a,"\n";
+}
+} );
+
+echo "RecursiveDirectoryIterator... ";
+class RecursiveDirectoryIteratorTest extends RecursiveDirectoryIterator {
+ function __construct(){}
+}
+test2 ( function() {
+$o = new RecursiveDirectoryIteratorTest;
+foreach ($o as $a) {
+echo $a,"\n";
+}
+} );
+--EXPECT--
+IteratorIterator... exception (expected)
+FilterIterator... exception (expected)
+RecursiveFilterIterator... exception (expected)
+ParentIterator... exception (expected)
+LimitIterator... exception (expected)
+CachingIterator... exception (expected)
+RecursiveCachingIterator... exception (expected)
+NoRewindIterator... exception (expected)
+RegexIterator... exception (expected)
+RecursiveRegexIterator... exception (expected)
+GlobIterator... exception (expected)
+SplFileObject... exception (expected)
+SplTempFileObject... exception (expected)
+SplFileInfo... ran normally (expected)
+DirectoryIterator... ran normally (expected)
+FileSystemIterator... ran normally (expected)
+RecursiveDirectoryIterator... ran normally (expected)
echo "$k=>$v\n";
}
+class MyAppendIterator2 extends AppendIterator
+{
+ function __construct()
+ {
+ echo __METHOD__ . "\n";
+ }
+}
+
class MyAppendIterator extends AppendIterator
{
function __construct()
{
+ parent::__construct();
echo __METHOD__ . "\n";
}
}
}
-$ap = new MyAppendIterator;
-
try
{
- $ap->append($it);
+ $aperr = new MyAppendIterator2;
+
}
-catch(BadMethodCallException $e)
+catch(Exception $e)
{
echo $e->getMessage() . "\n";
}
-$ap->parent__construct();
+$ap = new MyAppendIterator;
try
{
$ap->parent__construct($it);
}
-catch(BadMethodCallException $e)
+catch(Exception $e)
{
echo $e->getMessage() . "\n";
}
MyArrayIterator::rewind
0=>1
1=>2
+MyAppendIterator2::__construct
+In the constructor of MyAppendIterator2, parent::__construct() must be called and its exceptions cannot be cleared
MyAppendIterator::__construct
-MyAppendIterator::append
-Classes derived from AppendIterator must call AppendIterator::__construct()
AppendIterator::getIterator() must be called exactly once per instance
MyAppendIterator::append
MyArrayIterator::rewind