Skip to content

Array_get and array_has functions#21637

Open
carlos-granados wants to merge 3 commits intophp:masterfrom
carlos-granados:array-get-array-has
Open

Array_get and array_has functions#21637
carlos-granados wants to merge 3 commits intophp:masterfrom
carlos-granados:array-get-array-has

Conversation

@carlos-granados
Copy link
Copy Markdown

This PR proposes adding two small, focused array functions to ext/standard for retrieving and checking nested array elements using dot notation, in a single step.

See the related RFC:
https://wiki.php.net/rfc/array_get_and_array_has


if (dot == NULL) {
/* No dot found, this is a simple key lookup */
zend_string *zkey = zend_string_init(key, key_len, 0);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have a zend_string already here, then you can avoid the allocation.
In general, use zend_symtable_str_find (also below).


/* If key is null, return the whole array */
if (key == NULL || Z_TYPE_P(key) == IS_NULL) {
ZVAL_ARR(return_value, zend_array_dup(ht));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid a duplication, just return a copy with the refcount. Sadly, ZVAL_ARR sucks as it doesn't take into account the mutability in the type info; so you'd have to use Z_PARAM_ARRAY so you have a zval that you can use RETURN_COPY on.

result = array_get_nested(ht, Z_STRVAL_P(key), Z_STRLEN_P(key));

if (result != NULL) {
ZVAL_COPY(return_value, result);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RETURN_COPY

result = zend_hash_index_find(ht, Z_LVAL_P(key));

if (result != NULL) {
ZVAL_COPY(return_value, result);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RETURN_COPY


/* Key not found, return default value */
if (default_value != NULL) {
ZVAL_COPY(return_value, default_value);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RETURN_COPY

if (default_value != NULL) {
ZVAL_COPY(return_value, default_value);
} else {
RETVAL_NULL();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary, the return value is NULL implicitly

}

/* Invalid key type */
RETURN_FALSE;
Copy link
Copy Markdown
Member

@ndossche ndossche Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case is impossible by using the type int|string, use ZEND_UNREACHABLE();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet impossible, because Z_PARAM_ZVAL, but yes, it should be properly typed in ZPP.

}

/* Recurse into the nested array with the remaining key */
return array_get_nested(Z_ARRVAL_P(current), dot + 1, key_len - segment_len - 1);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless this gets tailcall eliminated, you have primitive recursion here which can overflow. Best to write an explicit loop.

current = zend_symtable_find(ht, segment);
zend_string_release(segment);

if (current == NULL || Z_TYPE_P(current) != IS_ARRAY) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this handle references properly?

@carlos-granados
Copy link
Copy Markdown
Author

@ndossche thanks for your review. Following some comments on the RFC discussion I have updated the implementation to also accept an array in the $key parameter. I also looked into all the other issues that you raised (though some do not apply any longer)

}

/* Handle integer keys (simple lookup) */
ZEND_ASSERT(Z_TYPE_P(key) == IS_LONG);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is insufficient, the ZPP parsing still accepts any argument. The stubs don't enforce the argument type check in any way.

/* Use php_explode to split the string by '.' */
zend_string *delim = ZSTR_CHAR('.');
array_init(&segments_array);
php_explode(delim, Z_STR_P(key), &segments_array, ZEND_LONG_MAX);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit of an inefficient way to go about it.

result = zend_hash_index_find(ht, Z_LVAL_P(key));

if (result != NULL) {
RETURN_COPY(result);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work properly with references I believe.
The function stub doesn't seem to return a reference, so this needs to be RETURN_COPY_DEREF (same issue is also present in other places).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants