bensedev / type-guard
Type-safe value validation and coercion for PHP with PHPStan support
Installs: 31
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 2
pkg:composer/bensedev/type-guard
Requires
- php: ^8.4
Requires (Dev)
- laravel/pint: ^1.0
- nunomaduro/collision: ^v8.1.1
- pestphp/pest: ^v3.0.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.0.0
This package is auto-updated.
Last update: 2025-12-24 22:26:07 UTC
README
Type-safe value validation and coercion for PHP with PHPStan support
TypeGuard provides two simple classes for handling mixed types safely in PHP:
Ensure- Returns default values on type mismatch (never throws)Guard- Throws exceptions on type mismatch (strict validation)
Perfect for working with APIs, user input, or any scenario where you need to safely handle mixed types with full PHPStan/Larastan compatibility.
Why TypeGuard?
- ๐ฏ PHPStan-friendly - Proper type narrowing that static analysis understands
- ๐ก๏ธ Two approaches - Choose between safe defaults or strict validation
- ๐ Zero dependencies - Pure PHP 8.4+
- ๐งช Fully tested - Comprehensive test coverage
- ๐ฆ Lightweight - Minimal footprint, maximum utility
Installation
Install via Composer:
composer require bensedev/type-guard
Usage
Ensure - Safe Defaults
Use Ensure when you want safe fallback values instead of exceptions:
use Bensedev\TypeGuard\Ensure; // API response with mixed types $data = json_decode($apiResponse, true); // Safely extract values with defaults $name = Ensure::string($data['name'] ?? null); // '' if not string $age = Ensure::int($data['age'] ?? null); // 0 if not int $price = Ensure::float($data['price'] ?? null); // 0.0 if not float $active = Ensure::bool($data['active'] ?? null); // false if not bool $tags = Ensure::array($data['tags'] ?? null); // [] if not array
Custom Defaults
use Bensedev\TypeGuard\Ensure; $name = Ensure::string($value, 'Unknown'); $age = Ensure::int($value, 18); $price = Ensure::float($value, 9.99); $active = Ensure::bool($value, true); $tags = Ensure::array($value, ['default']);
Guard - Strict Validation
Use Guard when you want to enforce types and throw exceptions:
use Bensedev\TypeGuard\Guard; use Bensedev\TypeGuard\Exceptions\TypeMismatchException; try { $userId = Guard::int($request->input('user_id')); $email = Guard::string($request->input('email')); $settings = Guard::array($request->input('settings')); // Use the validated values processUser($userId, $email, $settings); } catch (TypeMismatchException $e) { // Handle type mismatch // e.g., "Expected value of type "int", but got "string"" }
Real-World Examples
API Response Handling
use Bensedev\TypeGuard\Ensure; class UserApiResponse { public function __construct(array $data) { // Safely extract with defaults - no null checks needed! $this->id = Ensure::int($data['id'] ?? null); $this->name = Ensure::string($data['name'] ?? null, 'Anonymous'); $this->email = Ensure::string($data['email'] ?? null); $this->age = Ensure::int($data['age'] ?? null, 0); $this->isActive = Ensure::bool($data['is_active'] ?? null); $this->roles = Ensure::array($data['roles'] ?? null); } }
Form Validation with Guards
use Bensedev\TypeGuard\Guard; use Bensedev\TypeGuard\Exceptions\TypeMismatchException; class CreateUserRequest { public function validate(array $input): array { try { return [ 'name' => Guard::string($input['name'] ?? null), 'age' => Guard::int($input['age'] ?? null), 'email' => Guard::string($input['email'] ?? null), 'settings' => Guard::array($input['settings'] ?? null), ]; } catch (TypeMismatchException $e) { throw new ValidationException('Invalid input: ' . $e->getMessage()); } } }
Laravel Controller Example
use Bensedev\TypeGuard\Ensure; use Bensedev\TypeGuard\Guard; class ProductController extends Controller { public function store(Request $request) { // Strict validation for required fields $name = Guard::string($request->input('name')); $price = Guard::float($request->input('price')); // Safe defaults for optional fields $description = Ensure::string($request->input('description'), 'No description'); $stock = Ensure::int($request->input('stock'), 0); $tags = Ensure::array($request->input('tags')); Product::create([ 'name' => $name, 'price' => $price, 'description' => $description, 'stock' => $stock, 'tags' => $tags, ]); } }
PHPStan Integration
TypeGuard works perfectly with PHPStan/Larastan for proper type narrowing:
use Bensedev\TypeGuard\Ensure; use Bensedev\TypeGuard\Guard; function processData(mixed $input): void { // PHPStan knows $value is string after this $value = Ensure::string($input); strlen($value); // โ PHPStan happy // PHPStan knows $count is int after this $count = Guard::int($input); $count + 10; // โ PHPStan happy }
Working with JSON
use Bensedev\TypeGuard\Ensure; $json = '{"name":"John","age":"not a number","tags":["php","laravel"]}'; $data = json_decode($json, true); $name = Ensure::string($data['name']); // 'John' $age = Ensure::int($data['age'], 25); // 25 (default, because 'not a number' isn't int) $tags = Ensure::array($data['tags']); // ['php', 'laravel'] $active = Ensure::bool($data['active']); // false (key doesn't exist)
API Reference
Ensure Methods
All methods accept a mixed value and return the requested type (never null):
Ensure::string(mixed $value, string $default = ''): string Ensure::int(mixed $value, int $default = 0): int Ensure::float(mixed $value, float $default = 0.0): float Ensure::bool(mixed $value, bool $default = false): bool Ensure::array(mixed $value, array $default = []): array
Guard Methods
All methods accept a mixed value and return the requested type or throw:
Guard::string(mixed $value): string Guard::int(mixed $value): int Guard::float(mixed $value): float Guard::bool(mixed $value): bool Guard::array(mixed $value): array
Note: Guard::float() accepts both float and int values (converts int to float).
Exceptions
TypeMismatchException extends InvalidArgumentException
Thrown by Guard methods when type validation fails. Message format:
Expected value of type "int", but got "string"
When to Use Which?
| Use Case | Use Ensure |
Use Guard |
|---|---|---|
| Optional API fields | โ | โ |
| Required API fields | โ | โ |
| User input (optional) | โ | โ |
| User input (required) | โ | โ |
| Config with defaults | โ | โ |
| Strict validation | โ | โ |
| Working with legacy code | โ | โ |
Testing
Run the test suite:
composer test
Run PHPStan:
composer analyse
Run Laravel Pint:
composer format
Requirements
- PHP 8.4 or higher
License
The MIT License (MIT). Please see License File for more information.
Credits
Support
If you discover any issues, please open an issue on GitHub.