Skip to content

Commit cb4855d

Browse files
changes after new PR review
1 parent 10037a7 commit cb4855d

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
@@ -6916,7 +6916,7 @@ PHP_FUNCTION(array_key_exists)
69166916
/* }}} */
69176917

69186918
/* {{{ Helper function to get a nested value from array using an array of segments */
6919-
static zval* array_get_nested(HashTable *ht, HashTable *segments)
6919+
static zval* array_get_nested_from_hash(HashTable *ht, HashTable *segments)
69206920
{
69216921
zval *segment_val;
69226922
zval *current;
@@ -6966,14 +6966,65 @@ static zval* array_get_nested(HashTable *ht, HashTable *segments)
69666966
}
69676967
/* }}} */
69686968

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

69797030
ZEND_PARSE_PARAMETERS_START(2, 3)
@@ -6990,41 +7041,42 @@ PHP_FUNCTION(array_get)
69907041

69917042
ht = Z_ARRVAL_P(array);
69927043

6993-
/* Handle array keys (array of segments) */
6994-
if (Z_TYPE_P(key) == IS_ARRAY) {
6995-
result = array_get_nested(ht, Z_ARRVAL_P(key));
7044+
switch (Z_TYPE_P(key)) {
7045+
case IS_ARRAY:
7046+
/* Handle array keys (array of segments) */
7047+
result = array_get_nested_from_hash(ht, Z_ARRVAL_P(key));
69967048

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

7008-
result = array_get_nested(ht, Z_ARRVAL(segments_array));
7054+
case IS_STRING:
7055+
/* Handle string keys with dot notation */
7056+
result = array_get_nested_from_string(ht, Z_STRVAL_P(key), Z_STRLEN_P(key));
70097057

7010-
zval_ptr_dtor(&segments_array);
7058+
if (result != NULL) {
7059+
RETURN_COPY_DEREF(result);
7060+
}
7061+
break;
70117062

7012-
if (result != NULL) {
7013-
RETURN_COPY(result);
7014-
}
7015-
}
7016-
/* Handle integer keys (simple lookup) */
7017-
else if (Z_TYPE_P(key) == IS_LONG) {
7018-
result = zend_hash_index_find(ht, Z_LVAL_P(key));
7063+
case IS_LONG:
7064+
/* Handle integer keys (simple lookup) */
7065+
result = zend_hash_index_find(ht, Z_LVAL_P(key));
70197066

7020-
if (result != NULL) {
7021-
RETURN_COPY(result);
7022-
}
7067+
if (result != NULL) {
7068+
RETURN_COPY_DEREF(result);
7069+
}
7070+
break;
7071+
7072+
default:
7073+
zend_argument_type_error(2, "must be of type string|int|array, %s given", zend_zval_value_name(key));
7074+
RETURN_THROWS();
70237075
}
70247076

70257077
/* Key not found, return default value */
70267078
if (default_value != NULL) {
7027-
RETURN_COPY(default_value);
7079+
RETURN_COPY_DEREF(default_value);
70287080
}
70297081
}
70307082
/* }}} */
@@ -7035,7 +7087,6 @@ PHP_FUNCTION(array_has)
70357087
zval *array;
70367088
zval *key;
70377089
zval *result;
7038-
zval segments_array;
70397090
HashTable *ht;
70407091

70417092
ZEND_PARSE_PARAMETERS_START(2, 2)
@@ -7045,27 +7096,25 @@ PHP_FUNCTION(array_has)
70457096

70467097
ht = Z_ARRVAL_P(array);
70477098

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

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

7062-
zval_ptr_dtor(&segments_array);
7063-
RETURN_BOOL(result != NULL);
7064-
}
7110+
case IS_LONG:
7111+
/* Handle integer keys (simple lookup) */
7112+
RETURN_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
70657113

7066-
/* Handle integer keys (simple lookup) */
7067-
ZEND_ASSERT(Z_TYPE_P(key) == IS_LONG);
7068-
RETURN_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
7114+
default:
7115+
zend_argument_type_error(2, "must be of type string|int|array, %s given", zend_zval_value_name(key));
7116+
RETURN_THROWS();
7117+
}
70697118
}
70707119
/* }}} */
70717120

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)