lbarnaud                                 Sun, 27 Mar 2011 20:13:27 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=309749

Log:
Added CallbackFilterIterator and RecursiveCallbackFilterIterator classes

[DOC] This is a concrete FilterIterator which takes a callback as
constructor parameter, and uses this callback for filtering. This allows
to use FilterIterator without extending it.

CallbackFilterIterator Example:

$it = new ArrayIterator(range(1,100));
$it = new CallbackFilterIterator($it, function($value) {
        return $value % 2;
});
foreach($it as $value) // ...

RecursiveCallbackFilterIterator Example:

$it = new RecursiveDirectoryIterator("/");
$it = new RecursiveCallbackFilterIterator($it, function($file, $k, $it) {
        return $it->hasChildren() || $file->getSize() > 1024;
});
foreach(new RecursiveIteratorIterator($it) as $file) // ...

The callback takes the current value, the current key and the inner
iterator as parameters.

Changed paths:
    U   php/php-src/trunk/NEWS
    U   php/php-src/trunk/ext/spl/spl_iterators.c
    U   php/php-src/trunk/ext/spl/spl_iterators.h
    A   php/php-src/trunk/ext/spl/tests/CallbackFilterIteratorTest-002.phpt
    A   php/php-src/trunk/ext/spl/tests/CallbackFilterIteratorTest.phpt
    A   php/php-src/trunk/ext/spl/tests/RecursiveCallbackFilterIteratorTest.phpt

Modified: php/php-src/trunk/NEWS
===================================================================
--- php/php-src/trunk/NEWS	2011-03-27 18:14:58 UTC (rev 309748)
+++ php/php-src/trunk/NEWS	2011-03-27 20:13:27 UTC (rev 309749)
@@ -197,6 +197,7 @@
   . Added RegexIterator::getRegex() method. (Joshua Thijssen)
   . Added SplObjectStorage::getHash() hook. (Etienne)
   . Added SplFileInfo::getExtension(). FR #48767. (Peter Cowburn)
+  . Added CallbackFilterIterator and RecursiveCallbackFilterIterator (Arnaud)

 - Improved ZLIB extension:
   . Re-implemented non-file related functionality. (Mike)

Modified: php/php-src/trunk/ext/spl/spl_iterators.c
===================================================================
--- php/php-src/trunk/ext/spl/spl_iterators.c	2011-03-27 18:14:58 UTC (rev 309748)
+++ php/php-src/trunk/ext/spl/spl_iterators.c	2011-03-27 20:13:27 UTC (rev 309749)
@@ -44,7 +44,9 @@
 PHPAPI zend_class_entry *spl_ce_RecursiveIterator;
 PHPAPI zend_class_entry *spl_ce_RecursiveIteratorIterator;
 PHPAPI zend_class_entry *spl_ce_FilterIterator;
+PHPAPI zend_class_entry *spl_ce_CallbackFilterIterator;
 PHPAPI zend_class_entry *spl_ce_RecursiveFilterIterator;
+PHPAPI zend_class_entry *spl_ce_RecursiveCallbackFilterIterator;
 PHPAPI zend_class_entry *spl_ce_ParentIterator;
 PHPAPI zend_class_entry *spl_ce_SeekableIterator;
 PHPAPI zend_class_entry *spl_ce_LimitIterator;
@@ -1499,6 +1501,23 @@
 			break;
 		}
 #endif
+		case DIT_CallbackFilterIterator:
+		case DIT_RecursiveCallbackFilterIterator: {
+			_spl_cbfilter_it_intern *cfi = emalloc(sizeof(*cfi));
+			if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Of", &zobject, ce_inner, &cfi->fci, &cfi->fcc) == FAILURE) {
+				zend_restore_error_handling(&error_handling TSRMLS_CC);
+				efree(cfi);
+				return NULL;
+			}
+			if (cfi->fci.function_name) {
+				Z_ADDREF_P(cfi->fci.function_name);
+			}
+			if (cfi->fci.object_ptr) {
+				Z_ADDREF_P(cfi->fci.object_ptr);
+			}
+			intern->u.cbfilter = cfi;
+			break;
+		}
 		default:
 			if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zobject, ce_inner) == FAILURE) {
 				zend_restore_error_handling(&error_handling TSRMLS_CC);
@@ -1527,6 +1546,13 @@
 	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_FilterIterator, zend_ce_iterator, DIT_FilterIterator);
 } /* }}} */

