Skip to content

Commit 37495c1

Browse files
changes after new PR review
1 parent fdfd3b1 commit 37495c1

2 files changed

Lines changed: 116 additions & 47 deletions

File tree

ext/standard/array.c

Lines changed: 96 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6914,7 +6914,7 @@ PHP_FUNCTION(array_key_exists)
69146914
/* }}} */
69156915

69166916
/* {{{ Helper function to get a nested value from array using an array of segments */
6917-
static zval* array_get_nested(HashTable *ht, HashTable *segments)
6917+
static zval* array_get_nested_from_hash(HashTable *ht, HashTable *segments)
69186918
{
69196919
zval *segment_val;
69206920
zval *current;
@@ -6964,14 +6964,65 @@ static zval* array_get_nested(HashTable *ht, HashTable *segments)
69646964
}
69656965
/* }}} */
69666966

6967+
/* {{{ Helper function to get a nested value from array using dot notation string */
6968+
static zval* array_get_nested_from_string(HashTable *ht, const char *key, size_t key_len)
6969+
{
6970+
const char *segment_start;
6971+
const char *dot;
6972+
size_t segment_len;
6973+
size_t remaining_len;
6974+
zval *current;
6975+
HashTable *current_ht;
6976+
zend_string *segment;
6977+
6978+
current_ht = ht;
6979+
segment_start = key;
6980+
remaining_len = key_len;
6981+
6982+
/* Iterate through each dot-separated segment */
6983+
while (remaining_len > 0) {
6984+
/* Find the next dot */
6985+
dot = memchr(segment_start, '.', remaining_len);
6986+
6987+
if (dot == NULL) {
6988+
/* Last segment */
6989+
segment_len = remaining_len;
6990+
} else {
6991+
segment_len = dot - segment_start;
6992+
}
6993+
6994+
/* Look up the current segment */
6995+
segment = zend_string_init(segment_start, segment_len, 0);
6996+
current = zend_symtable_find(current_ht, segment);
6997+
zend_string_release(segment);
6998+
6999+
/* If this is the last segment, return the result */
7000+
if (dot == NULL) {
7001+
return current;
7002+
}
7003+
7004+
/* Check if the segment exists and is an array for next iteration */
7005+
if (current == NULL || Z_TYPE_P(current) != IS_ARRAY) {
7006+
return NULL;
7007+
}
7008+
7009+
/* Move to the next segment */
7010+
current_ht = Z_ARRVAL_P(current);
7011+
segment_start = dot + 1;
7012+
remaining_len = remaining_len - segment_len - 1;
7013+
}
7014+
7015+
return NULL;
7016+
}
7017+
/* }}} */
7018+
69677019
/* {{{ Retrieves a value from a deeply nested array using "dot" notation */
69687020
PHP_FUNCTION(array_get)
69697021
{
69707022
zval *array;
69717023
zval *key = NULL;
69727024
zval *default_value = NULL;
69737025
zval *result;
6974-
zval segments_array;
69757026
HashTable *ht;
69767027

69777028
ZEND_PARSE_PARAMETERS_START(2, 3)
@@ -6988,41 +7039,42 @@ PHP_FUNCTION(array_get)
69887039

69897040
ht = Z_ARRVAL_P(array);
69907041

6991-
/* Handle array keys (array of segments) */
6992-
if (Z_TYPE_P(key) == IS_ARRAY) {
6993-
result = array_get_nested(ht, Z_ARRVAL_P(key));
7042+
switch (Z_TYPE_P(key)) {
7043+
case IS_ARRAY:
7044+
/* Handle array keys (array of segments) */
7045+
result = array_get_nested_from_hash(ht, Z_ARRVAL_P(key));
69947046

6995-
if (result != NULL) {
6996-
RETURN_COPY(result);
6997-
}
6998-
}
6999-
/* Handle string keys with dot notation - convert to array of segments */
7000-
else if (Z_TYPE_P(key) == IS_STRING) {
7001-
/* Use php_explode to split the string by '.' */
7002-
zend_string *delim = ZSTR_CHAR('.');
7003-
array_init(&segments_array);
7004-
php_explode(delim, Z_STR_P(key), &segments_array, ZEND_LONG_MAX);
7047+
if (result != NULL) {
7048+
RETURN_COPY_DEREF(result);
7049+
}
7050+
break;
70057051

7006-
result = array_get_nested(ht, Z_ARRVAL(segments_array));
7052+
case IS_STRING:
7053+
/* Handle string keys with dot notation */
7054+
result = array_get_nested_from_string(ht, Z_STRVAL_P(key), Z_STRLEN_P(key));
70077055

7008-
zval_ptr_dtor(&segments_array);
7056+
if (result != NULL) {
7057+
RETURN_COPY_DEREF(result);
7058+
}
7059+
break;
70097060

7010-
if (result != NULL) {
7011-
RETURN_COPY(result);
7012-
}
7013-
}
7014-
/* Handle integer keys (simple lookup) */
7015-
else if (Z_TYPE_P(key) == IS_LONG) {
7016-
result = zend_hash_index_find(ht, Z_LVAL_P(key));
7061+
case IS_LONG:
7062+
/* Handle integer keys (simple lookup) */
7063+
result = zend_hash_index_find(ht, Z_LVAL_P(key));
70177064

7018-
if (result != NULL) {
7019-
RETURN_COPY(result);
7020-
}
7065+
if (result != NULL) {
7066+
RETURN_COPY_DEREF(result);
7067+
}
7068+
break;
7069+
7070+
default:
7071+
zend_argument_type_error(2, "must be of type string|int|array, %s given", zend_zval_value_name(key));
7072+
RETURN_THROWS();
70217073
}
70227074

70237075
/* Key not found, return default value */
70247076
if (default_value != NULL) {
7025-
RETURN_COPY(default_value);
7077+
RETURN_COPY_DEREF(default_value);
70267078
}
70277079
}
70287080
/* }}} */
@@ -7033,7 +7085,6 @@ PHP_FUNCTION(array_has)
70337085
zval *array;
70347086
zval *key;
70357087
zval *result;
7036-
zval segments_array;
70377088
HashTable *ht;
70387089

70397090
ZEND_PARSE_PARAMETERS_START(2, 2)
@@ -7043,27 +7094,25 @@ PHP_FUNCTION(array_has)
70437094

70447095
ht = Z_ARRVAL_P(array);
70457096

7046-
/* Handle array keys (array of segments) */
7047-
if (Z_TYPE_P(key) == IS_ARRAY) {
7048-
result = array_get_nested(ht, Z_ARRVAL_P(key));
7049-
RETURN_BOOL(result != NULL);
7050-
}
7051-
/* Handle string keys with dot notation - convert to array of segments */
7052-
if (Z_TYPE_P(key) == IS_STRING) {
7053-
/* Use php_explode to split the string by '.' */
7054-
zend_string *delim = ZSTR_CHAR('.');
7055-
array_init(&segments_array);
7056-
php_explode(delim, Z_STR_P(key), &segments_array, ZEND_LONG_MAX);
7097+
switch (Z_TYPE_P(key)) {
7098+
case IS_ARRAY:
7099+
/* Handle array keys (array of segments) */
7100+
result = array_get_nested_from_hash(ht, Z_ARRVAL_P(key));
7101+
RETURN_BOOL(result != NULL);
70577102

7058-
result = array_get_nested(ht, Z_ARRVAL(segments_array));
7103+
case IS_STRING:
7104+
/* Handle string keys with dot notation */
7105+
result = array_get_nested_from_string(ht, Z_STRVAL_P(key), Z_STRLEN_P(key));
7106+
RETURN_BOOL(result != NULL);
70597107

7060-
zval_ptr_dtor(&segments_array);
7061-
RETURN_BOOL(result != NULL);
7062-
}
7108+
case IS_LONG:
7109+
/* Handle integer keys (simple lookup) */
7110+
RETURN_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
70637111

7064-
/* Handle integer keys (simple lookup) */
7065-
ZEND_ASSERT(Z_TYPE_P(key) == IS_LONG);
7066-
RETURN_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
7112+
default:
7113+
zend_argument_type_error(2, "must be of type string|int|array, %s given", zend_zval_value_name(key));
7114+
RETURN_THROWS();
7115+
}
70677116
}
70687117
/* }}} */
70697118

