====== PHP RFC: array_get_path and array_has_path functions ======
* Version: 0.5
* Date: 2026-04-14
* Author: Carlos Granados, barel.barelon@gmail.com
* Status: Under discussion
* Implementation: https://github.com/php/php-src/pull/21637
===== Introduction =====
In PHP applications, it is very common to work with deeply nested arrays, especially when dealing with configuration data, decoded JSON payloads, request data, framework metadata, or other dynamic structures.
When the structure of the array is known in advance and the exact element to retrieve is hardcoded, existing PHP syntax works well:
['desk' => ['price' => 100]]];
$price = $array['products']['desk']['price'] ?? null;
However, this becomes much harder when the structure is dynamic and the path is also dynamic, for example because it is built at runtime, comes from configuration, or is derived from user input.
In such cases, developers must manually:
* iterate through the path segments
* traverse the array step by step
* check each intermediate level
* handle missing keys consistently
This logic is common, repetitive, and often implemented slightly differently across projects.
This RFC proposes adding two small, focused array functions to ext/standard for accessing nested arrays using a path in a single step:
* ''array_get_path()'' retrieves a value from a deeply nested array and returns a default value if the path does not exist.
* ''array_has_path()'' checks whether a given nested path exists in an array.
These operations already appear across PHP codebases and frameworks whenever developers need to traverse structured array data dynamically. Standardizing them in core would make intent explicit, reduce repeated boilerplate, and provide consistent behavior for edge cases.
Typical use cases include: reading dynamic configuration paths, traversing decoded JSON structures, accessing request payloads, resolving runtime-defined property paths, checking whether nested optional data exists before processing it, and working with framework or library metadata stored in arrays.
===== Proposal =====
This RFC proposes adding two small, focused array functions to ext/standard for retrieving and checking nested array elements using a path, in a single step.
Today, this is typically implemented via manual traversal by iterating over an array of path segments and checking whether each segment exists. This results in repeated boilerplate, inconsistent edge-case handling, and less intention-revealing code.
This proposal standardizes the behavior in the core, making intent explicit ("get nested value", "check nested path"), and enabling a fast and consistent implementation in C.
Why this brings substantial value: deeply nested array access is very common in modern PHP applications, especially when data is dynamic rather than statically known. These helpers provide:
* Readability / intent: "what" is expressed directly, not reconstructed from loops and nested checks.
* Consistency: everyone gets the same semantics for missing paths, null values, numeric keys, and non-array intermediate values.
* Less error-prone code: avoids repeated manual traversal logic and subtle differences between implementations.
* Convenience for dynamic access: especially useful when the path is built at runtime instead of written literally in code.
==== Features and examples ====
1) Retrieve nested values with a default fallback
Use case examples
* Read configuration values using dynamic keys
* Access JSON fields selected at runtime
* Resolve user-provided paths
2) Check whether a nested path exists
Use case examples
* Validate dynamic input paths
* Check optional configuration values
3) Using dot or similar notations
While this RFC does not support dot notation (or similar notations which use a separator to specify a path) directly, it can easily be used in combination with existing PHP functions:
==== Desired syntax and semantics ====
Function list (global namespace)
As proposed, the function signatures are:
array_get_path(array $array, array $path, mixed $default = null): mixed
array_has_path(array $array, array $path): bool
Parameter order aims to follow existing conventions in related PHP functionality:
* The array being inspected is the first argument
* The path is the second argument
* ''array_get_path()'' accepts an optional default value as the third argument
==== Common behaviour ====
Array paths: $path is an array where each element represents one level of traversal.
Allowed path segment types:
* Each segment must be either a string or an integer
* If any segment is not a string or integer, a TypeError is thrown
Numeric segments:
* Integer segments are used directly as array keys
Intermediate values must be arrays:
* If traversal reaches a value that is not an array before the path is fully consumed, traversal fails.
Missing path behaviour: If any path is not found, no errors or warnings will be thrown. Instead:
* ''array_get_path()'' returns ''$default''
* ''array_has_path()'' returns ''false''
''array_has_path()'' uses existence semantics similar to ''array_key_exists()'':
* If the final key exists and its value is ''null'', ''array_has_path()'' still returns ''true''.
==== Exact semantics (per function) ====
* ''array_get_path()''
function array_get_path(array $array, array $path, mixed $default = null): mixed {
foreach ($path as $segment) {
if (!is_string($segment) && !is_int($segment)) {
throw new \TypeError('Path segment must be of type string|int');
}
if (!is_array($array) || !array_key_exists($segment, $array)) {
return $default;
}
$array = $array[$segment];
}
return $array;
}
* Each path segment is applied sequentially
* If any segment is not a string or integer: throw a TypeError.
* If any segment does not exist: return ''$default''.
* If any intermediate value is not an array before traversal is complete: return ''$default''.
* If the final key exists return its value.
* ''array_has_path()''
function array_has_path(array $array, array $path): bool {
foreach ($path as $segment) {
if (!is_string($segment) && !is_int($segment)) {
throw new \TypeError('Path segment must be of type string|int');
}
if (!is_array($array) || !array_key_exists($segment, $array)) {
return false;
}
$array = $array[$segment];
}
return true;
}
* Each path segment is applied sequentially
* If any segment is not a string or integer: throw a TypeError.
* If any segment does not exist: return ''false''.
* If any intermediate value is not an array before traversal is complete: return ''false''.
* If the final key exists return ''true''.
==== Edge cases and potential gotchas ====
* Numeric path segments
$array = ['users' => [['name' => 'Alice']]];
array_get_path($array, ['users', 0, 'name']); // 'Alice'
array_has_path($array, ['users', 0, 'name']); // true
Numeric segments are interpreted as integer keys so that indexed arrays can be traversed naturally.
* ''null'' values
$array = ['a' => ['b' => null]];
array_get_path($array, ['a', 'b']); // null
array_has_path($array, ['a', 'b']); // true
This matches existence semantics rather than ''isset()'' semantics.
* Invalid array path segments
$array = ['home' => ['sub' => ['third' => 'value']]];
array_get_path($array, ['home', new stdClass(), 'third'], 'default'); // 'default'
array_has_path($array, ['home', new stdClass(), 'third']); // false
If an array path contains any segment that is neither a string nor an integer, the path is treated as not found.
==== Examples ====
* Simple get value examples:
$array = ['products' => ['desk' => ['price' => 100]]];
$price = array_get_path($array, ['products', 'desk', 'price']); // 100
$discount = array_get_path($array, ['products', 'desk', 'discount'], 10); // 10
* Existence checks:
$array = ['product' => ['name' => 'Desk', 'price' => 100]];
array_has_path($array, ['product', 'name']); // true
array_has_path($array, ['product', 'color']); // false
* Indexed arrays:
$array = [
'users' => [
['name' => 'Alice'],
['name' => 'Bob'],
],
];
array_get_path($array, ['users', 1, 'name']); // 'Bob'
array_has_path($array, ['users', 2, 'name']); // false
* Dynamic example:
$path = getPathFromConfiguration();
$value = array_get_path($array, $path, 'not found');
This last example illustrates one of the main motivations for this RFC: when the path is dynamic, direct array syntax is no longer practical, and the alternative is repeated userland traversal logic.
===== Backward Incompatible Changes =====
This proposal introduces two new global functions. It does not modify the behaviour of any existing functions, classes, language constructs, or extensions.
As with any new global function, there is a theoretical risk of name collisions with user-defined functions of the same name. However, a search in GitHub revealed only four public PHP repositories using these function names.
===== Proposed PHP Version(s) =====
PHP 8.6
===== RFC Impact =====
==== To the Ecosystem ====
The impact on the ecosystem is limited to tooling updates to recognize the newly introduced functions; no changes in behaviour, syntax, or analysis rules are required.
==== To Existing Extensions ====
None
==== To SAPIs ====
None
===== Open Issues =====
None at the moment
===== Future Scope =====
Possible future extensions, intentionally excluded from this RFC:
* Wildcard support such as ''['products', '*', 'price']''
* Equivalent helpers for objects or array/object mixed traversal
===== Voting Choices =====
Primary Vote requiring a 2/3 majority to accept the RFC:
* Yes
* No
* Abstain
===== Patches and Tests =====
Current implementation: https://github.com/php/php-src/pull/21637
===== Implementation =====
TODO: After acceptance.
===== References =====
The Laravel framework provides similar functionality through its ''Arr::get()'' and ''Arr::has()'' helper methods, which allow accessing and checking nested array values using dot notation. These helpers are widely used within the Laravel ecosystem and demonstrate the practical utility of this pattern in real-world PHP applications. Their widespread adoption indicates that developers frequently require a concise and consistent way to traverse nested arrays dynamically.
* https://laravel.com/docs/master/helpers#method-array-get
* https://laravel.com/docs/master/helpers#method-array-has
Similar functionality exists outside of PHP, particularly in the JavaScript ecosystem. The Lodash library provides ''get()'' and ''has()'' functions that support accessing nested object properties using string paths. Its docs explicitly state that path can be either ''Array|string'', with examples using both string paths and array paths. These functions are widely used in JavaScript applications to safely traverse complex data structures. The presence of equivalent utilities in other languages and ecosystems further highlights that this is a common and well-understood pattern rather than a framework-specific abstraction.
* https://lodash.com/docs/#get
* https://lodash.com/docs/#has
Discussion: https://news-web.php.net/php.internals/130559
Replaces this RFC due to the change in the name of the functions: https://wiki.php.net/rfc/array_get_and_array_has
===== Changelog =====
* 2026-04-04: Initial RFC published
* 2026-04-04: Discussion started on internals
* 2026-04-05: Add references to implementations in userland and other languages
* 2026-04-05: Add open issues for string lists as keys and dot escaping
* 2026-04-06: Modify the proposal so that the $key parameter can also be a list of strings/ints
* 2026-04-11: Remove accepting a $key parameter with dot notation and only use a $path parameter with an array path
* 2026-04-14: Modify function names to add _path suffix, throw TypeError if segment is not int|string