Skip to content

Dot Path Syntax

Learn how to use dot-path notation and wildcards to access nested data.

All helpers (DataAccessor, DataMutator, DataMapper) use the same dot-path syntax and wildcard semantics.

Dot-path notation allows you to access deeply nested data structures using a simple string syntax:

// Access nested data
$accessor->get('user.profile.name');
// Access array elements
$accessor->get('users.0.email');
// Use wildcards
$accessor->get('users.*.email');

Segments are separated by . (dot):

// Simple path
'user.profile.name' // Accesses $data['user']['profile']['name']
// Numeric index
'users.0.email' // Accesses $data['users'][0]['email']
// Multiple levels
'company.departments.0.employees.5.name'
  • Segments are separated by . (dot)
  • Numeric segments index arrays/collections
  • Empty segments are not allowed
  • Invalid syntax throws InvalidArgumentException:
    • Leading dot: .a
    • Trailing dot: a.
    • Double dots: a..b
  • Empty path "" is allowed and yields no segments

* matches any single segment at that position:

// Match all emails in users array
'users.*.email'
// Returns:
[
'users.0.email' => 'alice@example.com',
'users.1.email' => 'bob@example.com',
'users.2.email' => 'charlie@example.com',
]

Multiple * can appear in one path:

// Match all SKUs in all items across all orders
'orders.*.items.*.sku'
// Returns:
[
'orders.0.items.0.sku' => 'WIDGET-A',
'orders.0.items.1.sku' => 'GADGET-B',
'orders.1.items.0.sku' => 'TOOL-C',
]
// Check if path contains wildcard
$accessor->containsWildcard('users.*.email'); // true
$accessor->containsWildcard('users.0.email'); // false
// Throws on invalid syntax
$accessor->containsWildcard('users..*.email'); // InvalidArgumentException

DataAccessor returns an associative array keyed by the resolved dot-path for each match:

$data = [
'users' => [
['email' => 'a@example.com'],
['email' => null],
['email' => 'b@example.com'],
],
];
$accessor = new DataAccessor($data);
$result = $accessor->get('users.*.email');
// Returns:
[
'users.0.email' => 'a@example.com',
'users.1.email' => null,
'users.2.email' => 'b@example.com',
]

When mapping from users.*.email to emails.*:

$mapper = new DataMapper($data);
$mapper->map('users.*.email', 'emails.*');
// Each matched value is placed into emails.{i}
$mapper->map('users.*.email', 'emails.*', skipNull: true);
// Nulls are skipped
$mapper->map('users.*.email', 'emails.*', reindexWildcard: false);
// Preserves numeric gaps (e.g., keep 0 and 2)
$mapper->map('users.*.email', 'emails.*', reindexWildcard: true);
// Compacts indices to sequential array [0..n-1]

Paths like 0.name are valid and target the root-level array index:

$data = [
['name' => 'Alice'],
['name' => 'Bob'],
];
$accessor = new DataAccessor($data);
$name = $accessor->get('0.name'); // 'Alice'

Returns null if a path does not exist:

$accessor->get('user.nonexistent.key'); // null

With skipNull=true, values that resolve to null are skipped:

$mapper->map('user.email', 'contact.email', skipNull: true);
'user.profile.name' // Accesses $data['user']['profile']['name']
'users.0.email' // Accesses $data['users'][0]['email']
'users.*.email' // Matches all emails in users array
// Returns: ['users.0.email' => 'a@x', 'users.1.email' => 'b@x']
'orders.*.items.*.sku' // Matches all SKUs in all items across all orders
// Returns: ['orders.0.items.0.sku' => 'A', 'orders.0.items.1.sku' => 'B']
'0.name' // Accesses $data[0]['name'] when $data is a numeric array

An empty path "" is allowed and yields no segments. DataAccessor returns the entire data structure:

$accessor = new DataAccessor(['a' => 1]);
$result = $accessor->get(''); // ['a' => 1]

These paths throw InvalidArgumentException:

'.user.name' // Leading dot
'user.name.' // Trailing dot
'user..name' // Double dots
// ✅ Good - use wildcard
$accessor->get('users.*.email');
// ❌ Bad - manual loop
foreach ($data['users'] as $i => $user) {
$accessor->get("users.{$i}.email");
}
// ✅ Good - validate before use
if ($accessor->containsWildcard($path)) {
// Handle wildcard path
}
// ❌ Bad - assume path format
$accessor->get($path);