+/* {{{ proto void CallbackFilterIterator::__construct(Iterator it, callback)
+   Create an Iterator from another iterator */
+SPL_METHOD(CallbackFilterIterator, __construct)
+{
+	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_CallbackFilterIterator, zend_ce_iterator, DIT_CallbackFilterIterator);
+} /* }}} */
+
 /* {{{ proto Iterator FilterIterator::getInnerIterator()
        proto Iterator CachingIterator::getInnerIterator()
        proto Iterator LimitIterator::getInnerIterator()
@@ -1800,6 +1826,14 @@
 	spl_filter_it_next(getThis(), intern TSRMLS_CC);
 } /* }}} */

+/* {{{ proto void RecursiveCallbackFilterIterator::__construct(RecursiveIterator it, callback)
+   Create a RecursiveCallbackFilterIterator from a RecursiveIterator */
+SPL_METHOD(RecursiveCallbackFilterIterator, __construct)
+{
+	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RecursiveCallbackFilterIterator, spl_ce_RecursiveIterator, DIT_RecursiveCallbackFilterIterator);
+} /* }}} */
+
+
 /* {{{ proto void RecursiveFilterIterator::__construct(RecursiveIterator it)
    Create a RecursiveFilterIterator from a RecursiveIterator */
 SPL_METHOD(RecursiveFilterIterator, __construct)
@@ -1850,6 +1884,27 @@
 	}
 } /* }}} */

+/* {{{ proto RecursiveCallbackFilterIterator RecursiveCallbackFilterIterator::getChildren()
+   Return the inner iterator's children contained in a RecursiveCallbackFilterIterator */
+SPL_METHOD(RecursiveCallbackFilterIterator, getChildren)
+{
+	spl_dual_it_object   *intern;
+	zval                 *retval;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+	zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval);
+	if (!EG(exception) && retval) {
+		spl_instantiate_arg_ex2(Z_OBJCE_P(getThis()), &return_value, 0, retval, intern->u.cbfilter->fci.function_name TSRMLS_CC);
+	}
+	if (retval) {
+		zval_ptr_dtor(&retval);
+	}
+} /* }}} */
 /* {{{ proto void ParentIterator::__construct(RecursiveIterator it)
    Create a ParentIterator from a RecursiveIterator */
 SPL_METHOD(ParentIterator, __construct)
@@ -1865,6 +1920,53 @@
 	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_RegexIterator, zend_ce_iterator, DIT_RegexIterator);
 } /* }}} */

+/* {{{ proto bool CallbackFilterIterator::accept()
+   Calls the callback with the current value, the current key and the inner iterator as arguments */
+SPL_METHOD(CallbackFilterIterator, accept)
+{
+	spl_dual_it_object     *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
+	zend_fcall_info        *fci = &intern->u.cbfilter->fci;
+	zend_fcall_info_cache  *fcc = &intern->u.cbfilter->fcc;
+	zval                  **params[3];
+	zval                    zkey;
+	zval                   *zkey_p = &zkey;
+	zval                   *result;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	if (intern->current.data == NULL) {
+		RETURN_FALSE;
+	}
+
+	INIT_PZVAL(&zkey);
+	if (intern->current.key_type == HASH_KEY_IS_LONG) {
+		ZVAL_LONG(&zkey, intern->current.int_key);
+	} else {
+		ZVAL_STRINGL(&zkey, intern->current.str_key, intern->current.str_key_len-1, 0);
+	}
+
+	params[0] = &intern->current.data;
+	params[1] = &zkey_p;
+	params[2] = &intern->inner.zobject;
+
+	fci->retval_ptr_ptr = &result;
+	fci->param_count = 3;
+	fci->params = params;
+	fci->no_separation = 0;
+
+	if (zend_call_function(fci, fcc TSRMLS_CC) != SUCCESS || !result) {
+		RETURN_FALSE;
+	}
+	if (EG(exception)) {
+		return;
+	}
+
+	RETURN_ZVAL(result, 1, 1);
+}
+/* }}} */
+
 /* {{{ proto bool RegexIterator::accept()
    Match (string)current() against regular expression */
 SPL_METHOD(RegexIterator, accept)
