Collection::get
Returns the collection item stored at a given key, or a default when that key is absent.
Collection::get(mixed $key, mixed $default = null): TValue|TGetDefaultParameters
| Parameter | Type | Description |
|---|---|---|
$key | TKey|null | The key to read. Passing null is coerced to an empty-string key. Dot notation is not interpreted — the key is matched literally. |
$defaultoptional | TGetDefault|(\Closure(): TGetDefault)= null | Returned when the key is absent. A closure is resolved lazily through value(), so it runs only on a miss. |
Return value
TValue|TGetDefaultThe item stored at the key, or the default when the key does not exist. A closure default is invoked and its return value used.
get reads a single item by key. It is the safe counterpart to array-style access: where $collection['missing'] raises a warning and returns null, get('missing') quietly returns null or whatever default you supplied.
When to reach for it
Use it any time the key might legitimately be absent — reading an optional field out of a decoded payload, looking up a setting that may not have been configured, pulling an entry from a keyed lookup table. It works on integer offsets too, so it is also the safe way to index into a list.
The lazy default
The second argument passes through value(), so a closure is invoked only when the key is missing. That is what makes get($key, fn () => $this->computeExpensiveFallback()) worth writing — the fallback costs nothing on a hit. Pass the call directly instead of wrapping it in a closure and you pay for it on every lookup.
The thing that trips people up
Presence is decided with array_key_exists, not isset. A key whose stored value is null counts as present, so the default is skipped and you get null back. If your data uses null to mean "unset", get will not substitute for it — reach for ?? on the result, or check has() yourself.
Example
When to use it
- Read an optional field from a decoded JSON body without triggering an undefined-index warning.
- Look up a setting with a computed fallback that should only run when the setting is missing.
- Safely index into a list by position when the offset may be out of range.
Frequently asked questions
Why does get() return null instead of my default value?
Because the key exists and holds null. get() tests presence with array_key_exists rather than isset, so a stored null is a real value and the default is skipped. Use ?? on the result if null should also trigger the fallback.
Is the default argument evaluated when the key is found?
Only if it is a plain value. A closure default is resolved through value() and runs exclusively on a miss, so wrap anything expensive in fn () => ... rather than calling it inline.
Does Collection::get support dot notation?
No, the key is matched literally against the collection's own keys. For nested lookups use data_get(), or firstWhere() with a dotted path when searching a collection of records.
More examples
Integer offsets and out-of-range access
The outputs are string(1) "b", NULL, string(7) "missing" and bool(false). get() is the reason you rarely need bounds checking around a collection index — an out-of-range offset produces the default rather than a warning, which makes it safe inside a template or a response payload.
<?php
$list = collect(['a', 'b', 'c']);
var_dump($list->get(1));
var_dump($list->get(9));
var_dump($list->get(9, 'missing'));
// Array access throws on a missing offset in modern PHP; get() does not.
var_dump(isset($list[9]));Lazy defaults keep expensive work off the happy path
The output is int(1), then string(5) "built", then "closure ran 1 time". This is the practical argument for always wrapping a costly fallback in a closure: the counter proves the builder was skipped entirely on the cache hit, which a plain default value could never do.
<?php
$calls = 0;
$build = function () use (&$calls) {
$calls++;
return 'built';
};
$cache = collect(['a' => 1]);
var_dump($cache->get('a', $build)); // hit - closure not called
var_dump($cache->get('b', $build)); // miss - closure called
echo 'closure ran ', $calls, ' time', PHP_EOL;get versus getOrPut
The output is int(2), bool(false), int(2), bool(true). Both return the same value, but only getOrPut writes it back into the collection. Reach for getOrPut when the collection is acting as a memoisation table and you want the second lookup to be a hit.
<?php
$cache = collect(['a' => 1]);
// get leaves the collection untouched.
var_dump($cache->get('b', 2));
var_dump($cache->has('b'));
// getOrPut stores the computed value for next time.
var_dump($cache->getOrPut('b', fn () => 2));
var_dump($cache->has('b'));
Discussion