rfc:array_get_and_array_has

PHP RFC: array_get and array_has functions

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.

The use of “dot notation” to access these nested data structures is already widespread across PHP ecosystems and beyond. It is commonly used in frameworks, in configuration systems, and in many userland helper libraries. Outside of PHP, similar patterns appear in JavaScript when working with object paths, in JSON query tools, and in configuration formats where hierarchical data is represented as strings. Developers are therefore already familiar with expressing nested access using paths such as db.connections.mysql.host or users.0.email, and frequently implement their own helpers to support this pattern in PHP.

When the structure of the array is known in advance and the exact element to retrieve is hardcoded, existing PHP syntax works well:

<?php
 
$array = ['products' => ['desk' => ['price' => 100]]];
 
$price = $array['products']['desk']['price'] ?? null;

However, this becomes much harder when the structure is dynamic and the key is also dynamic, for example because it is built at runtime, comes from configuration, or is derived from user input.

<?php
 
$path = $_GET['field'] ?? 'products.desk.price';
 
// accessing $array using $path requires manual traversal today

In such cases, direct array access requires manual parsing of the key, iterative traversal of the array, repeated is_array() checks, and careful handling of missing intermediate segments.

This RFC proposes adding two small, focused array functions to ext/standard for accessing nested arrays using “dot notation” in a single step:

  • array_get() retrieves a value from a deeply nested array and returns a default value if the path does not exist.
  • array_has() 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 dot notation, in a single step.

Today, this is typically implemented via manual traversal, often by exploding a string path and looping through the array while 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
<?php
 
$field = $_GET['field'] ?? 'products.desk.price';
 
$value = array_get($array, $field, 'not found');

2) Check whether a nested path exists

Use case examples

  • Validate dynamic input paths
  • Check optional configuration values
<?php
 
$field = $_GET['field'] ?? 'product.name';
 
if (array_has($array, $field)) {
    // field was provided
}

Desired syntax and semantics

Function list (global namespace)

As proposed, the function signatures are:

array_get(array $array, string|int|null $key, mixed $default = null): mixed
 
array_has(array $array, string|int $key): bool

Parameter order aims to follow existing conventions in related PHP functionality:

  • The array being inspected is the first argument
  • The key/path is the second argument
  • array_get() accepts an optional default value as the third argument

Common behaviour

Dot notation traversal: string keys are split on . into path segments, which are used to traverse the array one level at a time.

Numeric path segments: if a segment is a numeric string such as “0”, it is treated as the integer key 0.

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 behavior:

  • array_get() returns $default
  • array_has() returns false

array_get() special case for null key:

  • If $key is null, the full input array is returned unchanged.

array_has() uses existence semantics similar to array_key_exists():

  • If the final key exists and its value is null, array_has() still returns true.

Exact semantics (per function)

  • array_get()
function array_get(array $array, string|int|null $key, mixed $default = null): mixed {
    if ($key === null) {
        return $array;
    }
 
    if (is_int($key)) {
        return array_key_exists($key, $array) ? $array[$key] : $default;
    }
 
    $segments = explode('.', $key);
 
    foreach ($segments as $segment) {
 
        if (!is_array($array) || !array_key_exists($segment, $array)) {
            return $default;
        }
 
        $array = $array[$segment];
    }
 
    return $array;
}
  • If $key is null: return the whole array unchanged.
  • If $key is an integer: check that key directly in the top-level array.
  • If $key is a string: split it on dots and traverse the array segment by segment.
  • 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()
function array_has(array $array, string|int $key): bool {
    if (is_int($key)) {
        return array_key_exists($key, $array);
    }
 
    $segments = explode('.', $key);
 
    foreach ($segments as $segment) {
 
        if (!is_array($array) || !array_key_exists($segment, $array)) {
            return false;
        }
 
        $array = $array[$segment];
    }
 
    return true;
}
  • If $key is an integer: check that key directly in the top-level array.
  • If $key is a string: split it on dots and traverse the array segment by segment.
  • 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

  • null key in array_get()
array_get(['a' => 1], null); // ['a' => 1]

This is useful in code paths where the caller may optionally provide a path and wants the original array returned unchanged when no path is given.

  • Integer keys
array_get(['a', 'b'], 1); // 'b'
 
array_has(['a', 'b'], 2); // false

If an integer key is used we retrieve or check that key directly in the top-level array.

  • Numeric string path segments
$array = ['users' => [['name' => 'Alice']]];
 
array_get($array, 'users.0.name'); // 'Alice'
array_has($array, 'users.0.name'); // true

Numeric string segments are interpreted as integer keys so that indexed arrays can be traversed naturally.

  • null values
$array = ['a' => ['b' => null]];
 
array_get($array, 'a.b'); // null
array_has($array, 'a.b'); // true

This matches existence semantics rather than isset() semantics.

  • Literal dots in array keys
$array = ['user.name' => 'Carlos'];
 
array_get($array, 'user.name'); // looks for ['user']['name'], not ['user.name']

This RFC does not propose an escaping mechanism for literal dots inside key names. Dot notation is interpreted structurally.

Examples

  • Simple get value examples:
$array = ['products' => ['desk' => ['price' => 100]]];
 
$price = array_get($array, 'products.desk.price'); // 100
 
$discount = array_get($array, 'products.desk.discount', 0); // 0
  • Existence checks:
$array = ['product' => ['name' => 'Desk', 'price' => 100]];
 
array_has($array, 'product.name'); // true
 
array_has($array, 'product.color'); // false
  • Indexed arrays:
$array = [
    'users' => [
        ['name' => 'Alice'],
        ['name' => 'Bob'],
    ],
];
 
array_get($array, 'users.1.name'); // 'Bob'
 
array_has($array, 'users.2.name'); // false
  • Dynamic example:
$path = $_GET['field'] ?? 'products.desk.price';
 
$value = array_get($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 behavior, syntax, or analysis rules are required.

To Existing Extensions

None

To SAPIs

None

Open Issues

None currently.

Future Scope

Possible future extensions, intentionally excluded from this RFC:

  • Escaping for literal dots in keys
  • 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:

Implement array_get and array_has functions as outlined in the RFC?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

Patches and Tests

Implementation

TODO: After acceptance.

References

Changelog

  • 2026-04-04: Initial RFC published
  • 2026-04-04: Discussion started on internals
rfc/array_get_and_array_has.txt · Last modified: by barel