Hello There,

I would like to propose a third argument to implode(), named
skip_empty, that will cause empty elements to be ignored when
generating the implode string. By empty I mean everything that
converts to an empty string such as '', false, null, etc.

For example:

<?php
$a = array(1,'',2,null,3,0,4,true,5,false,false);
echo implode(',', $a); // old behavior
echo "\n";
echo implode(',', $a, true); // new feature
echo "\n";
echo $foo;
echo "\n";
?>

Will output:

1,,2,,3,0,4,1,5,,
1,2,3,0,4,1,5

Obviously the new parameter defaults to 'false' for backwards compatibility.

The patch file is attached for evaluation, review, feedback etc.
Please keep in mind that this is my first time playing with PHP source
so expect flaws on my code.

I did a few simple tests (using `time`, if someone has a non buggy
valgrind for OS X please let me know) against 5.3 CVS (named php53 in
examples) and my patched version (named php+ in examples). The PHP
scripts are right below the tests. Looking at the results I would say
there is almost no performance loss.

Before I place a feature request at PHP.net I would love to hear some
feedback here, or even a yes/no for this feature.

Best Regards,
Igor Feghali.

=== TEST 1 ===
$ time ./php53 /tmp/more_small.php
real    0m0.257s
user    0m0.223s
sys     0m0.032s

$ time ./php53 /tmp/more_small.php
real    0m0.258s
user    0m0.224s
sys     0m0.032s

$ time ./php53 /tmp/more_small.php
real    0m0.260s
user    0m0.225s
sys     0m0.032s

$ time ./php+ /tmp/more_small.php
real    0m0.261s
user    0m0.226s
sys     0m0.033s

$ time ./php+ /tmp/more_small.php
real    0m0.258s
user    0m0.224s
sys     0m0.032s

$ time ./php+ /tmp/more_small.php
real    0m0.260s
user    0m0.225s
sys     0m0.033s


=== TEST 2 ===
$ time ./php53 /tmp/less_big.php
real    0m0.328s
user    0m0.205s
sys     0m0.120s

$ time ./php53 /tmp/less_big.php
real    0m0.328s
user    0m0.206s
sys     0m0.120s

$ time ./php53 /tmp/less_big.php
real    0m0.326s
user    0m0.205s
sys     0m0.119s

$ time ./php+ /tmp/less_big.php
real    0m0.330s
user    0m0.206s
sys     0m0.122s

$ time ./php+ /tmp/less_big.php
real    0m0.330s
user    0m0.206s
sys     0m0.121s

$ time ./php+ /tmp/less_big.php
real    0m0.333s
user    0m0.207s
sys     0m0.124s


=== TEST 3 ===
$ time ./php+ /tmp/lots_null.php
real    0m0.263s
user    0m0.229s
sys     0m0.032s

$ time ./php+ /tmp/lots_null.php
real    0m0.261s
user    0m0.227s
sys     0m0.032s

$ time ./php+ /tmp/lots_null.php
real    0m0.260s
user    0m0.226s
sys     0m0.032s

$ time ./php+ /tmp/no_null.php
real    0m0.259s
user    0m0.226s
sys     0m0.032s

$ time ./php+ /tmp/no_null.php
real    0m0.257s
user    0m0.223s
sys     0m0.031s

$ time ./php+ /tmp/no_null.php
real    0m0.264s
user    0m0.229s
sys     0m0.032s

=== SCRIPTS ===
less_big.php
<?php

$str = str_repeat(str_repeat('foo', 1000).',', 11345);
$arr = explode(',', $str);

$out = implode(',', $arr);

lots_null.php
<?php

$str = str_repeat('foo,,', 99999);
$arr = explode(',', $str);

$out = implode(',', $arr);

more_small.php
<?php

$str = str_repeat('foo,bar,', 99999);
$arr = explode(',', $str);

$out = implode(',', $arr);

no_null.php
<?php

$str = str_repeat('foo,,', 99999);
$arr = explode(',', $str);

$out = implode(',', $arr, true);
Index: ext/standard/php_string.h
===================================================================
RCS file: /repository/php-src/ext/standard/php_string.h,v
retrieving revision 1.87.2.2.2.3.2.3
diff -u -r1.87.2.2.2.3.2.3 php_string.h
--- ext/standard/php_string.h	2 Nov 2008 18:24:34 -0000	1.87.2.2.2.3.2.3
+++ ext/standard/php_string.h	6 Dec 2008 01:52:42 -0000
@@ -137,7 +137,7 @@
 PHPAPI size_t php_strip_tags_ex(char *rbuf, int len, int *stateptr, char *allow, int allow_len, zend_bool allow_tag_spaces);
 PHPAPI int php_char_to_str_ex(char *str, uint len, char from, char *to, int to_len, zval *result, int case_sensitivity, int *replace_count);
 PHPAPI int php_char_to_str(char *str, uint len, char from, char *to, int to_len, zval *result);
