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)
+{
+ int cnt = 0;
+
+ cnt += (flags & CIT_CALL_TOSTRING) ? 1 : 0;
+ cnt += (flags & CIT_TOSTRING_USE_KEY) ? 1 : 0;
+ cnt += (flags & CIT_TOSTRING_USE_CURRENT) ? 1 : 0;
+ cnt += (flags & CIT_TOSTRING_USE_INNER) ? 1 : 0;
+
+ return cnt <= 1 ? SUCCESS : FAILURE;
+}
+
static spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce_base, zend_class_entry *ce_inner, dual_it_type dit_type)
{
zval *zobject, *retval;
php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
return NULL;
}
- if (((flags & CIT_CALL_TOSTRING) && (flags & (CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT)))
- || ((flags & (CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT)) == (CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT))) {
+ if (spl_cit_check_flags(flags) != SUCCESS) {
php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
- zend_throw_exception(spl_ce_InvalidArgumentException, "Flags must contain only one of CATCH_GET_CHILD, CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT", 0 TSRMLS_CC);
+ zend_throw_exception(spl_ce_InvalidArgumentException, "Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_CURRENT", 0 TSRMLS_CC);
return NULL;
}
intern->u.caching.flags |= flags & CIT_PUBLIC;
}
if (intern->u.caching.flags & CIT_CATCH_GET_CHILD) {
zend_clear_exception(TSRMLS_C);
+ } else {
+ return;
}
- return;
} else {
INIT_PZVAL(&zflags);
ZVAL_LONG(&zflags, intern->u.caching.flags & CIT_PUBLIC);
zval_ptr_dtor(&zchildren);
}
}
- zval_ptr_dtor(&retval);
+ zval_ptr_dtor(&retval);
}
- if (intern->u.caching.flags & CIT_CALL_TOSTRING) {
+ if (intern->u.caching.flags & (CIT_TOSTRING_USE_INNER|CIT_CALL_TOSTRING)) {
int use_copy;
zval expr_copy;
ALLOC_ZVAL(intern->u.caching.zstr);
- *intern->u.caching.zstr = *intern->current.data;
+ if (intern->u.caching.flags & CIT_TOSTRING_USE_INNER) {
+ *intern->u.caching.zstr = *intern->inner.zobject;
+ } else {
+ *intern->u.caching.zstr = *intern->current.data;
+ }
if (UG(unicode)) {
zend_make_unicode_zval(intern->u.caching.zstr, &expr_copy, &use_copy);
} else {
intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
- if (!(intern->u.caching.flags & (CIT_CALL_TOSTRING|CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT))) {
+ if (!(intern->u.caching.flags & (CIT_CALL_TOSTRING|CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT|CIT_TOSTRING_USE_INNER))) {
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%v does not fetch string value (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name);
}
if (intern->u.caching.flags & CIT_TOSTRING_USE_KEY) {
RETURN_UNICODEL(intern->current.str_key.u, intern->current.str_key_len, 1);
} else {
RETVAL_LONG(intern->current.int_key);
- convert_to_string(return_value);
+ convert_to_text(return_value);
return;
}
} else if (intern->u.caching.flags & CIT_TOSTRING_USE_CURRENT) {
- RETVAL_ZVAL(intern->current.data, 1, 0);
-
- return;
- }
- if (intern->u.caching.zstr) {
- *return_value = *intern->u.caching.zstr;
+ *return_value = *intern->current.data;
zval_copy_ctor(return_value);
convert_to_text(return_value);
INIT_PZVAL(return_value);
+ return;
+ }
+ if (intern->u.caching.zstr) {
+ RETURN_ZVAL(intern->u.caching.zstr, 1, 0);
} else {
RETURN_NULL();
}
return;
}
- if (((flags & CIT_CALL_TOSTRING) && (flags & (CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT)))
- || ((flags & (CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT)) == (CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT))) {
- zend_throw_exception(spl_ce_InvalidArgumentException , "Flags must contain only one of CIT_CALL_TOSTRING, CIT_TOSTRING_USE_KEY, CIT_TOSTRING_USE_CURRENT", 0 TSRMLS_CC);
+ if (spl_cit_check_flags(flags) != SUCCESS) {
+ zend_throw_exception(spl_ce_InvalidArgumentException , "Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER", 0 TSRMLS_CC);
return;
}
- if ((intern->u.caching.flags & CIT_CALL_TOSTRING) != 0 && (flags & ~CIT_CALL_TOSTRING) == 0) {
+ if ((intern->u.caching.flags & CIT_CALL_TOSTRING) != 0 && (flags & CIT_CALL_TOSTRING) == 0) {
zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag CALL_TO_STRING is not possible", 0 TSRMLS_CC);
return;
}
+ if ((intern->u.caching.flags & CIT_TOSTRING_USE_INNER) != 0 && (flags & CIT_TOSTRING_USE_INNER) == 0) {
+ zend_throw_exception(spl_ce_InvalidArgumentException, "Unsetting flag TOSTRING_USE_INNER is not possible", 0 TSRMLS_CC);
+ return;
+ }
if ((flags && CIT_FULL_CACHE) != 0 && (intern->u.caching.flags & CIT_FULL_CACHE) == 0) {
/* clear on (re)enable */
zend_hash_clean(HASH_OF(intern->u.caching.zcache));
REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "CATCH_GET_CHILD", CIT_CATCH_GET_CHILD);
REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_KEY", CIT_TOSTRING_USE_KEY);
REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_CURRENT", CIT_TOSTRING_USE_CURRENT);
+ REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "TOSTRING_USE_INNER",CIT_TOSTRING_USE_INNER);
REGISTER_SPL_CLASS_CONST_LONG(CachingIterator, "FULL_CACHE", CIT_FULL_CACHE);
REGISTER_SPL_SUB_CLASS_EX(RecursiveCachingIterator, CachingIterator, spl_dual_it_new, spl_funcs_RecursiveCachingIterator);
--- /dev/null
+--TEST--
+SPL: CachingIterator and __toString
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+
+function test($ar, $flags)
+{
+ echo "===$flags===\n";
+ $it = new CachingIterator($ar, 0);
+ try
+ {
+ $it->setFlags($flags);
+ }
+ catch (Exception $e)
+ {
+ echo 'Exception: ' . $e->getMessage() . "\n";
+ var_dump($it->getFlags());
+ return;
+ }
+ var_dump($it->getFlags());
+ try
+ {
+ foreach($it as $v)
+ {
+ var_dump((string)$it);
+ }
+ }
+ catch (Exception $e)
+ {
+ echo 'Exception: ' . $e->getMessage() . "\n";
+ }
+}
+
+class MyItem
+{
+ function __construct($value)
+ {
+ $this->value = $value;
+ }
+
+ function __toString()
+ {
+ return (string)$this->value;
+ }
+}
+
+class MyArrayIterator extends ArrayIterator
+{
+ function __toString()
+ {
+ return $this->key() . ':' . $this->current();
+ }
+}
+
+$ar = new MyArrayIterator(array(1, 2, 3));
+
+test($ar, CachingIterator::CALL_TOSTRING);
+test($ar, CachingIterator::TOSTRING_USE_KEY);
+test($ar, CachingIterator::TOSTRING_USE_CURRENT);
+
+$ar = new MyArrayIterator(array(new MyItem(1), new MyItem(2), new MyItem(3)));
+
+test($ar, CachingIterator::TOSTRING_USE_INNER);
+test($ar, CachingIterator::CALL_TOSTRING | CachingIterator::TOSTRING_USE_KEY);
+test($ar, CachingIterator::CALL_TOSTRING | CachingIterator::TOSTRING_USE_CURRENT);
+test($ar, CachingIterator::CALL_TOSTRING | CachingIterator::TOSTRING_USE_INNER);
+test($ar, CachingIterator::TOSTRING_USE_KEY | CachingIterator::TOSTRING_USE_CURRENT);
+test($ar, CachingIterator::TOSTRING_USE_KEY | CachingIterator::TOSTRING_USE_INNER);
+
+echo "===X===\n";
+try
+{
+ $it = new CachingIterator($ar, CachingIterator::CALL_TOSTRING);
+ $it->setFlags(0);
+}
+catch (Exception $e)
+{
+ echo 'Exception: ' . $e->getMessage() . "\n";
+}
+try
+{
+ $it = new CachingIterator($ar, CachingIterator::TOSTRING_USE_INNER);
+ $it->setFlags(0);
+}
+catch (Exception $e)
+{
+ echo 'Exception: ' . $e->getMessage() . "\n";
+}
+
+?>
+===DONE===
+--EXPECTF--
+===1===
+int(1)
+string(1) "1"
+string(1) "2"
+string(1) "3"
+===2===
+int(2)
+string(1) "0"
+string(1) "1"
+string(1) "2"
+===4===
+int(4)
+string(1) "1"
+string(1) "2"
+string(1) "3"
+===8===
+int(8)
+string(3) "0:1"
+string(3) "1:2"
+string(3) "2:3"
+===3===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===5===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===9===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===6===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===10===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===X===
+Exception: Unsetting flag CALL_TO_STRING is not possible
+Exception: Unsetting flag TOSTRING_USE_INNER is not possible
+===DONE===
+--UEXPECTF--
+===1===
+int(1)
+unicode(1) "1"
+unicode(1) "2"
+unicode(1) "3"
+===2===
+int(2)
+unicode(1) "0"
+unicode(1) "1"
+unicode(1) "2"
+===4===
+int(4)
+unicode(1) "1"
+unicode(1) "2"
+unicode(1) "3"
+===8===
+int(8)
+unicode(3) "0:1"
+unicode(3) "1:2"
+unicode(3) "2:3"
+===3===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===5===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===9===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===6===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===10===
+Exception: Flags must contain only one of CALL_TOSTRING, TOSTRING_USE_KEY, TOSTRING_USE_CURRENT, TOSTRING_USE_INNER
+int(0)
+===X===
+Exception: Unsetting flag CALL_TO_STRING is not possible
+Exception: Unsetting flag TOSTRING_USE_INNER is not possible
+===DONE===