From 116267687678e3b01004385e98630276973c21c9 Mon Sep 17 00:00:00 2001 From: John Bafford Date: Mon, 29 Oct 2012 21:07:10 -0400 Subject: [PATCH] Add array_key_first, array_key_last, array_key_index Add three new functions: $key = array_key_first($array [, &$value]) $key = array_key_last($array [, &$value]) $key = array_key_index($array, $index [, &$value]) array_key_first() and array_key_last() respectively return the first and last key in an array, and optionally, the value. array_key_index() returns the key belonging to the zero-based index specified, with the ability to search from the end of the array if a negative index is passed. These functions do this without mutating the source arrays (so, they do not modify the internal array pointer.) This allows you to do the equivalent of array_keys($arr)[0] (or foreach($arr as $k => $v) { break; }) with less overhead. --- ext/standard/array.c | 119 ++++++++++ ext/standard/basic_functions.c | 16 ++ ext/standard/php_array.h | 3 + ext/standard/tests/array/array_key_index.phpt | 222 ++++++++++++++++++ 4 files changed, 360 insertions(+) create mode 100644 ext/standard/tests/array/array_key_index.phpt diff --git a/ext/standard/array.c b/ext/standard/array.c index 79c9ab6207ad..0fff8b344388 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -5531,6 +5531,125 @@ PHP_FUNCTION(array_combine) } /* }}} */ +/* {{{ php_array_key_index_common + offset_type: 0: determine from parameters; 1 = first; -1 = last + */ +static void php_array_key_index_common(INTERNAL_FUNCTION_PARAMETERS, int offset_type) +{ + int argc; + zval *input, *outValue; + int key_offset; + int writeValue; + zval *arrValue; + HashPosition pos; + int key_type; + zend_string *string_key; + zend_ulong num_key; + zend_bool direction; + + argc = ZEND_NUM_ARGS(); + + if(offset_type == 0) { + key_offset = 0; + + //array_key_index(array input, int offset, [mixed value]) + if(zend_parse_parameters(argc TSRMLS_CC, "al|z/", &input, &key_offset, &outValue) == FAILURE) { + RETURN_NULL(); + } + + writeValue = (argc >= 3); + } else { + if(offset_type == 1) { + key_offset = 0; + } else { + key_offset = offset_type; + } + + //array_key_(first|last)(array input, [mixed value]) + if(zend_parse_parameters(argc TSRMLS_CC, "a|z/", &input, &outValue) == FAILURE) { + RETURN_NULL(); + } + + writeValue = (argc >= 2); + } + + //true == forward; false == backwards + direction = (key_offset >= 0); + if(!direction) { + key_offset = abs(key_offset) - 1; + } + + if(key_offset >= 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(key_offset >= 1) { + while(key_offset--) { + 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, &num_key, &pos); + + switch(key_type) { + case HASH_KEY_IS_LONG: + RETVAL_LONG(num_key); + break; + + case HASH_KEY_IS_STRING: + RETVAL_STR_COPY(string_key); + break; + + case HASH_KEY_NON_EXISTENT: + default: + RETURN_NULL(); + break; + } + + if(writeValue) { + zval_dtor(outValue); + + arrValue = zend_hash_get_current_data_ex(Z_ARRVAL_P(input), &pos); + + ZVAL_COPY_VALUE(outValue, arrValue); + } +} +/* }}} */ + +/* {{{ 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_key_first(array input [, mixed value]) + Return the array's first key (and optionally, value) */ +PHP_FUNCTION(array_key_first) +{ + php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto mixed array_key_last(array input [, mixed value]) + Return the array's last key (and optionally, value) */ +PHP_FUNCTION(array_key_last) +{ + php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1); +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 5334e26439b7..adc7018496c3 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -601,6 +601,19 @@ ZEND_BEGIN_ARG_INFO(arginfo_array_combine, 0) 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_key_first, 0) + ZEND_ARG_INFO(0, input) + ZEND_ARG_INFO(1, value) +ZEND_END_ARG_INFO() /* }}} */ /* {{{ basic_functions.c */ ZEND_BEGIN_ARG_INFO(arginfo_get_magic_quotes_gpc, 0) @@ -3341,6 +3354,9 @@ const zend_function_entry basic_functions[] = { /* {{{ */ 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_key_first, arginfo_array_key_first) + PHP_FE(array_key_last, arginfo_array_key_first) /* aliases from array.c */ PHP_FALIAS(pos, current, arginfo_current) diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index ba2490c98b84..e5fbd6cf672e 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -102,6 +102,9 @@ PHP_FUNCTION(array_map); PHP_FUNCTION(array_key_exists); PHP_FUNCTION(array_chunk); PHP_FUNCTION(array_combine); +PHP_FUNCTION(array_key_index); +PHP_FUNCTION(array_key_first); +PHP_FUNCTION(array_key_last); PHPAPI int php_array_merge(HashTable *dest, HashTable *src); PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src); diff --git a/ext/standard/tests/array/array_key_index.phpt b/ext/standard/tests/array/array_key_index.phpt new file mode 100644 index 000000000000..119c50444a03 --- /dev/null +++ b/ext/standard/tests/array/array_key_index.phpt @@ -0,0 +1,222 @@ +--TEST-- +array_key_first, array_key_last, array_key_index +--FILE-- + 'first', + 'two' => 'middle', + 'three' => 'last', +); +array_key_index_test($arr); +array_key_index_test_value($arr); + +echo "test with numeric array\n"; +$arr = array( + 1 => 'first', + 2 => 'middle', + 3 => 'last', +); +array_key_index_test($arr); +array_key_index_test_value($arr); + +echo "test with mixed array\n"; +$arr = array( + 1 => 'first', + 'second' => 'middle', + 3 => 'last', +); +array_key_index_test($arr); +array_key_index_test_value($arr); +?> +--EXPECT-- +test with string array +string(3) "one" +middle + +string(5) "three" +middle + +string(3) "two" +middle + +string(3) "one" +middle + +NULL +middle + + +string(3) "one" +string(5) "first" +middle + +string(5) "three" +string(4) "last" +middle + +string(3) "two" +string(6) "middle" +middle + +string(3) "one" +string(5) "first" +middle + +NULL +string(5) "first" +middle + + +test with numeric array +int(1) +middle + +int(3) +middle + +int(2) +middle + +int(1) +middle + +NULL +middle + + +int(1) +string(5) "first" +middle + +int(3) +string(4) "last" +middle + +int(2) +string(6) "middle" +middle + +int(1) +string(5) "first" +middle + +NULL +string(5) "first" +middle + + +test with mixed array +int(1) +middle + +int(3) +middle + +string(6) "second" +middle + +int(1) +middle + +NULL +middle + + +int(1) +string(5) "first" +middle + +int(3) +string(4) "last" +middle + +string(6) "second" +string(6) "middle" +middle + +int(1) +string(5) "first" +middle + +NULL +string(5) "first" +middle