chrisharrison/php-array-of
A lightweight PHP helper for creating and working with typed “array of” value collections. Simplifies validation/coercion so arrays contain only the expected item type, improving safety and readability for DTOs, configs, and API payload handling.
Installation:
composer require chrisharrison/php-array-of
No configuration required—just autoload the package.
First Use Case: Enforce an array of strings in a Laravel request handler:
use ArrayOf\ArrayOf;
$validated = request()->validate(['tags' => 'array']);
$typedTags = ArrayOf::of('string')->from($validated['tags']);
$validated['tags'] contains a non-string (e.g., 123 or null), it throws ArrayOf\Exception\InvalidTypeException.ArrayOf::of($type) → Creates a validator for type $type (e.g., 'int', User::class).->from($array) → Validates and returns the array (throws on failure).->validate($array) → Validates without returning (returns bool).ArrayOf::of(function($value) { return $value > 0; }) for custom logic.Replace manual foreach checks with php-array-of in DTO constructors:
class UserDTO {
public function __construct(
private array $roles,
private array $permissions
) {
$this->roles = ArrayOf::of('string')->from($roles);
$this->permissions = ArrayOf::of(Permission::class)->from($permissions);
}
}
FormRequest validation:
public function rules() {
return ['roles' => 'array'];
}
public function withValidator($validator) {
$validator->after(function ($validator) {
ArrayOf::of('string')->validate($this->input['roles']);
});
}
Ensure collections in domain objects are type-safe:
class Order {
public function __construct(
private array $items
) {
$this->items = ArrayOf::of(OrderItem::class)->from($items);
}
public function addItem(OrderItem $item) {
$this->items[] = $item; // No need for manual type checks
}
}
Sanitize and validate nested arrays:
$rawData = ['tags' => ['admin', 123, null]];
$cleanTags = ArrayOf::of('string')->from($rawData['tags']);
// Throws on `123` or `null`; returns `['admin']` on success.
Extend Laravel’s validation with reusable rules:
use Illuminate\Validation\Rule;
class ArrayOfRule extends Rule {
public function __construct($type) {
$this->type = $type;
}
public function passes($attribute, $value) {
return ArrayOf::of($this->type)->validate($value);
}
}
// Usage:
'roles' => ['array', new ArrayOfRule('string')],
$validator = ArrayOf::of(User::class);
foreach ($users as $user) {
$validator->validate([$user]); // Reuse instance
}
$chunks = array_chunk($largeArray, 100);
foreach ($chunks as $chunk) {
ArrayOf::of('int')->validate($chunk);
}
Null Handling:
ArrayOf::of('string')->from([null]) throws an exception.ArrayOf::of('string')->nullable()->from([null]) (if supported; check docs for nullable methods).Class Validation Quirks:
User::class (not 'User').App\Models\User::class, not User.Custom Callables:
bool. Silent failures (returning false without throwing) may hide bugs.Empty Arrays:
ArrayOf::of('int')->from([]) passes (empty arrays are valid).if (empty($array)) throw new \InvalidArgumentException('Array cannot be empty');
Silent Failures:
validate() returns bool, while from() throws.try-catch for graceful handling:
try {
$typedArray = ArrayOf::of('int')->from($input);
} catch (InvalidTypeException $e) {
report($e); // Log without crashing
return response()->json(['error' => 'Invalid data'], 400);
}
Type Hints:
@throws to methods using php-array-of:
/**
* @throws InvalidTypeException
*/
public function process(array $items) {
$validItems = ArrayOf::of(Item::class)->from($items);
// ...
}
Custom Exceptions:
ArrayOf\Exception\InvalidTypeException for domain-specific errors:
class InvalidOrderItemException extends InvalidTypeException {}
ArrayOf::of(OrderItem::class)->onInvalidThrow(new InvalidOrderItemException());
Lazy Validation:
class LazyArray {
private $array;
private $validator;
public function __construct(array $array, $type) {
$this->array = $array;
$this->validator = ArrayOf::of($type);
}
public function get() {
return $this->validator->from($this->array);
}
}
Integration with Laravel Collectives:
Illuminate\Support\Collection for fluent validation:
$collection = collect($rawData)
->pipe(function ($coll) {
return ArrayOf::of('string')->from($coll->all());
});
'int', 'bool'), classes (User::class), and callables.ArrayOf::of(function ($value) {
return $value instanceof User && $value->isActive();
})->from($users);
Combine with Laravel’s ensure():
$user = User::query()->findOrFail($id)->ensure(fn ($user) =>
ArrayOf::of(User::class)->from([$user])
);
API Response Wrapping: Ensure API responses are typed:
return response()->json([
'data' => ArrayOf::of(UserResource::class)->from(UserResource::collection($users))
]);
Testing:
ArrayOf in unit tests to simulate validation failures:
$this->partialMock(ArrayOf::class, 'of')
->shouldReceive('from')
->andThrow(new InvalidTypeException());
How can I help you explore Laravel packages today?