@@ -2154,6 +2256,18 @@
 	}
 #endif

+	if (object->dit_type == DIT_CallbackFilterIterator || object->dit_type == DIT_RecursiveCallbackFilterIterator) {
+		if (object->u.cbfilter) {
+			if (object->u.cbfilter->fci.function_name) {
+				zval_ptr_dtor(&object->u.cbfilter->fci.function_name);
+			}
+			if (object->u.cbfilter->fci.object_ptr) {
+				zval_ptr_dtor(&object->u.cbfilter->fci.object_ptr);
+			}
+			efree(object->u.cbfilter);
+		}
+	}
+
 	zend_object_std_dtor(&object->std TSRMLS_CC);

 	efree(object);
@@ -2195,6 +2309,29 @@
 	{NULL, NULL, NULL}
 };

+ZEND_BEGIN_ARG_INFO(arginfo_callback_filter_it___construct, 0)
+	ZEND_ARG_OBJ_INFO(0, iterator, Iterator, 0)
+	ZEND_ARG_INFO(0, callback)
+ZEND_END_ARG_INFO();
+
+static const zend_function_entry spl_funcs_CallbackFilterIterator[] = {
+	SPL_ME(CallbackFilterIterator, __construct, arginfo_callback_filter_it___construct, ZEND_ACC_PUBLIC)
+	SPL_ME(CallbackFilterIterator, accept,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
+	{NULL, NULL, NULL}
+};
+
+ZEND_BEGIN_ARG_INFO(arginfo_recursive_callback_filter_it___construct, 0)
+	ZEND_ARG_OBJ_INFO(0, iterator, RecursiveIterator, 0)
+	ZEND_ARG_INFO(0, callback)
+ZEND_END_ARG_INFO();
+
+static const zend_function_entry spl_funcs_RecursiveCallbackFilterIterator[] = {
+	SPL_ME(RecursiveCallbackFilterIterator, __construct, arginfo_recursive_callback_filter_it___construct, ZEND_ACC_PUBLIC)
+	SPL_ME(RecursiveFilterIterator,  hasChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
+	SPL_ME(RecursiveCallbackFilterIterator,  getChildren,      arginfo_recursive_it_void, ZEND_ACC_PUBLIC)
+	{NULL, NULL, NULL}
+};
+
 ZEND_BEGIN_ARG_INFO(arginfo_parent_it___construct, 0)
 	ZEND_ARG_OBJ_INFO(0, iterator, RecursiveIterator, 0)
 ZEND_END_ARG_INFO();
@@ -3535,6 +3672,12 @@
 	REGISTER_SPL_SUB_CLASS_EX(RecursiveFilterIterator, FilterIterator, spl_dual_it_new, spl_funcs_RecursiveFilterIterator);
 	REGISTER_SPL_IMPLEMENTS(RecursiveFilterIterator, RecursiveIterator);

+	REGISTER_SPL_SUB_CLASS_EX(CallbackFilterIterator, FilterIterator, spl_dual_it_new, spl_funcs_CallbackFilterIterator);
+
+	REGISTER_SPL_SUB_CLASS_EX(RecursiveCallbackFilterIterator, CallbackFilterIterator, spl_dual_it_new, spl_funcs_RecursiveCallbackFilterIterator);
+	REGISTER_SPL_IMPLEMENTS(RecursiveCallbackFilterIterator, RecursiveIterator);
+
+
 	REGISTER_SPL_SUB_CLASS_EX(ParentIterator, RecursiveFilterIterator, spl_dual_it_new, spl_funcs_ParentIterator);

 	REGISTER_SPL_INTERFACE(Countable);

Modified: php/php-src/trunk/ext/spl/spl_iterators.h
===================================================================
--- php/php-src/trunk/ext/spl/spl_iterators.h	2011-03-27 18:14:58 UTC (rev 309748)
+++ php/php-src/trunk/ext/spl/spl_iterators.h	2011-03-27 20:13:27 UTC (rev 309749)
@@ -75,6 +75,8 @@
 	DIT_RegexIterator,
 	DIT_RecursiveRegexIterator,
 #endif
+	DIT_CallbackFilterIterator,
+	DIT_RecursiveCallbackFilterIterator,
 	DIT_Unknown = ~0
 } dual_it_type;

@@ -114,6 +116,11 @@
 	REGIT_MODE_MAX
 } regex_mode;

