-
Notifications
You must be signed in to change notification settings - Fork 8k
Array_get and array_has functions #21637
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6910,6 +6910,114 @@ PHP_FUNCTION(array_key_exists) | |
| } | ||
| /* }}} */ | ||
|
|
||
| /* {{{ Helper function to get a nested value from array using dot notation */ | ||
| static zval* array_get_nested(HashTable *ht, const char *key, size_t key_len) | ||
| { | ||
| const char *dot; | ||
| zval *current; | ||
|
|
||
| /* Find the first dot in the key */ | ||
| dot = memchr(key, '.', key_len); | ||
|
|
||
| if (dot == NULL) { | ||
| /* No dot found, this is a simple key lookup */ | ||
| zend_string *zkey = zend_string_init(key, key_len, 0); | ||
| current = zend_symtable_find(ht, zkey); | ||
| zend_string_release(zkey); | ||
| return current; | ||
| } | ||
|
|
||
| /* We have a dot, so we need to recurse */ | ||
| size_t segment_len = dot - key; | ||
| zend_string *segment = zend_string_init(key, segment_len, 0); | ||
| current = zend_symtable_find(ht, segment); | ||
| zend_string_release(segment); | ||
|
|
||
| if (current == NULL || Z_TYPE_P(current) != IS_ARRAY) { | ||
|
||
| return NULL; | ||
| } | ||
|
|
||
| /* Recurse into the nested array with the remaining key */ | ||
| return array_get_nested(Z_ARRVAL_P(current), dot + 1, key_len - segment_len - 1); | ||
|
||
| } | ||
| /* }}} */ | ||
|
|
||
| /* {{{ Retrieves a value from a deeply nested array using "dot" notation */ | ||
| PHP_FUNCTION(array_get) | ||
| { | ||
| HashTable *ht; | ||
| zval *key = NULL; | ||
| zval *default_value = NULL; | ||
| zval *result; | ||
|
|
||
| ZEND_PARSE_PARAMETERS_START(2, 3) | ||
| Z_PARAM_ARRAY_HT(ht) | ||
| Z_PARAM_ZVAL_OR_NULL(key) | ||
| Z_PARAM_OPTIONAL | ||
| Z_PARAM_ZVAL(default_value) | ||
| ZEND_PARSE_PARAMETERS_END(); | ||
|
|
||
| /* 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)); | ||
|
||
| return; | ||
| } | ||
|
|
||
| /* Handle string keys with dot notation */ | ||
| if (Z_TYPE_P(key) == IS_STRING) { | ||
| result = array_get_nested(ht, Z_STRVAL_P(key), Z_STRLEN_P(key)); | ||
|
|
||
| if (result != NULL) { | ||
| ZVAL_COPY(return_value, result); | ||
|
||
| return; | ||
| } | ||
| } | ||
| /* Handle integer keys (no dot notation support) */ | ||
| else if (Z_TYPE_P(key) == IS_LONG) { | ||
| result = zend_hash_index_find(ht, Z_LVAL_P(key)); | ||
|
|
||
| if (result != NULL) { | ||
| ZVAL_COPY(return_value, result); | ||
|
||
| return; | ||
| } | ||
| } | ||
|
|
||
| /* Key not found, return default value */ | ||
| if (default_value != NULL) { | ||
| ZVAL_COPY(return_value, default_value); | ||
|
||
| } else { | ||
| RETVAL_NULL(); | ||
|
||
| } | ||
| } | ||
| /* }}} */ | ||
|
|
||
| /* {{{ Checks whether a given item exists in an array using "dot" notation */ | ||
| PHP_FUNCTION(array_has) | ||
| { | ||
| HashTable *ht; | ||
| zval *key; | ||
| zval *result; | ||
|
|
||
| ZEND_PARSE_PARAMETERS_START(2, 2) | ||
| Z_PARAM_ARRAY_HT(ht) | ||
| Z_PARAM_ZVAL(key) | ||
| ZEND_PARSE_PARAMETERS_END(); | ||
|
|
||
| /* Handle string keys with dot notation */ | ||
| if (Z_TYPE_P(key) == IS_STRING) { | ||
| result = array_get_nested(ht, Z_STRVAL_P(key), Z_STRLEN_P(key)); | ||
| RETURN_BOOL(result != NULL); | ||
| } | ||
| /* Handle integer keys (no dot notation support) */ | ||
| else if (Z_TYPE_P(key) == IS_LONG) { | ||
| RETURN_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key))); | ||
| } | ||
|
|
||
| /* Invalid key type */ | ||
| RETURN_FALSE; | ||
|
||
| } | ||
| /* }}} */ | ||
|
|
||
| /* {{{ Split array into chunks */ | ||
| PHP_FUNCTION(array_chunk) | ||
| { | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| --TEST-- | ||
| Test array_get() function | ||
| --FILE-- | ||
| <?php | ||
| /* | ||
| * Test functionality of array_get() | ||
| */ | ||
|
|
||
| echo "*** Testing array_get() ***\n"; | ||
|
|
||
| // Basic array access | ||
| $array = ['products' => ['desk' => ['price' => 100]]]; | ||
|
|
||
| // Test nested access with dot notation | ||
| var_dump(array_get($array, 'products.desk.price')); | ||
|
|
||
| // Test with default value when key doesn't exist | ||
| var_dump(array_get($array, 'products.desk.discount', 0)); | ||
|
|
||
| // Test simple key access | ||
| $simple = ['name' => 'John', 'age' => 30]; | ||
| var_dump(array_get($simple, 'name')); | ||
| var_dump(array_get($simple, 'missing', 'default')); | ||
|
|
||
| // Test with integer key | ||
| $indexed = ['a', 'b', 'c']; | ||
| var_dump(array_get($indexed, 0)); | ||
| var_dump(array_get($indexed, 5, 'not found')); | ||
|
|
||
| // Test with null key (returns whole array) | ||
| $test = ['foo' => 'bar']; | ||
| var_dump(array_get($test, null)); | ||
|
|
||
| // Test nested with missing intermediate key | ||
| var_dump(array_get($array, 'products.chair.price', 50)); | ||
|
|
||
| // Test single level key that doesn't exist | ||
| var_dump(array_get($array, 'missing')); | ||
|
|
||
| // Test with numeric string in path (like users.0.name) | ||
| $users = ['users' => [['name' => 'Alice'], ['name' => 'Bob']]]; | ||
| var_dump(array_get($users, 'users.0.name')); | ||
| var_dump(array_get($users, 'users.1.age', 70)); | ||
|
|
||
| echo "Done"; | ||
| ?> | ||
| --EXPECT-- | ||
| *** Testing array_get() *** | ||
| int(100) | ||
| int(0) | ||
| string(4) "John" | ||
| string(7) "default" | ||
| string(1) "a" | ||
| string(9) "not found" | ||
| array(1) { | ||
| ["foo"]=> | ||
| string(3) "bar" | ||
| } | ||
| int(50) | ||
| NULL | ||
| string(5) "Alice" | ||
| int(70) | ||
| Done |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| --TEST-- | ||
| Test array_has() function | ||
| --FILE-- | ||
| <?php | ||
| /* | ||
| * Test functionality of array_has() | ||
| */ | ||
|
|
||
| echo "*** Testing array_has() ***\n"; | ||
|
|
||
| // Basic array | ||
| $array = ['product' => ['name' => 'Desk', 'price' => 100]]; | ||
|
|
||
| // Test nested key exists with dot notation | ||
| var_dump(array_has($array, 'product.name')); | ||
|
|
||
| // Test nested key doesn't exist | ||
| var_dump(array_has($array, 'product.color')); | ||
|
|
||
| // Test intermediate key doesn't exist | ||
| var_dump(array_has($array, 'category.name')); | ||
|
|
||
| // Test simple key access | ||
| $simple = ['name' => 'John', 'age' => 30]; | ||
| var_dump(array_has($simple, 'name')); | ||
| var_dump(array_has($simple, 'missing')); | ||
|
|
||
| // Test with integer key | ||
| $indexed = ['a', 'b', 'c']; | ||
| var_dump(array_has($indexed, 0)); | ||
| var_dump(array_has($indexed, 1)); | ||
| var_dump(array_has($indexed, 5)); | ||
|
|
||
| // Test with value that is null (key exists, but value is null) | ||
| $withNull = ['key' => null]; | ||
| var_dump(array_has($withNull, 'key')); | ||
|
|
||
| // Test with numeric string in path (like users.0.name) | ||
| $users = ['users' => [['name' => 'Alice'], ['name' => 'Bob']]]; | ||
| var_dump(array_has($users, 'users.0.name')); | ||
| var_dump(array_has($users, 'users.1.age')); | ||
| var_dump(array_has($users, 'users.2.name')); | ||
|
|
||
| echo "Done"; | ||
| ?> | ||
| --EXPECT-- | ||
| *** Testing array_has() *** | ||
| bool(true) | ||
| bool(false) | ||
| bool(false) | ||
| bool(true) | ||
| bool(false) | ||
| bool(true) | ||
| bool(true) | ||
| bool(false) | ||
| bool(true) | ||
| bool(true) | ||
| bool(false) | ||
| bool(false) | ||
| Done |
There was a problem hiding this comment.
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).