Skip to content

Commit 1162676

Browse files
committed
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.
1 parent cc2147d commit 1162676

File tree

4 files changed

+360
-0
lines changed

4 files changed

+360
-0
lines changed

ext/standard/array.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5531,6 +5531,125 @@ PHP_FUNCTION(array_combine)
55315531
}
55325532
/* }}} */
55335533

5534+
/* {{{ php_array_key_index_common
5535+
offset_type: 0: determine from parameters; 1 = first; -1 = last
5536+
*/
5537+
static void php_array_key_index_common(INTERNAL_FUNCTION_PARAMETERS, int offset_type)
5538+
{
5539+
int argc;
5540+
zval *input, *outValue;
5541+
int key_offset;
5542+
int writeValue;
5543+
zval *arrValue;
5544+
HashPosition pos;
5545+
int key_type;
5546+
zend_string *string_key;
5547+
zend_ulong num_key;
5548+
zend_bool direction;
5549+
5550+
argc = ZEND_NUM_ARGS();
5551+
5552+
if(offset_type == 0) {
5553+
key_offset = 0;
5554+
5555+
//array_key_index(array input, int offset, [mixed value])
5556+
if(zend_parse_parameters(argc TSRMLS_CC, "al|z/", &input, &key_offset, &outValue) == FAILURE) {
5557+
RETURN_NULL();
5558+
}
5559+
5560+
writeValue = (argc >= 3);
5561+
} else {
5562+
if(offset_type == 1) {
5563+
key_offset = 0;
5564+
} else {
5565+
key_offset = offset_type;
5566+
}
5567+
5568+
//array_key_(first|last)(array input, [mixed value])
5569+
if(zend_parse_parameters(argc TSRMLS_CC, "a|z/", &input, &outValue) == FAILURE) {
5570+
RETURN_NULL();
5571+
}
5572+
5573+
writeValue = (argc >= 2);
5574+
}
5575+
5576+
//true == forward; false == backwards
5577+
direction = (key_offset >= 0);
5578+
if(!direction) {
5579+
key_offset = abs(key_offset) - 1;
5580+
}
5581+
5582+
if(key_offset >= zend_hash_num_elements(Z_ARRVAL_P(input))) {
5583+
RETURN_NULL();
5584+
}
5585+
5586+
if(direction) {
5587+
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
5588+
} else {
5589+
zend_hash_internal_pointer_end_ex(Z_ARRVAL_P(input), &pos);
5590+
}
5591+
5592+
if(key_offset >= 1) {
5593+
while(key_offset--) {
5594+
if(direction) {
5595+
zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
5596+
} else {
5597+
zend_hash_move_backwards_ex(Z_ARRVAL_P(input), &pos);
5598+
}
5599+
}
5600+
}
5601+
5602+
key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &num_key, &pos);
5603+
5604+
switch(key_type) {
5605+
case HASH_KEY_IS_LONG:
5606+
RETVAL_LONG(num_key);
5607+
break;
5608+
5609+
case HASH_KEY_IS_STRING:
5610+
RETVAL_STR_COPY(string_key);
5611+
break;
5612+
5613+
case HASH_KEY_NON_EXISTENT:
5614+
default:
5615+
RETURN_NULL();
5616+
break;
5617+
}
5618+
5619+
if(writeValue) {
5620+
zval_dtor(outValue);
5621+
5622+
arrValue = zend_hash_get_current_data_ex(Z_ARRVAL_P(input), &pos);
5623+
5624+
ZVAL_COPY_VALUE(outValue, arrValue);
5625+
}
5626+
}
5627+
/* }}} */
5628+
5629+
/* {{{ proto mixed array_key_index(array input, int index [, mixed value])
5630+
Return the array's index key (and optionally, value) */
5631+
PHP_FUNCTION(array_key_index)
5632+
{
5633+
php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
5634+
}
5635+
/* }}} */
5636+
5637+
/* {{{ proto mixed array_key_first(array input [, mixed value])
5638+
Return the array's first key (and optionally, value) */
5639+
PHP_FUNCTION(array_key_first)
5640+
{
5641+
php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
5642+
}
5643+
/* }}} */
5644+
5645+
/* {{{ proto mixed array_key_last(array input [, mixed value])
5646+
Return the array's last key (and optionally, value) */
5647+
PHP_FUNCTION(array_key_last)
5648+
{
5649+
php_array_key_index_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1);
5650+
}
5651+
/* }}} */
5652+
55345653
/*
55355654
* Local variables:
55365655
* tab-width: 4

ext/standard/basic_functions.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,19 @@ ZEND_BEGIN_ARG_INFO(arginfo_array_combine, 0)
601601
ZEND_ARG_INFO(0, keys) /* ARRAY_INFO(0, keys, 0) */
602602
ZEND_ARG_INFO(0, values) /* ARRAY_INFO(0, values, 0) */
603603
ZEND_END_ARG_INFO()
604+
605+
static
606+
ZEND_BEGIN_ARG_INFO(arginfo_array_key_index, 0)
607+
ZEND_ARG_INFO(0, input)
608+
ZEND_ARG_INFO(0, index)
609+
ZEND_ARG_INFO(1, value)
610+
ZEND_END_ARG_INFO()
611+
612+
static
613+
ZEND_BEGIN_ARG_INFO(arginfo_array_key_first, 0)
614+
ZEND_ARG_INFO(0, input)
615+
ZEND_ARG_INFO(1, value)
616+
ZEND_END_ARG_INFO()
604617
/* }}} */
605618
/* {{{ basic_functions.c */
606619
ZEND_BEGIN_ARG_INFO(arginfo_get_magic_quotes_gpc, 0)
@@ -3341,6 +3354,9 @@ const zend_function_entry basic_functions[] = { /* {{{ */
33413354
PHP_FE(array_chunk, arginfo_array_chunk)
33423355
PHP_FE(array_combine, arginfo_array_combine)
33433356
PHP_FE(array_key_exists, arginfo_array_key_exists)
3357+
PHP_FE(array_key_index, arginfo_array_key_index)
3358+
PHP_FE(array_key_first, arginfo_array_key_first)
3359+
PHP_FE(array_key_last, arginfo_array_key_first)
33443360

33453361
/* aliases from array.c */
33463362
PHP_FALIAS(pos, current, arginfo_current)

ext/standard/php_array.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ PHP_FUNCTION(array_map);
102102
PHP_FUNCTION(array_key_exists);
103103
PHP_FUNCTION(array_chunk);
104104
PHP_FUNCTION(array_combine);
105+
PHP_FUNCTION(array_key_index);
106+
PHP_FUNCTION(array_key_first);
107+
PHP_FUNCTION(array_key_last);
105108

106109
PHPAPI int php_array_merge(HashTable *dest, HashTable *src);
107110
PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src);
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
--TEST--
2+
array_key_first, array_key_last, array_key_index
3+
--FILE--
4+
<?php
5+
6+
function array_key_index_test($arr)
7+
{
8+
reset($arr);
9+
next($arr); //skip to second parameter
10+
11+
$key = array_key_first($arr);
12+
var_dump($key);
13+
echo current($arr) . "\n";
14+
echo "\n";
15+
16+
17+
$key = array_key_last($arr);
18+
var_dump($key);
19+
echo current($arr) . "\n";
20+
echo "\n";
21+
22+
$key = array_key_index($arr, 1);
23+
24+
var_dump($key);
25+
echo current($arr) . "\n";
26+
echo "\n";
27+
28+
$key = array_key_index($arr, -3);
29+
30+
var_dump($key);
31+
echo current($arr) . "\n";
32+
echo "\n";
33+
34+
$key = array_key_index($arr, 3);
35+
var_dump($key);
36+
echo current($arr) . "\n";
37+
38+
echo "\n\n";
39+
}
40+
41+
function array_key_index_test_value($arr)
42+
{
43+
reset($arr);
44+
next($arr); //skip to second parameter
45+
46+
$value = array('value has not been written to');
47+
48+
$key = array_key_first($arr, $value);
49+
var_dump($key);
50+
var_dump($value);
51+
echo current($arr) . "\n";
52+
echo "\n";
53+
54+
55+
$key = array_key_last($arr, $value);
56+
var_dump($key);
57+
var_dump($value);
58+
echo current($arr) . "\n";
59+
echo "\n";
60+
61+
$key = array_key_index($arr, 1, $value);
62+
63+
var_dump($key);
64+
var_dump($value);
65+
echo current($arr) . "\n";
66+
echo "\n";
67+
68+
$key = array_key_index($arr, -3, $value);
69+
70+
var_dump($key);
71+
var_dump($value);
72+
echo current($arr) . "\n";
73+
echo "\n";
74+
75+
$key = array_key_index($arr, 3, $value);
76+
var_dump($key);
77+
var_dump($value);
78+
echo current($arr) . "\n";
79+
80+
echo "\n\n";
81+
}
82+
83+
echo "test with string array\n";
84+
$arr = array(
85+
'one' => 'first',
86+
'two' => 'middle',
87+
'three' => 'last',
88+
);
89+
array_key_index_test($arr);
90+
array_key_index_test_value($arr);
91+
92+
echo "test with numeric array\n";
93+
$arr = array(
94+
1 => 'first',
95+
2 => 'middle',
96+
3 => 'last',
97+
);
98+
array_key_index_test($arr);
99+
array_key_index_test_value($arr);
100+
101+
echo "test with mixed array\n";
102+
$arr = array(
103+
1 => 'first',
104+
'second' => 'middle',
105+
3 => 'last',
106+
);
107+
array_key_index_test($arr);
108+
array_key_index_test_value($arr);
109+
?>
110+
--EXPECT--
111+
test with string array
112+
string(3) "one"
113+
middle
114+
115+
string(5) "three"
116+
middle
117+
118+
string(3) "two"
119+
middle
120+
121+
string(3) "one"
122+
middle
123+
124+
NULL
125+
middle
126+
127+
128+
string(3) "one"
129+
string(5) "first"
130+
middle
131+
132+
string(5) "three"
133+
string(4) "last"
134+
middle
135+
136+
string(3) "two"
137+
string(6) "middle"
138+
middle
139+
140+
string(3) "one"
141+
string(5) "first"
142+
middle
143+
144+
NULL
145+
string(5) "first"
146+
middle
147+
148+
149+
test with numeric array
150+
int(1)
151+
middle
152+
153+
int(3)
154+
middle
155+
156+
int(2)
157+
middle
158+
159+
int(1)
160+
middle
161+
162+
NULL
163+
middle
164+
165+
166+
int(1)
167+
string(5) "first"
168+
middle
169+
170+
int(3)
171+
string(4) "last"
172+
middle
173+
174+
int(2)
175+
string(6) "middle"
176+
middle
177+
178+
int(1)
179+
string(5) "first"
180+
middle
181+
182+
NULL
183+
string(5) "first"
184+
middle
185+
186+
187+
test with mixed array
188+
int(1)
189+
middle
190+
191+
int(3)
192+
middle
193+
194+
string(6) "second"
195+
middle
196+
197+
int(1)
198+
middle
199+
200+
NULL
201+
middle
202+
203+
204+
int(1)
205+
string(5) "first"
206+
middle
207+
208+
int(3)
209+
string(4) "last"
210+
middle
211+
212+
string(6) "second"
213+
string(6) "middle"
214+
middle
215+
216+
int(1)
217+
string(5) "first"
218+
middle
219+
220+
NULL
221+
string(5) "first"
222+
middle

0 commit comments

Comments
 (0)