+typedef struct _spl_cbfilter_it_intern {
+	zend_fcall_info       fci;
+	zend_fcall_info_cache fcc;
+} _spl_cbfilter_it_intern;
+
 typedef struct _spl_dual_it_object {
 	zend_object              std;
 	struct {
@@ -157,6 +164,7 @@
 			uint             regex_len;
 		} regex;
 #endif
+		_spl_cbfilter_it_intern *cbfilter;
 	} u;
 } spl_dual_it_object;


Added: php/php-src/trunk/ext/spl/tests/CallbackFilterIteratorTest-002.phpt
===================================================================
--- php/php-src/trunk/ext/spl/tests/CallbackFilterIteratorTest-002.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/spl/tests/CallbackFilterIteratorTest-002.phpt	2011-03-27 20:13:27 UTC (rev 309749)
@@ -0,0 +1,71 @@
+--TEST--
+CallbackFilterIterator 002
+--FILE--
+<?php
+
+set_error_handler(function($errno, $errstr){
+	echo $errstr . "\n";
+	return true;
+});
+
+try {
+	new CallbackFilterIterator();
+} catch(InvalidArgumentException $e) {
+	echo $e->getMessage() . "\n";
+}
+
+try {
+	new CallbackFilterIterator(null);
+} catch(InvalidArgumentException $e) {
+	echo $e->getMessage() . "\n";
+}
+
+try {
+	new CallbackFilterIterator(new ArrayIterator(array()), null);
+} catch(InvalidArgumentException $e) {
+	echo $e->getMessage() . "\n";
+}
+
+try {
+	new CallbackFilterIterator(new ArrayIterator(array()), array());
+} catch(InvalidArgumentException $e) {
+	echo $e->getMessage() . "\n";
+}
+
+$it = new CallbackFilterIterator(new ArrayIterator(array(1)), function() {
+	throw new Exception("some message");
+});
+try {
+	foreach($it as $e);
+} catch(Exception $e) {
+	echo $e->getMessage() . "\n";
+}
+
+class Test extends CallbackFilterIterator {
+	function __construct(){}
+}
+class Test2 extends RecursiveCallbackFilterIterator {
+	function __construct(){}
+}
+
+try {
+	new Test;
+} catch(LogicException $e) {
+	echo $e->getMessage() . "\n";
+}
+
+try {
+	new Test2;
+} catch(LogicException $e) {
+	echo $e->getMessage() . "\n";
+}
+
+--EXPECT--
+CallbackFilterIterator::__construct() expects exactly 2 parameters, 0 given
+Argument 1 passed to CallbackFilterIterator::__construct() must implement interface Iterator, null given
+CallbackFilterIterator::__construct() expects exactly 2 parameters, 1 given
+CallbackFilterIterator::__construct() expects parameter 2 to be a valid callback, no array or string given
+CallbackFilterIterator::__construct() expects parameter 2 to be a valid callback, array must have exactly two members
+some message
+In the constructor of Test, parent::__construct() must be called and its exceptions cannot be cleared
+In the constructor of Test2, parent::__construct() must be called and its exceptions cannot be cleared