-PHPAPI void php_implode(zval *delim, zval *arr, zval *return_value TSRMLS_DC);
+PHPAPI void php_implode(zval *delim, zval *arr, zval *skip_empty, zval *return_value TSRMLS_DC);
 PHPAPI void php_explode(zval *delim, zval *str, zval *return_value, int limit);
 
 PHPAPI size_t php_strspn(char *s1, char *s2, char *s1_end, char *s2_end); 
Index: ext/standard/string.c
===================================================================
RCS file: /repository/php-src/ext/standard/string.c,v
retrieving revision 1.445.2.14.2.69.2.38
diff -u -r1.445.2.14.2.69.2.38 string.c
--- ext/standard/string.c	21 Nov 2008 19:16:50 -0000	1.445.2.14.2.69.2.38
+++ ext/standard/string.c	6 Dec 2008 01:52:44 -0000
@@ -1033,30 +1033,28 @@
 }
 /* }}} */
 
-/* {{{ proto string join(array src, string glue)
+/* {{{ proto string join(array src, string glue[, boolean skip_empty])
    An alias for implode */
 /* }}} */
 
 /* {{{ php_implode
  */
-PHPAPI void php_implode(zval *delim, zval *arr, zval *return_value TSRMLS_DC) 
+PHPAPI void php_implode(zval *delim, zval *arr, zval *skip_empty, zval *return_value TSRMLS_DC) 
 {
 	zval         **tmp;
 	HashPosition   pos;
 	smart_str      implstr = {0};
-	int            numelems, i = 0;
-	zval tmp_val;
-	int str_len;
-
-	numelems = zend_hash_num_elements(Z_ARRVAL_P(arr));
+	zval           tmp_val;
+	int            str_len, len;
 
-	if (numelems == 0) {
+	if (zend_hash_num_elements(Z_ARRVAL_P(arr)) == 0) {
 		RETURN_EMPTY_STRING();
 	}
 
 	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos);
 
 	while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **) &tmp, &pos) == SUCCESS) {
+        len = implstr.len;
 		switch ((*tmp)->type) {
 			case IS_STRING:
 				smart_str_appendl(&implstr, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
@@ -1107,11 +1105,15 @@
 				
 		}
 
-		if (++i != numelems) {
-			smart_str_appendl(&implstr, Z_STRVAL_P(delim), Z_STRLEN_P(delim));
-		}
 		zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos);
+		if (Z_BVAL_P(skip_empty) && (implstr.len == len)) {
+			continue;
+		}
+        smart_str_appendl(&implstr, Z_STRVAL_P(delim), Z_STRLEN_P(delim));
 	}
+    if (implstr.len) {
+        implstr.len--;
+    }
 	smart_str_0(&implstr);
 
 	if (implstr.len) {
@@ -1123,14 +1125,14 @@
 }
 /* }}} */
 
-/* {{{ proto string implode([string glue,] array pieces)
+/* {{{ proto string implode([string glue,] array pieces[, boolean skip_empty])
    Joins array elements placing glue string between items and return one string */
 PHP_FUNCTION(implode)
 {
-	zval **arg1 = NULL, **arg2 = NULL, *delim, *arr;
+	zval **arg1 = NULL, **arg2 = NULL, **arg3 = NULL, *delim, *arr, *skip_empty;
 	HashPosition pos;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|Z", &arg1, &arg2) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|ZZ", &arg1, &arg2, &arg3) == FAILURE) {
 		return;
 	}
 	
@@ -1161,9 +1163,17 @@
 		}
 	}
 
+	if (arg3 != NULL) {
+		convert_to_boolean_ex(arg3);
+		skip_empty = *arg3;
+	} else {
+		MAKE_STD_ZVAL(skip_empty);
+		ZVAL_BOOL(skip_empty, 0);
+	}
+
 	pos = Z_ARRVAL_P(arr)->pInternalPointer;
 	
-	php_implode(delim, arr, return_value TSRMLS_CC);
+	php_implode(delim, arr, skip_empty, return_value TSRMLS_CC);
 
 	Z_ARRVAL_P(arr)->pInternalPointer = pos;
 
Index: tests/strings/001.phpt
===================================================================
RCS file: /repository/php-src/tests/strings/001.phpt,v
retrieving revision 1.3.4.1.4.1
diff -u -r1.3.4.1.4.1 001.phpt
--- tests/strings/001.phpt	5 Jun 2008 08:29:29 -0000	1.3.4.1.4.1
+++ tests/strings/001.phpt	6 Dec 2008 01:52:49 -0000
@@ -190,6 +190,18 @@
 	echo("failed!\n");
 }
 
+echo 'Testing implode: ';
+$foo = 'bar';
+$arr = array(1,'',2,null,3,0,4,true,5,false,false);
+
+$str1 = implode(',', $arr);
+$str2 = implode(',', $arr, $foo);
+
+if ($str1 == '1,,2,,3,0,4,1,5,,' && $str2 == '1,2,3,0,4,1,5' && $foo == 'bar') {
+	echo("passed\n");
+} else {
+	echo("failed!\n");
+}
 ?>
 --EXPECT--
 Testing strtok: passed
@@ -208,3 +220,5 @@
 Testing addslashes: passed
 Testing stripslashes: passed
 Testing uniqid: passed
+Testing implode: passed
+
-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to