ext/standard/tests/array/array_get.phpt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ var_dump(array_get($array, ['products', 'chair', 'price'], 75));
5151
// Test with invalid segment type in array key
5252
var_dump(array_get($array, ['products', new stdClass(), 'price'], 'invalid'));
5353

54+
// Test with references - ensure returned value is a copy, not a reference
55+
$ref_array = ['data' => ['value' => 'original']];
56+
$ref =& $ref_array['data']['value'];
57+
$result = array_get($ref_array, 'data.value');
58+
var_dump($result);
59+
$ref = 'modified';
60+
var_dump($result); // Should still be 'original' (not affected by reference change)
61+
62+
// Test with default value being a reference
63+
$default_value = 'default';
64+
$default_ref =& $default_value;
65+
$result_with_ref_default = array_get($ref_array, 'missing.key', $default_ref);
66+
var_dump($result_with_ref_default);
67+
$default_value = 'changed';
68+
var_dump($result_with_ref_default); // Should still be 'default' (not affected by reference change)
69+
5470
echo "Done";
5571
?>
5672
--EXPECT--
@@ -74,4 +90,8 @@ string(4) "John"
7490
string(5) "Alice"
7591
int(75)
7692
string(7) "invalid"
93+
string(8) "original"
94+
string(8) "original"
95+
string(7) "default"
96+
string(7) "default"
7797
Done

0 commit comments

Comments
 (0)