php-standard-library/type
Runtime type validation for PHP using “Parse, Don’t Validate”: coerce and assert unstructured input into well-typed data. Useful for APIs, configs, and user input with clear parsing rules, assertions, and predictable failures.
Installation
composer require php-standard-library/type
Ensure your composer.json targets PHP 8.1+ (required for named arguments and strict types).
First Use Case: API Request Validation
Replace manual type checks in FormRequest or controller methods:
use PhpStandardLibrary\Type\Type;
public function store(Request $request) {
$data = $request->validate([
'name' => 'required|string',
'tags' => 'sometimes|array',
]);
// Use package for runtime assertions
Type::assertString($data['name']);
Type::assertArray($data['tags'] ?? null, true); // Allow null
}
Where to Look First
PhpStandardLibrary\Type\Type for is*(), assert*(), and coerce*() methods.
Example: Type::isNonEmptyArray(), Type::assertCallable(), Type::coerceToInt().type package specifics).phpunit to see real-world usage patterns.Validation Workflows
assert*() methods to fail fast on malformed input.
public function update(Request $request) {
$payload = json_decode($request->getContent(), true);
Type::assertNonEmptyArray($payload, 'Invalid payload format');
// Proceed with validated data
}
Validator for hybrid validation.
Validator::extend('valid_type', function ($attribute, $value, $parameters) {
return Type::isString($value) || Type::isInt($value);
});
Data Transformation
$userId = Type::coerceToInt($request->input('user_id'), 0); // Default to 0
$isActive = Type::coerceToBool($request->input('active')); // "1" → true
DTO/Value Object Safety
public function __construct(
private string $name,
private array $metadata = []
) {
Type::assertString($name);
Type::assertArray($metadata);
}
Legacy Code Modernization
is_* checks with fluent assertions:
// Before
if (!is_array($config) || empty($config) || !isset($config['timeout'])) {
throw new InvalidArgumentException();
}
// After
Type::assertNonEmptyArray($config);
Type::assertArrayKeyExists('timeout', $config);
withValidator() to add custom rules:
public function withValidator($validator) {
$validator->after(function ($validator) {
Type::assertNonEmptyArray($this->all()['tags'] ?? null);
});
}
$this->app->bind(ValidatedData::class, function ($app) {
return new ValidatedData(
Type::coerceToInt($app['request']->input('limit'), 10)
);
});
Type methods in unit tests to isolate validation logic:
Type::shouldReceive('assertString')->once()->with($input);
$isValid = cache()->remember("type_check_{$value}", now()->addHours(1), function () {
return Type::isString($value);
});
Null Handling Quirks
Type::isArray(null) returns false (unlike is_array(null) which returns true).Type::isArray($value, true) to allow null values./**
* @param array|null $value Allows null arrays.
*/
public function process(array|null $value) { ... }
Recursive Type Checks
isArrayOf() or isObjectOf() may not handle deeply nested structures as expected.Type::deepValidate() (if available) or implement custom recursion:
function assertNestedArrays(array $data, int $depth = 0): void {
if ($depth > 5) throw new \RuntimeException('Max depth exceeded');
foreach ($data as $item) {
Type::assertArray($item);
assertNestedArrays($item, $depth + 1);
}
}
False Positives in Assertions
Type::assertCallable() may fail on valid closures or strings (e.g., "strlen").Type::isCallable() for detection, then assert separately:
if (!Type::isCallable($callback)) {
throw new \InvalidArgumentException('Callable expected');
}
Laravel-Specific Conflicts
Type or Assert to prevent conflicts.Validator rules.Unexpected Failures
\Log::debug('Value type:', [
'value' => $value,
'type' => gettype($value),
'var_dump' => var_export($value, true),
]);
Type::assertString($value);
stdClass instances.Performance Bottlenecks
Type::* calls in hot paths.Edge Cases
Type::isInt(1.0) may return false (use Type::isNumeric() if needed).Type::isResource() is rarely useful in Laravel (prefer is_resource() for files/DB handles).Custom Type Guards
class AppType {
public static function isLaravelCollection(mixed $value): bool {
return $value instanceof \Illuminate\Support\Collection;
}
}
Integration with Laravel Validation
use Illuminate\Validation\Rule;
class TypeRule extends Rule {
public function passes($attribute, $value) {
return Type::isNonEmptyArray($value);
}
}
Usage:
$request->validate([
'tags' => ['required', new TypeRule],
]);
Coercion Strategies
coerceToInt):
$value = Type::coerceToInt($input, function ($val) {
return (int) str_replace(',', '', $val); // Custom parsing
});
Null Object Pattern
Type::coerceToNull() to handle missing/empty values:
$config = Type::coerceToNull($request->input('config'), []);
if ($config === null) {
// Handle default case
}
config/type.php—customize via service providers or helper classes.string|int) over runtime checks where possible.How can I help you explore Laravel packages today?