[PATCH] New functions: array_key_index(), array_first(), array_last()
| From: | John Bafford | Date: | Fri, 05 Jan 2007 05:49:57 +0000 |
| Subject: | [PATCH] New functions: array_key_index(), array_first(), array_last() | ||
| Groups: | php.internals | ||
| Request: | Send a blank email to internals+get-27264@lists.php.net to get a copy of this message | ||
Hello,
Attached is a patch and corresponding test file I would like to submit for inclusion in PHP. I would like this patch to be included in PHP 5.2.1, as it is entirely new code (and thus shouldn't cause regressions), but as RC2 was just released earlier today, I understand if it must wait until PHP 5.2.2.
This patch implements three new functions, as described below.
mixed array_key_index(array input, int index [, mixed value])
Return the array's index'th key (and optionally, value)
mixed array_first(array input [, mixed value])
Return the array's first key (and optionally, value)
This is equivalent to array_key_index($input, 0 [, $value]);
mixed array_last(array input [, mixed value])
Return the array's last key (and optionally, value)
This is equivalent to array_key_index($input, -1 [, $value]);
array_first() is a non-destructive way to get the first key (and value, if requested) from an array. array_first() is intended to replace code similar to the following:
foreach($arr as $key => $val) break;
reset($arr); $key = key($arr);
reset($arr); list($key, $val) = each($arr);
$key = array_keys($arr); $key = $key[0];
array_last() is the obvious counterpart to array_first(), returning the last key (and value, if requested) from an array.
array_first() and array_last() are implemented via array_key_index(), which returns a key based on its order in the array. The index parameter functions similarly to substr()'s start parameter (>= 0 start from the beginning of the array, <= -1 start from the end.)
All three functions leave the original array unmodified. In the event of an error (invalid parameters, or attempting to seek past the end of the array), all three functions return NULL and leave $value unchanged.
Please let me know if you have any comments.
Thanks,
-John
Index: ext/standard/array.c =================================================================== RCS file: /repository/php-src/ext/standard/array.c,v retrieving revision 1.308.2.21.2.22 diff -u -u -r1.308.2.21.2.22 array.c --- ext/standard/array.c 1 Jan 2007 09:36:07 -0000 1.308.2.21.2.22 +++ ext/standard/array.c 5 Jan 2007 04:57:47 -0000 @@ -4528,6 +4528,115 @@ } /* }}} */ +/* {{{ php_array_key_index_common + keyOffset: 0: determine from parameters; 1 = first; -1 = last + */ +static void php_array_key_index_common(INTERNAL_FUNCTION_PARAMETERS, int keyOffset) +{ + zval *input, *value; + zval **arrValue; + HashPosition pos; + int key_type; + char *string_key; + uint string_key_len; + ulong num_key; + zend_bool direction; + + if(keyOffset == 0) { + //array_key_index(array input, int offset, [mixed value]) + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|z", &input, &keyOffset, &value) == FAILURE) { + RETURN_NULL(); + } + } else { + if(keyOffset == 1) { + keyOffset = 0; + } + + //array_(first|last)(array input, [mixed value]) + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &input, &value) == FAILURE) { + RETURN_NULL(); + } + } + + //true == forward; false == backwards + direction = (keyOffset >= 0); + if(!direction) { + keyOffset = abs(keyOffset) - 1; + } + + if(keyOffset >= zend_hash_num_elements(Z_ARRVAL_P(input))) { + RETURN_NULL(); + } + + if(direction) { + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos); + } else { + zend_hash_internal_pointer_end_ex(Z_ARRVAL_P(input), &pos); + } + + if(keyOffset >= 1) { + while(keyOffset--) { + if(direction) { + zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos); + } else { + zend_hash_move_backwards_ex(Z_ARRVAL_P(input), &pos); + } + } + } + + key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos); + + switch(key_type) { + case HASH_KEY_IS_LONG: + RETVAL_LONG(num_key); + break; + + case HASH_KEY_IS_STRING: + RETVAL_STRINGL(string_key, string_key_len - 1, 1); + break; + + case HASH_KEY_NON_EXISTANT: + default: + RETURN_NULL(); + break; + } + + if(ZEND_NUM_ARGS() >= 2) { + zval_dtor(value); + + zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void**)&arrValue, &pos); + + *value = **arrValue; + zval_copy_ctor(value); + ZVAL_ADDREF(value); + } +} +/* }}} */ + +/* {{{ proto mixed array_key_index(array input, int index [, mixed value]) + Return the array's index key (and optionally, value) */ +PHP_FUNCTION(array_key_index) +{ + php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto mixed array_first(array input [, mixed value]) + Return the array's first key (and optionally, value) */ +PHP_FUNCTION(array_first) +{ + php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto mixed array_last(array input [, mixed value]) + Return the array's last key (and optionally, value) */ +PHP_FUNCTION(array_last) +{ + php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1); +} +/* }}} */ + /* * Local variables: * tab-width: 4 Index: ext/standard/basic_functions.c =================================================================== RCS file: /repository/php-src/ext/standard/basic_functions.c,v retrieving revision 1.725.2.31.2.39 diff -u -u -r1.725.2.31.2.39 basic_functions.c --- ext/standard/basic_functions.c 1 Jan 2007 09:36:08 -0000 1.725.2.31.2.39 +++ ext/standard/basic_functions.c 5 Jan 2007 04:57:48 -0000 @@ -682,6 +682,19 @@ ZEND_ARG_INFO(0, keys) /* ARRAY_INFO(0, keys, 0) */ ZEND_ARG_INFO(0, values) /* ARRAY_INFO(0, values, 0) */ ZEND_END_ARG_INFO() + +static +ZEND_BEGIN_ARG_INFO(arginfo_array_key_index, 0) + ZEND_ARG_INFO(0, input) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(1, value) +ZEND_END_ARG_INFO() + +static +ZEND_BEGIN_ARG_INFO(arginfo_array_first, 0) + ZEND_ARG_INFO(0, input) + ZEND_ARG_INFO(1, value) +ZEND_END_ARG_INFO() /* }}} */ /* {{{ basic_functions.c */ static @@ -3743,6 +3756,9 @@ PHP_FE(array_chunk, arginfo_array_chunk) PHP_FE(array_combine, arginfo_array_combine) PHP_FE(array_key_exists, arginfo_array_key_exists) + PHP_FE(array_key_index, arginfo_array_key_index) + PHP_FE(array_first, arginfo_array_first) + PHP_FE(array_last, arginfo_array_first) /* aliases from array.c */ PHP_FALIAS(pos, current, arginfo_current) Index: ext/standard/php_array.h =================================================================== RCS file: /repository/php-src/ext/standard/php_array.h,v retrieving revision 1.50.2.2.2.3 diff -u -u -r1.50.2.2.2.3 php_array.h --- ext/standard/php_array.h 1 Jan 2007 09:36:08 -0000 1.50.2.2.2.3 +++ ext/standard/php_array.h 5 Jan 2007 04:57:48 -0000 @@ -99,6 +99,9 @@ PHP_FUNCTION(array_key_exists); PHP_FUNCTION(array_chunk); PHP_FUNCTION(array_combine); +PHP_FUNCTION(array_key_index); +PHP_FUNCTION(array_first); +PHP_FUNCTION(array_last); HashTable* php_splice(HashTable *, int, int, zval ***, int, HashTable **); PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC); --TEST-- Test array_key_index(), array_first(), and array_last() --FILE-- <?php $arr = array( 'one' => 'first', 'two' => 'middle', 'three' => 'last', ); $value = array('value has not been written to'); $key = array_first($arr, $value); echo "Testing array_first():\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_last($arr, $value); echo "Testing array_last():\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_key_index($arr, 1, $value); echo "Testing array_index() with positive offset:\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_key_index($arr, -3, $value); echo "Testing array_index() with negative offset:\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_key_index($arr, 3, $value); echo "Testing array_index() with positive offset past the end of the array:\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_key_index($arr, -4, $value); echo "Testing array_index() with negative offset past the end of the array:\n"; var_dump($key); var_dump($value); ?> --EXPECT-- Testing array_first(): string(3) "one" string(5) "first" Testing array_last(): string(5) "three" string(4) "last" Testing array_index() with positive offset: string(3) "two" string(6) "middle" Testing array_index() with negative offset: string(3) "one" string(5) "first" Testing array_index() with positive offset past the end of the array: NULL string(5) "first" Testing array_index() with negative offset past the end of the array: NULL string(5) "first"
Index: ext/standard/array.c =================================================================== RCS file: /repository/php-src/ext/standard/array.c,v retrieving revision 1.308.2.21.2.22 diff -u -u -r1.308.2.21.2.22 array.c --- ext/standard/array.c 1 Jan 2007 09:36:07 -0000 1.308.2.21.2.22 +++ ext/standard/array.c 5 Jan 2007 04:57:47 -0000 @@ -4528,6 +4528,115 @@ } /* }}} */ +/* {{{ php_array_key_index_common + keyOffset: 0: determine from parameters; 1 = first; -1 = last + */ +static void php_array_key_index_common(INTERNAL_FUNCTION_PARAMETERS, int keyOffset) +{ + zval *input, *value; + zval **arrValue; + HashPosition pos; + int key_type; + char *string_key; + uint string_key_len; + ulong num_key; + zend_bool direction; + + if(keyOffset == 0) { + //array_key_index(array input, int offset, [mixed value]) + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|z", &input, &keyOffset, &value) == FAILURE) { + RETURN_NULL(); + } + } else { + if(keyOffset == 1) { + keyOffset = 0; + } + + //array_(first|last)(array input, [mixed value]) + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &input, &value) == FAILURE) { + RETURN_NULL(); + } + } + + //true == forward; false == backwards + direction = (keyOffset >= 0); + if(!direction) { + keyOffset = abs(keyOffset) - 1; + } + + if(keyOffset >= zend_hash_num_elements(Z_ARRVAL_P(input))) { + RETURN_NULL(); + } + + if(direction) { + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos); + } else { + zend_hash_internal_pointer_end_ex(Z_ARRVAL_P(input), &pos); + } + + if(keyOffset >= 1) { + while(keyOffset--) { + if(direction) { + zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos); + } else { + zend_hash_move_backwards_ex(Z_ARRVAL_P(input), &pos); + } + } + } + + key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos); + + switch(key_type) { + case HASH_KEY_IS_LONG: + RETVAL_LONG(num_key); + break; + + case HASH_KEY_IS_STRING: + RETVAL_STRINGL(string_key, string_key_len - 1, 1); + break; + + case HASH_KEY_NON_EXISTANT: + default: + RETURN_NULL(); + break; + } + + if(ZEND_NUM_ARGS() >= 2) { + zval_dtor(value); + + zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void**)&arrValue, &pos); + + *value = **arrValue; + zval_copy_ctor(value); + ZVAL_ADDREF(value); + } +} +/* }}} */ + +/* {{{ proto mixed array_key_index(array input, int index [, mixed value]) + Return the array's index key (and optionally, value) */ +PHP_FUNCTION(array_key_index) +{ + php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto mixed array_first(array input [, mixed value]) + Return the array's first key (and optionally, value) */ +PHP_FUNCTION(array_first) +{ + php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto mixed array_last(array input [, mixed value]) + Return the array's last key (and optionally, value) */ +PHP_FUNCTION(array_last) +{ + php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1); +} +/* }}} */ + /* * Local variables: * tab-width: 4 Index: ext/standard/basic_functions.c =================================================================== RCS file: /repository/php-src/ext/standard/basic_functions.c,v retrieving revision 1.725.2.31.2.39 diff -u -u -r1.725.2.31.2.39 basic_functions.c --- ext/standard/basic_functions.c 1 Jan 2007 09:36:08 -0000 1.725.2.31.2.39 +++ ext/standard/basic_functions.c 5 Jan 2007 04:57:48 -0000 @@ -682,6 +682,19 @@ ZEND_ARG_INFO(0, keys) /* ARRAY_INFO(0, keys, 0) */ ZEND_ARG_INFO(0, values) /* ARRAY_INFO(0, values, 0) */ ZEND_END_ARG_INFO() + +static +ZEND_BEGIN_ARG_INFO(arginfo_array_key_index, 0) + ZEND_ARG_INFO(0, input) + ZEND_ARG_INFO(0, index) + ZEND_ARG_INFO(1, value) +ZEND_END_ARG_INFO() + +static +ZEND_BEGIN_ARG_INFO(arginfo_array_first, 0) + ZEND_ARG_INFO(0, input) + ZEND_ARG_INFO(1, value) +ZEND_END_ARG_INFO() /* }}} */ /* {{{ basic_functions.c */ static @@ -3743,6 +3756,9 @@ PHP_FE(array_chunk, arginfo_array_chunk) PHP_FE(array_combine, arginfo_array_combine) PHP_FE(array_key_exists, arginfo_array_key_exists) + PHP_FE(array_key_index, arginfo_array_key_index) + PHP_FE(array_first, arginfo_array_first) + PHP_FE(array_last, arginfo_array_first) /* aliases from array.c */ PHP_FALIAS(pos, current, arginfo_current) Index: ext/standard/php_array.h =================================================================== RCS file: /repository/php-src/ext/standard/php_array.h,v retrieving revision 1.50.2.2.2.3 diff -u -u -r1.50.2.2.2.3 php_array.h --- ext/standard/php_array.h 1 Jan 2007 09:36:08 -0000 1.50.2.2.2.3 +++ ext/standard/php_array.h 5 Jan 2007 04:57:48 -0000 @@ -99,6 +99,9 @@ PHP_FUNCTION(array_key_exists); PHP_FUNCTION(array_chunk); PHP_FUNCTION(array_combine); +PHP_FUNCTION(array_key_index); +PHP_FUNCTION(array_first); +PHP_FUNCTION(array_last); HashTable* php_splice(HashTable *, int, int, zval ***, int, HashTable **); PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC); --TEST-- Test array_key_index(), array_first(), and array_last() --FILE-- <?php $arr = array( 'one' => 'first', 'two' => 'middle', 'three' => 'last', ); $value = array('value has not been written to'); $key = array_first($arr, $value); echo "Testing array_first():\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_last($arr, $value); echo "Testing array_last():\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_key_index($arr, 1, $value); echo "Testing array_index() with positive offset:\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_key_index($arr, -3, $value); echo "Testing array_index() with negative offset:\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_key_index($arr, 3, $value); echo "Testing array_index() with positive offset past the end of the array:\n"; var_dump($key); var_dump($value); echo "\n"; $key = array_key_index($arr, -4, $value); echo "Testing array_index() with negative offset past the end of the array:\n"; var_dump($key); var_dump($value); ?> --EXPECT-- Testing array_first(): string(3) "one" string(5) "first" Testing array_last(): string(5) "three" string(4) "last" Testing array_index() with positive offset: string(3) "two" string(6) "middle" Testing array_index() with negative offset: string(3) "one" string(5) "first" Testing array_index() with positive offset past the end of the array: NULL string(5) "first" Testing array_index() with negative offset past the end of the array: NULL string(5) "first"