data_get

Read a nested value from an array or object using dot notation, with a default.

Signature
data_get(mixed $target, string|array|int|null $key, mixed $default = null): mixed

Parameters

ParameterTypeDescription
$targetmixedThe array, object, Collection or ArrayAccess value to read from.
$keystring|array|int|nullThe dot-notation path. Segments may be array keys, object properties, an * wildcard, or the {first} / {last} placeholders. Passing null returns the target unchanged.
$defaultoptionalmixed= nullReturned when the path does not resolve. If it is a closure it is invoked lazily through value().

Return value

mixedThe value at the given path, or the default when the path does not resolve.

Changelog

  • PHP 11.0The {first} and {last} path placeholders were added. On 10.x and earlier those segments are treated as literal keys and will not match.

data_get() walks a dot path through whatever it is handed — nested arrays, public object properties, Collections, ArrayAccess objects, or a mix of all of them in the same path. If any segment is missing it stops and returns the default instead of emitting a warning.

Why not Arr::get()

Arr::get() only traverses arrays and ArrayAccess. data_get() is the one to use when the shape is not guaranteed to be all arrays — decoded JSON that may hand you stdClass, a mix of models and arrays, or third-party payloads.

Wildcards and placeholders

An * segment collects the matching value from every element at that level and always returns an array, even when a single element matches. {first} and {last} pick one element instead, so they return a scalar.

The default is lazy

The third argument is passed through value(), so a closure is only invoked when the path actually misses. That makes an expensive fallback cheap to supply.

Null is a value

The default only applies when the path does not resolve. A key that exists and holds null returns null, not your default.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Pull a deeply nested field out of a decoded webhook payload without chained isset() checks.
  • Collect one attribute from every row of a nested list with a * wildcard.
  • Read a path that may traverse both array keys and object properties in the same expression.

Frequently asked questions

What is the difference between data_get and Arr::get?

data_get traverses objects as well as arrays, while Arr::get only handles arrays and ArrayAccess. data_get also supports * wildcards and the {first}/{last} placeholders, so use it whenever the structure may contain stdClass or model instances.

Why does data_get return null instead of my default value?

Because the key exists and its value is null — the default is only used when the path cannot be resolved at all. Write data_get($data, 'key') ?? $default if you want an existing null replaced too.

Does the * wildcard return a single value when only one item matches?

No, a wildcard always returns an array, even for one match or zero matches. Use the {first} or {last} placeholder, or call head() on the result, when you want a scalar back.

More examples

Mixed arrays and objects in one path

The same call handles a stdClass tree from json_decode, a plain array and a Collection. This is the distinction from Arr::get(), which would fail on the object segments.

Example · php
<?php

$response = json_decode('{"user":{"profile":{"city":"Lisbon"}}}');

// $response is a stdClass tree, but the path syntax is identical.
echo data_get($response, 'user.profile.city'), "\n";
// Lisbon

echo data_get($response, 'user.profile.country', 'unknown'), "\n";
// unknown

// Collections work too.
echo data_get(collect(['a' => ['b' => 42]]), 'a.b'), "\n";
// 42

{first} and {last} placeholders

{first} and {last} return the single matching value, while * returns an array of every match. Mixing them up is the usual cause of an "array to string conversion" notice further down the code.

Example · php
<?php

$flight = [
    'segments' => [
        ['from' => 'LHR', 'departure' => '9:00',  'to' => 'IST', 'arrival' => '15:00'],
        ['from' => 'IST', 'departure' => '16:00', 'to' => 'PKX', 'arrival' => '20:00'],
    ],
];

echo data_get($flight, 'segments.{first}.arrival'), "\n"; // 15:00
echo data_get($flight, 'segments.{last}.to'), "\n";       // PKX

// Contrast with a wildcard, which always returns an array.
print_r(data_get($flight, 'segments.*.from'));
// Array ( [0] => LHR [1] => IST )

Lazy default in an application

Because the default is resolved through value(), an expensive fallback such as a config or database lookup costs nothing when the key is present. This snippet needs a booted application for config().

Example · php
<?php

namespace App\Services;

class Settings
{
    public function __construct(private array $payload) {}

    public function currency(): string
    {
        // The closure only runs when the path is missing, so the
        // config lookup is skipped on the happy path.
        return data_get(
            $this->payload,
            'account.billing.currency',
            fn () => config('app.default_currency')
        );
    }
}

Discussion

  • Be the first to comment on this lesson.