Added: php/php-src/trunk/ext/spl/tests/CallbackFilterIteratorTest.phpt
===================================================================
--- php/php-src/trunk/ext/spl/tests/CallbackFilterIteratorTest.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/spl/tests/CallbackFilterIteratorTest.phpt	2011-03-27 20:13:27 UTC (rev 309749)
@@ -0,0 +1,133 @@
+--TEST--
+CallbackFilterIterator
+--FILE--
+<?php
+
+class A {
+    function test($value, $key, $inner) {
+        return test($value, $key, $inner);
+    }
+}
+
+class B {
+    static function test($value, $key, $inner) {
+        return test($value, $key, $inner);
+    }
+}
+
+function test($value, $key, $inner) {
+	printf("%s / %s / %d / %d\n"
+		, $value
+		, $key
+		, $value == $inner->current()
+		, $key == $inner->key()
+	);
+	return $value === 1 || $value === 4;
+}
+
+$tests = array(
+    'instance method'    => function() { return array(new A, 'test'); },
+    'static method'      => function() { return array('B', 'test'); },
+    'static method (2)'  => function() { return 'B::test'; },
+    'function'           => function() { return 'test'; },
+    'anonymous function' => function() { return function($value, $key, $inner) { return test($value, $key, $inner); }; },
+);
+
+foreach($tests as $name => $test) {
+
+    $callback = $test();
+    $it = new ArrayIterator(range(1, 5));
+    $it = new CallbackFilterIterator($it, $callback);
+
+    echo " = $name =\n";
+
+    foreach($it as $value) {
+        echo "=> $value\n";
+    }
+
+	// same test, with no reference to callback
+
+    $it = new ArrayIterator(range(1, 5));
+    $it = new CallbackFilterIterator($it, $test());
+    unset($callback);
+
+    foreach($it as $value) {
+        echo "=> $value\n";
+    }
+}
+--EXPECT--
+= instance method =
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1
+ = static method =
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1
+ = static method (2) =
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1
+ = function =
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1
+ = anonymous function =
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 1 / 1 / 1
+3 / 2 / 1 / 1
+4 / 3 / 1 / 1
+=> 4
+5 / 4 / 1 / 1

Added: php/php-src/trunk/ext/spl/tests/RecursiveCallbackFilterIteratorTest.phpt
===================================================================
--- php/php-src/trunk/ext/spl/tests/RecursiveCallbackFilterIteratorTest.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/spl/tests/RecursiveCallbackFilterIteratorTest.phpt	2011-03-27 20:13:27 UTC (rev 309749)
@@ -0,0 +1,138 @@
+--TEST--
+RecursiveCallbackFilterIterator
+--FILE--
+<?php
+
+class A {
+    function test($value, $key, $inner) {
+        return test($value, $key, $inner);
+    }
+}
+
+class B {
+    static function test($value, $key, $inner) {
+        return test($value, $key, $inner);
+    }
+}
+
+function test($value, $key, $inner) {
+	if ($inner->hasChildren()) {
+		return true;
+	}
+	printf("%s / %s / %d / %d\n"
+		, print_r($value, true)
+		, $key
+		, $value == $inner->current()
+		, $key == $inner->key()
+	);
+	return $value === 1 || $value === 4;
+}
+
+$tests = array(
+    'instance method'    => function() { return array(new A, 'test'); },
+    'static method'      => function() { return array('B', 'test'); },
+    'static method (2)'  => function() { return 'B::test'; },
+    'function'           => function() { return 'test'; },
+    'anonymous function' => function() { return function($value, $key, $inner) { return test($value, $key, $inner); }; },
+);
+
+foreach($tests as $name => $test) {
+
+    $callback = $test();
+    $it = new RecursiveArrayIterator(array(1, array(2, 3), array(4, 5)));
+    $it = new RecursiveCallbackFilterIterator($it, $callback);
+	$it = new RecursiveIteratorIterator($it);
+
+    echo " = $name =\n";
+
+    foreach($it as $value) {
+        echo "=> $value\n";
+    }
+
+	// same test, with no reference to callback
+
+    $it = new RecursiveArrayIterator(array(1, array(2, 3), array(4, 5)));
+    $it = new RecursiveCallbackFilterIterator($it, $test());
+	$it = new RecursiveIteratorIterator($it);
+    unset($callback);
+
+    foreach($it as $value) {
+        echo "=> $value\n";
+    }
+}
+--EXPECT--
+= instance method =
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
+ = static method =
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
+ = static method (2) =
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
+ = function =
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
+ = anonymous function =
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
+1 / 0 / 1 / 1
+=> 1
+2 / 0 / 1 / 1
+3 / 1 / 1 / 1
+4 / 0 / 1 / 1
+=> 4
+5 / 1 / 1 / 1
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to