codeliner/array-reader
Read values from multidimensional PHP arrays using dot-paths with escaping for dotted keys. Typed getters like stringValue() accept defaults when paths are missing, and pathExists() lets you distinguish null values from non-existent paths.
Installation:
composer require codeliner/array-reader:~2.0
Add to composer.json under require:
"codeliner/array-reader": "^2.0"
Basic Usage:
use Codeliner\ArrayReader\ArrayReader;
$reader = new ArrayReader([
'user' => [
'name' => 'John',
'details' => [
'age' => 30,
'email' => 'john@example.com'
]
]
]);
// Fetch values with dot notation
$name = $reader->stringValue('user.name'); // 'John'
$age = $reader->intValue('user.details.age'); // 30
Replace direct array access (e.g., $array['user']['name']) with ArrayReader to avoid UndefinedIndex or UndefinedOffset errors. Use default values for missing paths:
$email = $reader->stringValue('user.details.email', 'default@example.com');
// Returns 'john@example.com' if exists, otherwise 'default@example.com'
Leverage type-specific methods (stringValue(), intValue(), boolValue(), etc.) to enforce type casting and avoid runtime type errors:
$isActive = $reader->boolValue('user.is_active', false); // Casts to boolean
Use pathExists() to check for nested keys before accessing them (useful for conditional logic):
if ($reader->pathExists('user.address.city')) {
$city = $reader->stringValue('user.address.city');
}
Build paths dynamically (e.g., from user input or config) while escaping dots in keys:
$dynamicPath = 'user.profile.' . str_replace('.', '\.', $profileKey);
$value = $reader->stringValue($dynamicPath);
Form Requests: Validate nested array paths safely:
$this->validate($request, [
'user.name' => 'required|string',
'user.details.age' => 'integer|min:18',
]);
Use ArrayReader to parse validated data:
$reader = new ArrayReader($request->validated());
$name = $reader->stringValue('user.name');
Service Providers: Inject ArrayReader to handle config arrays:
public function register()
{
$this->app->singleton(ArrayReader::class, function ($app) {
return new ArrayReader(config('app.settings'));
});
}
Chain ArrayReader with Laravel’s collect() for fluent transformations:
$collection = collect($data)->mapWithKeys(function ($item) {
$reader = new ArrayReader($item);
return [
$reader->stringValue('id') => [
'name' => $reader->stringValue('name'),
'active' => $reader->boolValue('is_active', false),
]
];
});
Dot Escaping:
user.name.first vs. user.name\.first) will throw UndefinedIndex.str_replace('.', '\.', $key) when constructing paths dynamically.Type Casting:
intValue() will cast strings (e.g., "30" → 30) but fail on invalid strings (e.g., "abc" → 0).filterVar() or is_numeric() for stricter validation:
$age = $reader->intValue('user.age');
if (!is_numeric($reader->stringValue('user.age'))) {
throw new \InvalidArgumentException('Age must be numeric');
}
Null vs. Missing Path:
pathExists() returns true for null values. Use array_key_exists() or isset() on the underlying array if you need to distinguish:
$array = $reader->getArray();
if (isset($array['user']['name'])) {
// Key exists (even if value is null)
}
Performance:
ArrayReader traverses the array recursively for each call. For high-frequency access (e.g., loops), cache the reader or pre-fetch values:
$values = [
'name' => $reader->stringValue('user.name'),
'age' => $reader->intValue('user.age'),
];
Inspect the Underlying Array:
Use $reader->getArray() to debug the raw structure:
dd($reader->getArray());
Path Validation: Test paths incrementally:
if (!$reader->pathExists('user.profile')) {
throw new \RuntimeException("Invalid path: user.profile");
}
Type-Specific Issues:
boolValue(), empty strings ("") return false. Use stringValue() + explicit casting if needed:
$isActive = (bool) $reader->stringValue('user.is_active');
Custom Value Types:
Extend ArrayReader to add domain-specific methods (e.g., dateValue()):
class ExtendedArrayReader extends ArrayReader {
public function dateValue(string $path, ?\DateTimeInterface $default = null): ?\DateTimeInterface {
$value = $this->stringValue($path, $default?->format('Y-m-d'));
return $default ? new \DateTime($value) : null;
}
}
Path Normalization:
Override normalizePath() to handle custom path formats (e.g., / separators):
protected function normalizePath(string $path): string {
return str_replace(['/', '\\'], '.', $path);
}
Laravel Service Binding:
Bind the extended reader in AppServiceProvider:
$this->app->bind(ExtendedArrayReader::class, function ($app) {
return new ExtendedArrayReader(config('app.settings'));
});
ArrayReader does not modify the input array. If you need mutability, clone the array first:
$mutableArray = $reader->getArray();
$mutableArray['new_key'] = 'value';
How can I help you explore Laravel packages today?