PHPackages                             bensedev/type-guard - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. bensedev/type-guard

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

bensedev/type-guard
===================

Type-safe value validation and coercion for PHP with PHPStan support

v1.0.0(9mo ago)0113[2 PRs](https://github.com/BenSeDev/type-guard/pulls)1MITPHPPHP ^8.4CI passing

Since Oct 6Pushed 7mo agoCompare

[ Source](https://github.com/BenSeDev/type-guard)[ Packagist](https://packagist.org/packages/bensedev/type-guard)[ RSS](/packages/bensedev-type-guard/feed)WikiDiscussions main Synced 2d ago

READMEChangelogDependencies (5)Versions (4)Used By (1)

TypeGuard
=========

[](#typeguard)

**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?
--------------

[](#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
------------

[](#installation)

Install via Composer:

```
composer require bensedev/type-guard
```

Usage
-----

[](#usage)

### Ensure - Safe Defaults

[](#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

[](#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

[](#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
-------------------

[](#real-world-examples)

### API Response Handling

[](#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

[](#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

[](#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

[](#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

[](#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
-------------

[](#api-reference)

### Ensure Methods

[](#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

[](#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

[](#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?
------------------

[](#when-to-use-which)

Use CaseUse `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
-------

[](#testing)

Run the test suite:

```
composer test
```

Run PHPStan:

```
composer analyse
```

Run Laravel Pint:

```
composer format
```

Requirements
------------

[](#requirements)

- PHP 8.4 or higher

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

Credits
-------

[](#credits)

- [bensedev](https://github.com/bensedev)

Support
-------

[](#support)

If you discover any issues, please open an issue on GitHub.

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance62

Regular maintenance activity

Popularity11

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

271d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/27857775?v=4)[Ben Serlippens](/maintainers/bensedev)[@BenSeDev](https://github.com/BenSeDev)

---

Top Contributors

[![BenSeDev](https://avatars.githubusercontent.com/u/27857775?v=4)](https://github.com/BenSeDev "BenSeDev (2 commits)")

---

Tags

phptypePHPStanvalidationguardtype-safetyensure

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/bensedev-type-guard/health.svg)

```
[![Health](https://phpackages.com/badges/bensedev-type-guard/health.svg)](https://phpackages.com/packages/bensedev-type-guard)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
