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