PHPackages                             clicalmani/validation - 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. clicalmani/validation

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

clicalmani/validation
=====================

Tonka Validation

v3.2.0(3mo ago)01503MITPHPPHP &gt;=8.0

Since Feb 9Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/clicalmani/validation)[ Packagist](https://packagist.org/packages/clicalmani/validation)[ RSS](/packages/clicalmani-validation/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)DependenciesVersions (17)Used By (3)

Pour ce package PHP, l'outil standard est **PHPUnit**. Voici un guide complet et concret.

---

1. Installation
---------------

[](#1-installation)

```
composer require --dev phpunit/phpunit
```

Dans `composer.json`, ajouter l'autoload de test :

```
{
    "autoload-dev": {
        "psr-4": {
            "Clicalmani\\Validation\\Tests\\": "tests/"
        }
    },
    "scripts": {
        "test": "vendor/bin/phpunit"
    }
}
```

```
composer dump-autoload
```

---

2. Configuration `phpunit.xml`
------------------------------

[](#2-configuration-phpunitxml)

```

            tests

            src

```

---

3. Structure des tests
----------------------

[](#3-structure-des-tests)

```
tests/
├── Unit/
│   ├── ValidatorTest.php
│   ├── RuleTest.php
│   └── RuleOptionTest.php
├── Integration/
│   └── ValidationFlowTest.php
└── Fixtures/
    └── Rules/
        ├── EmailRule.php     ← règle de test
        └── IntegerRule.php

```

---

4. Fixtures — règles de test
----------------------------

[](#4-fixtures--règles-de-test)

```
// tests/Fixtures/Rules/EmailRule.php
namespace Clicalmani\Validation\Tests\Fixtures\Rules;

use Clicalmani\Validation\Rule;

class EmailRule extends Rule
{
    protected static string $argument = 'email';

    public function validate(mixed &$value): bool
    {
        return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
    }

    public function options(): array
    {
        return [
            'max' => ['required' => false, 'type' => 'integer'],
        ];
    }
}
```

---

5. Les tests unitaires
----------------------

[](#5-les-tests-unitaires)

```
// tests/Unit/ValidatorTest.php
namespace Clicalmani\Validation\Tests\Unit;

use PHPUnit\Framework\TestCase;
use Clicalmani\Validation\Validator;
use Clicalmani\Validation\Exceptions\ValidationException;

class ValidatorTest extends TestCase
{
    protected function setUp(): void
    {
        // Enregistre les règles de test avant chaque test
        ValidationServiceProvider::register('email', EmailRule::class);
        ValidationServiceProvider::register('integer', IntegerRule::class);
    }

    // -------------------------------------------------------
    // sanitize()
    // -------------------------------------------------------

    /** @test */
    public function it_passes_a_valid_email(): void
    {
        $inputs = ['email' => 'alice@example.com'];

        $validator = new Validator();
        $result = $validator->sanitize($inputs, ['email' => 'required|email']);

        $this->assertTrue($result);
        $this->assertSame('alice@example.com', $inputs['email']);
    }

    /** @test */
    public function it_throws_on_invalid_email_in_throw_mode(): void
    {
        $this->expectException(ValidationException::class);

        $inputs = ['email' => 'not-an-email'];
        $validator = new Validator(Validator::ERROR_THROW);
        $validator->sanitize($inputs, ['email' => 'required|email']);
    }

    /** @test */
    public function it_collects_errors_in_silence_mode(): void
    {
        $inputs = ['email' => 'not-an-email'];
        $validator = new Validator(Validator::ERROR_SILENCE);
        $validator->sanitize($inputs, ['email' => 'required|email']);

        $this->assertTrue(Validator::hasErrors());
        $this->assertNotEmpty(Validator::errors('email'));
    }

    /** @test */
    public function it_sets_nullable_field_to_null_when_empty(): void
    {
        $inputs = ['name' => '   '];
        $validator = new Validator();
        $validator->sanitize($inputs, ['name' => 'nullable|string']);

        $this->assertNull($inputs['name']);
    }

    /** @test */
    public function it_fails_when_required_field_is_missing(): void
    {
        $this->expectException(ValidationException::class);

        $inputs = [];
        $validator = new Validator(Validator::ERROR_THROW);
        $validator->sanitize($inputs, ['email' => 'required|email']);
    }

    /** @test */
    public function it_sets_sometimes_field_to_null_when_absent(): void
    {
        $inputs = [];
        $validator = new Validator();
        $validator->sanitize($inputs, ['phone' => 'sometimes|string']);

        $this->assertNull($inputs['phone']);
    }

    // -------------------------------------------------------
    // confirmed
    // -------------------------------------------------------

    /** @test */
    public function it_passes_confirmed_field_when_matching(): void
    {
        $inputs = [
            'password'              => 'secret123',
            'password_confirmation' => 'secret123',
        ];
        $validator = new Validator();
        $result = $validator->sanitize($inputs, ['password' => 'required|confirmed|string']);

        $this->assertTrue($result);
        $this->assertArrayNotHasKey('password_confirmation', $inputs);
    }

    /** @test */
    public function it_fails_confirmed_field_when_not_matching(): void
    {
        $this->expectException(ValidationException::class);

        $inputs = [
            'password'              => 'secret123',
            'password_confirmation' => 'different',
        ];
        $validator = new Validator(Validator::ERROR_THROW);
        $validator->sanitize($inputs, ['password' => 'required|confirmed|string']);
    }

    // -------------------------------------------------------
    // errors()
    // -------------------------------------------------------

    /** @test */
    public function errors_are_reset_between_make_calls(): void
    {
        $inputs = ['email' => 'bad'];

        Validator::make(['email' => 'required|email'], $inputs, Validator::ERROR_SILENCE);
        $this->assertTrue(Validator::hasErrors());

        // Second appel — les erreurs doivent être vidées
        Validator::make(['email' => 'required|email'], ['email' => 'ok@ok.com'], Validator::ERROR_SILENCE);
        $this->assertFalse(Validator::hasErrors());
    }

    /** @test */
    public function errors_returns_specific_parameter_errors(): void
    {
        $inputs = ['email' => 'bad', 'age' => 'not-an-int'];
        Validator::make(
            ['email' => 'required|email', 'age' => 'required|integer'],
            $inputs,
            Validator::ERROR_SILENCE
        );

        $emailErrors = Validator::errors('email');
        $this->assertIsArray($emailErrors);
        $this->assertNotEmpty($emailErrors);

        // age ne doit pas contaminer email
        foreach ($emailErrors as $msg) {
            $this->assertStringNotContainsStringIgnoringCase('age', $msg);
        }
    }

    // -------------------------------------------------------
    // getArguments() / getArgument()
    // -------------------------------------------------------

    /** @test */
    public function it_extracts_arguments_from_pattern(): void
    {
        $args = Validator::getArguments('required|email|max:255');
        $this->assertTrue($args->contains('required'));
        $this->assertTrue($args->contains('email'));
    }

    /** @test */
    public function it_returns_null_for_unknown_argument(): void
    {
        $rule = (new Validator())->getRule('required|unknown_xyz', 'field');
        $this->assertNull($rule);
    }

    /** @test */
    public function it_parses_argument_options_correctly(): void
    {
        $options = Validator::getArgumentOptions('required|email|max:255', 'email');
        $this->assertArrayHasKey('max', $options);
        $this->assertSame('255', $options['max']);
    }
}
```

---

6. Test de RuleOption — cas limites
-----------------------------------

[](#6-test-de-ruleoption--cas-limites)

```
// tests/Unit/RuleOptionTest.php

class RuleOptionTest extends TestCase
{
    /** @test */
    public function it_throws_when_required_option_is_empty(): void
    {
        $this->expectException(ValidationException::class);

        $value = '';
        $option = new RuleOption('min', $value, is_required: true);
        $option->validate();
    }

    /** @test */
    public function it_casts_value_to_correct_type(): void
    {
        $value = '42';
        $option = new RuleOption('min', $value, type: 'integer');
        $option->validate();

        $this->assertSame(42, $value); // modifié par référence
    }

    /** @test */
    public function it_maps_indexed_array_to_named_keys(): void
    {
        $value = ['Alice', 25];
        $option = new RuleOption('fields', $value, keys: ['name', 'age']);
        $option->validate();

        $this->assertSame(['name' => 'Alice', 'age' => 25], $value);
    }

    /** @test */
    public function it_applies_transform_function(): void
    {
        $value = '  hello  ';
        $option = new RuleOption(
            'trim',
            $value,
            func: fn($v) => trim($v)
        );
        $option->validate();

        $this->assertSame('hello', $value);
    }
}
```

---

7. Lancer les tests
-------------------

[](#7-lancer-les-tests)

```
# Tous les tests
composer test

# Avec couverture de code (nécessite Xdebug ou PCOV)
vendor/bin/phpunit --coverage-html coverage/

# Un seul fichier
vendor/bin/phpunit tests/Unit/ValidatorTest.php

# Un seul test par nom
vendor/bin/phpunit --filter it_throws_on_invalid_email_in_throw_mode
```

---

Priorités pour commencer
------------------------

[](#priorités-pour-commencer)

Les tests les plus rentables à écrire en premier, dans l'ordre :

1. **`sanitize()` + `required`/`nullable`/`sometimes`** — c'est le chemin critique
2. **`confirmed`** — le bug potentiel le plus impactant côté utilisateur
3. **`errors()` + reset entre appels** — valide le nouveau code ajouté
4. **`RuleOption::validate()`** — beaucoup de branches, facile à tester isolément

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance81

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity52

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

Every ~29 days

Recently: every ~61 days

Total

16

Last Release

112d ago

Major Versions

v2.0.7 → v3.0.02025-06-20

### Community

Maintainers

![](https://www.gravatar.com/avatar/7316d2003f9fbef5b19aaa8caa3983f37742f2b1872e72a5390077c2975fc2e6?d=identicon)[honorable85](/maintainers/honorable85)

---

Top Contributors

[![clicalmani](https://avatars.githubusercontent.com/u/16488123?v=4)](https://github.com/clicalmani "clicalmani (28 commits)")

### Embed Badge

![Health badge](/badges/clicalmani-validation/health.svg)

```
[![Health](https://phpackages.com/badges/clicalmani-validation/health.svg)](https://phpackages.com/packages/clicalmani-validation)
```

###  Alternatives

[toplan/laravel-sms

A mobile phone number validation solution based on laravel

83580.2k2](/packages/toplan-laravel-sms)[hyperf/validation

hyperf validation

122.2M213](/packages/hyperf-validation)[udokmeci/yii2-phone-validator

Modern Yii2 Phone validator wrapper on top of PhoneNumberUtil library also used by Android devices

36227.5k1](/packages/udokmeci-yii2-phone-validator)[snowcap/vat-validation

EU VAT number validation

10112.3k](/packages/snowcap-vat-validation)

PHPackages © 2026

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