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

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

tobento/service-validation
==========================

Easily validating data.

2.0.2(7mo ago)01554MITPHPPHP &gt;=8.4

Since Jan 24Pushed 7mo ago1 watchersCompare

[ Source](https://github.com/tobento-ch/service-validation)[ Packagist](https://packagist.org/packages/tobento/service-validation)[ Docs](https://www.tobento.ch)[ RSS](/packages/tobento-service-validation/feed)WikiDiscussions 2.x Synced today

READMEChangelog (9)Dependencies (12)Versions (11)Used By (4)

Validation Service
==================

[](#validation-service)

The Validation Service provides an easy way to validate data.

Table of Contents
-----------------

[](#table-of-contents)

- [Getting started](#getting-started)
    - [Requirements](#requirements)
    - [Highlights](#highlights)
- [Documentation](#documentation)
    - [Validating](#validating)
        - [Single value](#single-value)
        - [Multiple values](#multiple-values)
        - [Nested values](#nested-values)
        - [Rules Definition](#rules-definition)
    - [Validator](#validator)
        - [Create Validator](#create-validator)
        - [Validator Interface](#validator-interface)
        - [Rules Aware](#rules-aware)
    - [Validation](#validation)
        - [Validation Interface](#validation-interface)
        - [Error Messages](#error-messages)
        - [Validated Data](#validated-data)
    - [Rules](#rules)
        - [Rules Interface](#rules-interface)
        - [Default Rules](#default-rules)
            - [Available Rules](#available-rules)
            - [Adding Rules](#adding-rules)
        - [Custom Rules](#custom-rules)
        - [Convert Rules To HTML Validation Attributes](#convert-rules-to-html-validation-attributes)
    - [Rule](#rule)
        - [Rule Interface](#rule-interface)
        - [Passes Rule](#passes-rule)
        - [Custom Rule](#custom-rule)
    - [Rules Parser](#rules-parser)
        - [Default Rules Parser](#default-rules-parser)
        - [Custom Rules Parser](#custom-rules-parser)
    - [Messages](#messages)
        - [Messages Factory](#messages-factory)
        - [Message Translation](#message-translation)
- [Credits](#credits)

---

Getting started
===============

[](#getting-started)

Add the latest version of the validation service running this command.

```
composer require tobento/service-validation

```

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

[](#requirements)

- PHP 8.4 or greater

Highlights
----------

[](#highlights)

- Framework-agnostic, will work with any project
- Decoupled design

Documentation
=============

[](#documentation)

Validating
----------

[](#validating)

### Single value

[](#single-value)

Easily validate a single value.

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Validation\ValidatorInterface;
use Tobento\Service\Validation\ValidationInterface;

$validator = new Validator();

var_dump($validator instanceof ValidatorInterface);
// bool(true)

$validation = $validator->validating(
    value: 'foo',
    rules: 'alpha|minLen:2',
    data: [],
    key: null
);

var_dump($validation instanceof ValidationInterface);
// bool(true)
```

Check out [Validator](#validator) to learn more about the Validator.
Check out [Validation](#validation) to learn more about the ValidationInterface.

**Parameters explanation**

ParameterDescription**value**The value to validate.**rules**The rules definition. See [Rules Definition](#rules-definition) for more detail.**data**Any data used by certain rules for validation. See [Rules](#rules) for more detail.**key**Used for error messages. See [Error Messages](#error-messages) for more detail.### Multiple values

[](#multiple-values)

Validate multiple values.

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Validation\ValidatorInterface;
use Tobento\Service\Validation\ValidationInterface;

$validator = new Validator();

var_dump($validator instanceof ValidatorInterface);
// bool(true)

$validation = $validator->validate(
    data: [
        'title' => 'Product',
        'color' => 'blue',
    ],
    rules: [
        'title' => 'alpha',
        'color' => 'in:blue:red:green',
    ]
);

var_dump($validation instanceof ValidationInterface);
// bool(true)
```

Check out [Validator](#validator) to learn more about the Validator.
Check out [Validation](#validation) to learn more about the ValidationInterface.

**Parameters explanation**

ParameterDescription**data**The data to validate.**rules**The rules definitions. See [Rules Definition](#rules-definition) for more detail.### Nested values

[](#nested-values)

If the incoming values contains "nested" data, you may specify these attributes in your rules using "dot" syntax:

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Validation\ValidatorInterface;
use Tobento\Service\Validation\ValidationInterface;

$validator = new Validator();

var_dump($validator instanceof ValidatorInterface);
// bool(true)

$validation = $validator->validate(
    data: [
        'title' => 'Product',
        'meta' => [
            'color' => 'blue',
        ],
    ],
    rules: [
        'title' => 'alpha',
        'meta.color' => 'required|in:blue:red:green',
    ]
);

var_dump($validation instanceof ValidationInterface);
// bool(true)
```

Check out [Validator](#validator) to learn more about the Validator.
Check out [Validation](#validation) to learn more about the ValidationInterface.

**Parameters explanation**

ParameterDescription**data**The data to validate.**rules**The rules definitions. See [Rules Definition](#rules-definition) for more detail.### Rules Definition

[](#rules-definition)

The [Default Rules Parser](#default-rules-parser) supports the following rules definition.
You may also check out the [Default Rules](#default-rules) to learn more about the rules it provides.
If you add rules "lazy" with dependencies you will need to use the AutowiringRuleFactory for resolving see [Default Rules](#default-rules).

**string definition**

```
use Tobento\Service\Validation\Validator;

$validation = new Validator()->validate(
    data: [
        'title' => 'Product',
    ],
    rules: [
        'title' => 'minLen:2|alpha',
    ]
);
```

**array definition with string rules**

If you need to define additional rule parameters or custom error messages, wrap the rule into an array:

```
use Tobento\Service\Validation\Validator;

$validation = new Validator()->validate(
    data: [
        'title' => 'Product',
    ],
    rules: [
        'title' => [
            // single or multiple rules
            'required|alpha',

            // using array with string rule and custom parameters.
            ['minLen:3', 'error' => 'Custom error message'],

            // using array with string rule but seperate rule parameters and custom parameters.
            ['minLen', [3], 'error' => 'Custom error message'],
        ],
    ]
);
```

**object rules**

You may define object rules implementing the [Rule Interface](#rule-interface):

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Validation\Rule\Same;

$validation = new Validator()->validate(
    data: [
        'title' => 'Product',
    ],
    rules: [
        'title' => [
            // single or multiple rules
            'required|minLen:2',

            new Same(),

            // using array for custom parameters:
            [new Same(), 'error' => 'Custom error message'],

            // using array for lazy rule:
            [[Rule::class], [3], 'error' => 'Custom error message'],

            // lazy rule with unresolvable class params:
            // [[Rule::class, ['name' => 'value']], [3], 'error' => 'Custom error message'],
        ],
    ]
);
```

**object rules with different validation method**

You may define object rules with different validation methods:

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Validation\Rule\Length;

$validation = new Validator()->validate(
    data: [
        'title' => 'Product',
    ],
    rules: [
        'title' => [
            // single or multiple rules
            'required|alpha',

            // calls the min method for validation:
            [[new Length(), 'min'], [3], 'error' => 'Custom error message'],

            // lazy rule
            [[Length::class, 'min'], [3], 'error' => 'Custom error message'],

            // lazy rule with unresolvable class params:
            [[Length::class, 'min', ['name' => 'value']], [3], 'error' => 'Custom error message'],

            // lazy rule with unresolvable class params without method to call:
            // [[Rule::class, ['name' => 'value']], [3], 'error' => 'Custom error message'],
        ],
    ]
);
```

**Parameters**

For each rule you can define custom parameters.

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Validation\Rule\Length;

$validation = new Validator()->validate(
    data: [
        'title' => 'Product',
    ],
    rules: [
        'title' => [
            [
                'minLen:3',
                'error' => ':attribute must at least contain :parameters[0] chars',

                // you might want a custom value for the attribute:
                ':attribute' => 'The TITLE',

                // you might need a custom value:
                ':parameters[0]' => 3,

                // global modifier parameters:
                'limit_length' => 100,
            ],
        ],
    ]
);
```

**Global parameters**

Sometimes you may need custom parameters for all rules.

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Validation\Rule\Length;

$validation = new Validator()->validate(
    data: [
        'title' => 'Product',
    ],
    rules: [
        'title' => [
            // single or multiple rules
            'required|alpha',

            // calls the min method for validation:
            [[new Length(), 'min'], [3]],

            // global error message:
            'error' => 'Error message',

            // global replacement parameters for messages:
            ':attribute' => 'The TITLE',

            // global modifier parameters:
            'limit_length' => 100,
        ],
    ]
);
```

Validator
---------

[](#validator)

### Create Validator

[](#create-validator)

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Validation\ValidatorInterface;
use Tobento\Service\Validation\RulesInterface;
use Tobento\Service\Validation\RulesParserInterface;
use Tobento\Service\Validation\RulesAware;
use Tobento\Service\Message\MessagesFactoryInterface;

$validator = new Validator(
    rules: null, // null|RulesInterface
    rulesParser: null, // null|RulesParserInterface
    messagesFactory: null // null|MessagesFactoryInterface
);

var_dump($validator instanceof ValidatorInterface);
// bool(true)

var_dump($validator instanceof RulesAware);
// bool(true)
```

**Parameters explanation**

ParameterDescription**rules**Provides the rules for validation. See [Rules](#rules) for more detail.**rulesParser**Parses the rules. See [Rules Parser](#rules-parser) for more detail.**messagesFactory**Creates the error messages. See [Messages Factory](#messages-factory) for more detail.### Validator Interface

[](#validator-interface)

```
use Tobento\Service\Validation\ValidatorInterface;
use Tobento\Service\Validation\ValidationInterface;
use Tobento\Service\Collection\Collection;

interface ValidatorInterface
{
    public function validating(
        mixed $value,
        string|array $rules,
        array|Collection $data = [],
        null|string $key = null
    ): ValidationInterface;

    public function validate(mixed $data, array $rules): ValidationInterface;
}
```

Check out [Validating](#validating) to learn more about the methods.
Check out [Collection Service](https://github.com/tobento-ch/service-collection) to learn more it.

### Rules Aware

[](#rules-aware)

```
use Tobento\Service\Validation\RulesInterface;

interface RulesAware
{
    public function rules(): RulesInterface;
}
```

Check out [Rules Interface](#rules-interface) to learn more about the RulesInterface.

Validation
----------

[](#validation)

### Validation Interface

[](#validation-interface)

```
use Tobento\Service\Validation\ValidationInterface;
use Tobento\Service\Validation\RuleInterface;
use Tobento\Service\Message\MessagesInterface;
use Tobento\Service\Collection\Collection;

interface ValidationInterface
{
    public function isValid(): bool;

    public function skipped(): bool;

    public function errors(): MessagesInterface;

    public function data(): Collection;

    public function valid(): Collection;

    public function invalid(): Collection;

    public function rule(): null|RuleInterface;
}
```

**Methods explanation**

ParameterDescription**isValid**Returns true if the validation is valid, otherwise false.**skipped**Returns true if the validation is skipped, otherwise false.**errors**Returns the error messages. See [Message Service](https://github.com/tobento-ch/service-message) for more detail.**data**Returns the data to validate. See [Collection Service](https://github.com/tobento-ch/service-collection) for more detail.**valid**Returns the valid data. See [Collection Service](https://github.com/tobento-ch/service-collection) for more detail.**invalid**Returns the invalid data. See [Collection Service](https://github.com/tobento-ch/service-collection) for more detail.**rule**Returns the rule or null.### Error Messages

[](#error-messages)

By default, the rules keys are used as the :attribute parameter when defined in the error messages.
For translation reason, it is not recommended to write messages like "The :attribute must ...", it is better to add "The title" in the :attribute parameter because "The" might be different for an attribute name depending on the language.

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Message\MessagesInterface;

$validation = new Validator()->validate(
    data: [
        'title' => 'Pr',
        'color' => 'green',
    ],
    rules: [
        'title' => 'minLen:3|alpha',
        'color' => 'in:blue:red',
    ]
);

$errors = $validation->errors();

var_dump($errors instanceof MessagesInterface);
// bool(true)
```

**Retrieving the first error message for a data key**

```
use Tobento\Service\Message\MessageInterface;

$errors = $validation->errors();

$error = $errors->key('title')->first();

var_dump($error instanceof MessageInterface);
// bool(true)

echo $error;
// The title must at least contain 3 chars.
```

Check out the [Message Service](https://github.com/tobento-ch/service-message) to learn more about messages in general.

**Retrieving all error messages for all data keys**

```
use Tobento\Service\Message\MessageInterface;

$errors = $validation->errors();

foreach($errors->key('title')->all() as $error) {
    var_dump($error instanceof MessageInterface);
    // bool(true)
}
```

Check out the [Message Service](https://github.com/tobento-ch/service-message) to learn more about messages in general.

**Determine if messages exist for a data key**

```
$errors = $validation->errors();

var_dump($errors->key('title')->has());
// bool(true)
```

Check out the [Message Service](https://github.com/tobento-ch/service-message) to learn more about messages in general.

**Custom error message**

```
use Tobento\Service\Validation\Validator;

$validation = new Validator()->validate(
    data: [
        'title' => 'Pr',
        'color' => 'green',
    ],
    rules: [
        'title' => [
            'alpha',
            ['minLen', [3], 'error' => ':attribute must contain at least :parameters[0] chars!']
        ],
        'color' => 'in:blue:red',
    ]
);

$errors = $validation->errors();

echo $errors->key('title')->first();
// The title must contain at least 3 chars!
```

**Custom error message parameters**

```
use Tobento\Service\Validation\Validator;

$validation = new Validator()->validate(
    data: [
        'title' => 'Pr',
        'color' => 'green',
    ],
    rules: [
        'title' => [
            'alpha',
            [
                'minLen',
                [3],
                ':attribute' => 'The TITLE',
                ':parameters[0]' => 3,
                'error' => ':attribute must contain at least :parameters[0] chars!'
            ]
        ],
        'color' => 'in:blue:red',
    ]
);

$errors = $validation->errors();

echo $errors->key('title')->first();
// The TITLE must contain at least 3 chars!
```

**Global custom error message parameters**

You might want to define global message parameters for all rules defined:

```
use Tobento\Service\Validation\Validator;

$validation = new Validator()->validate(
    data: [
        'title' => 'Pr',
        'color' => 'green',
    ],
    rules: [
        'title' => [
            'minLen:3|alpha',
            ':attribute' => 'The TITLE',
            // you might even set a global message for all rules.
            'error' => ':attribute is invalid',
        ],
        'color' => 'in:blue:red',
    ]
);

$errors = $validation->errors();

echo $errors->key('title')->first();
// The TITLE is invalid.
```

**Skipping first parameter**

You might need to skip the first value of the parameters by declaring it as **:parameters\[-1\]**.

```
use Tobento\Service\Validation\Validator;

$validation = new Validator()->validate(
    data: [
        'title' => '',
        'color' => 'green',
    ],
    rules: [
        'title' => [
            'required_ifIn:color:green:red',
            'error' => ':attribute is required as :parameters[0] is in list :parameters[-1].',
        ],
        'color' => 'in:blue:red',
    ]
);

$errors = $validation->errors();

echo $errors->key('title')->first();
// The title is required as color is in list green, red.
```

### Validated Data

[](#validated-data)

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Collection\Collection;

$validation = new Validator()->validate(
    data: [
        'title' => 'Pr',
        'color' => 'green',
    ],
    rules: [
        'title' => 'minLen:3|alpha',
        'color' => 'in:blue:red',
    ]
);

// all validated data:
var_dump($validation->data() instanceof Collection);
// bool(true)

// all valid data:
var_dump($validation->valid() instanceof Collection);
// bool(true)

// all invalid data:
var_dump($validation->invalid() instanceof Collection);
// bool(true)
```

Check out the [Collection Service](https://github.com/tobento-ch/service-collection#collection) to learn more about Collection in general.

Rules
-----

[](#rules)

Rules Interface
---------------

[](#rules-interface)

```
use Tobento\Service\Validation\RulesInterface;
use Tobento\Service\Validation\RuleInterface;
use Tobento\Service\Validation\RuleNotFoundException;
use Tobento\Service\Validation\InvalidRuleException;

/**
 * RulesInterface
 */
interface RulesInterface
{
    /**
     * Add a rule.
     *
     * @param string $name
     * @param mixed $rule
     * @return static $this
     */
    public function add(string $name, mixed $rule): static;

    /**
     * Returns the rule based on the specified rule.
     *
     * @param mixed $rule
     * @return RuleInterface
     *
     * @throws RuleNotFoundException
     * @throws InvalidRuleException
     */
    public function get(mixed $rule): RuleInterface;
}
```

### Default Rules

[](#default-rules)

```
use Tobento\Service\Validation\DefaultRules;
use Tobento\Service\Validation\RulesInterface;
use Tobento\Service\Validation\RuleFactoryInterface;

$rules = new DefaultRules(
    ruleFactory: null, //null|RuleFactoryInterface
);

var_dump($rules instanceof RulesInterface);
// bool(true)
```

**With autowiring rule factory**

The autowiring rule factory is needed if you define or add rules "lazy" with dependencies.

```
use Tobento\Service\Validation\DefaultRules;
use Tobento\Service\Validation\AutowiringRuleFactory;

// Any PSR-11 container
$container = new \Tobento\Service\Container\Container();

$rules = new DefaultRules(
    ruleFactory: new AutowiringRuleFactory($container);
);
```

#### Available Rules

[](#available-rules)

The following rules are available out of the box:

RuleParameters ExampleAllows EmptyDescription**alnum**trueThe value must be entirely alpha \[a-zA-Z\] characters and/or numbers.**alpha**trueThe value must be entirely alpha \[a-zA-Z\] characters.**alphabetic**trueThe value must be entirely alphabetic characters.**alphabeticNum**trueThe value must be entirely alphabetic characters and/or numbers.**array**trueThe value must be an array.**bool**trueThe value must be a bool.
valid: true, false, 1, 0, "1", "0"**date**trueThe value must be a valid date.**dateAfter:date**dateAfter:2021-12-24
dateAfter:2021-12-24:true (same time is past)trueThe value must be a date after the date set.**dateBefore:date**dateBefore:2021-12-24
dateBefore:2021-12-24:true (same time is past)trueThe value must be a date before the date set.**dateFormat:format**dateFormat:Y-m-d:Y.m.dtrueThe value must match any of the date formats.**decimal**trueThe value must be a decimal.
**valid:** 23.00, '22.50', 0, -0.00000, '-0.1', 50, '55'**digit**trueThe value must only contain digits \[0-9\].**each:list**each:blue:red (starting for 0)
 each:5=&gt;blue:8=&gt;redtrueEach value in the array must be within the list of values provided with the same keys.**eachIn:list**eachIn:blue:redtrueEach value in the array must be within the list of values provided.**eachWith:key\_rules:value\_rules**eachWith:int/minNum;1:alpha/maxLen;3
\['key' =&gt; 'int', 'value' =&gt; 'required|alpha'\]trueThe value must be an with the rules passing.**email**trueThe value must be a valid email address.**float**trueThe value must be a float.**htmlclean**trueThe value must be html clean.**in:list**in:blue:redtrueThe value must be in the list provided.**int**trueThe value must be a int.**json**trueThe value must be a valid JSON string.**maxItems:number**maxItems:5trueThe array must be at most the number of items.**maxLen:length**maxLen:5trueThe value must at most have the maximun length set.**maxNum:number**maxNum:5trueThe value must be at most the number set.**minItems:number**minItems:5trueThe array must have at least the number of items.**minLen:length**minLen:5trueThe value must at least have the minimum length set.**minNum:number**minNum:5trueThe value must be at least the number set.**notEmpty**trueThe value must be not be empty.**notNull**trueThe value must be not be null.**numeric**trueThe value must be numeric.**regex:pattern**regex:#^\[a-z0-9\]+$#trueThe value must match the pattern.**required**falseThe value must not be empty.**required\_ifEqual:field:value**required\_ifEqual:role:adminfalseRequired when field is equal to value.**required\_ifIn:field:value:value1**required\_ifIn:role:admin:editorfalseRequired when field is one of the values.**required\_with:field:field1**required\_with:firstname:lastnamefalseRequired when one of the fields is present and not empty.**required\_without:field:field1**required\_without:firstname:lastnamefalseRequired when one of the fields is not present and not empty.**same:field**same:user.passwordtrueThe value must be the same as the field.**scalar**trueThe value must be scalar.**sometimes**trueValidation will be skipped if the field is NOT present in the data being validated.**string**trueThe value must be a string.**uri**trueThe value must be a valid URI.**url**trueThe value must be a valid URL.#### Adding Rules

[](#adding-rules)

You may add additional rules by the following way. If you add rules "lazy" with dependencies you will need to use the AutowiringRuleFactory for resolving.

```
use Tobento\Service\Validation\DefaultRules;
use Tobento\Service\Validation\RulesInterface;
use Tobento\Service\Validation\AutowiringRuleFactory;
use Tobento\Service\Validation\Rule\Same;
use Tobento\Service\Validation\Rule\Type;

// Any PSR-11 container
$container = new \Tobento\Service\Container\Container();

$rules = new DefaultRules(
    ruleFactory: new AutowiringRuleFactory($container);
);

$rules = new DefaultRules();

$rules->add('same', new Same());

// Lazy:
$rules->add('same', Same::class);

// Custom method:
$rules->add('bool', [new Type(), 'bool']);

// Lazy custom method:
$rules->add('bool', [Type::class, 'bool']);

// Lazy custom method with unresolvable parameters:
// $rules->add('rule', [Rule::class, 'bool', ['name' => 'value']]);

// Lazy with unresolvable parameters:
// $rules->add('rule', [Rule::class, ['name' => 'value']]);
```

### Custom Rules

[](#custom-rules)

You may write your own rules class or adjusting the default rules for your needs.

```
use Tobento\Service\Validation\DefaultRules;

class CustomDefaultRules extends DefaultRules
{
    protected function getDefaultRules(): array
    {
        $rules = parent::getDefaultRules();

        // adding or overwriting rules.
        $rules['bool'] = [\Tobento\Service\Validation\Rule\Type::class, 'bool'];

        return $rules;
    }
}

$rules = new CustomDefaultRules();
```

### Convert Rules To HTML Validation Attributes

[](#convert-rules-to-html-validation-attributes)

You may use the to convert rules to HTML validation attributes:

```
use Tobento\Service\Validation\Html\HtmlAttributesFactory;

$factory = new HtmlAttributesFactory();

$attributes = $factory->createAttributes(
    rules: 'required|maxLen:150',
    inputType: 'text',
    inputName: 'Title',

    // you may change to data attribute name for the messages:
    // messageDataAttributeName: 'data-errors',

    // or you may disable it at all:
    // messageDataAttributeName: null,
);

var_dump($attributes);
// array(3) {[0]=> string(8) "required" ["maxlength"]=> string(3) "150" ["data-validation-messages"]=> array(1) { ["maxlength"]=> string(41) "The Title must at most contain 150 chars." }}
```

**Supported Rules**

The following rules are supported. Any other rule will just be ignored.

`alnum`, `alpha`, `alphabetic`, `alphabeticNum`, `maxLen`, `maxNum`, `minLen`, `maxLen`, `notEmpty`, `notNull`, `required`.

**Mapping**

Validation AttributeSupported Input Types`pattern``text`, `search`, `url`, `tel`, `email`, `password``required``text`, `search`, `url`, `tel`, `email`, `password`, `date`, `month`, `week`, `time`, `datetime-local`, `number`, `checkbox`, `radio`, `select`, `textarea``minlength`, `maxlength`Except: `select``min`, `max`, `step``date`, `month`, `week`, `time`, `datetime-local`, `number`, `range`**Render Attributes**

You may install and use the [Service Tag - Attributes Class](https://github.com/tobento-ch/service-tag#attributes) to render the validation attributes on HTML form elements:

```
use Tobento\Service\Tag\Attributes;
use Tobento\Service\Validation\Html\HtmlAttributesFactory;

$factory = new HtmlAttributesFactory();

$attributes = $factory->createAttributes(
    rules: 'required|maxLen:150',
    inputType: 'text',
    inputName: 'Title',
);

$attributes = new Attributes($attributes);
var_dump((string)$attributes);
// string(130) " required maxlength="150" data-validation-messages='{"maxlength":"The Title must at most contain 150 chars."}'"
```

Rule
----

[](#rule)

### Rule Interface

[](#rule-interface)

```
use Tobento\Service\Validation\RuleInterface;

/**
 * RuleInterface
 */
interface RuleInterface
{
    /**
     * Skips validation depending on value and rule method.
     *
     * @param mixed $value The value to validate.
     * @param string $method
     * @return bool Returns true if skip validation, otherwise false.
     */
    public function skipValidation(mixed $value, string $method = 'passes'): bool;

    /**
     * Determine if the validation rule passes.
     *
     * @param mixed $value The value to validate.
     * @param array $parameters Any parameters used for the validation.
     * @return bool
     */
    public function passes(mixed $value, array $parameters = []): bool;

    /**
     * Returns the validation error messages.
     *
     * @return array
     */
    public function messages(): array;
}
```

### Passes Rule

[](#passes-rule)

With the Passes rule you can create any custom rule.

```
use Tobento\Service\Validation\Rule\Passes;

$validation = $validator->validating(
    value: 'foo',
    rules: [
        // rule does pass:
        new Passes(passes: true),

        // rule does not pass:
        new Passes(passes: false),

        // rule does pass:
        new Passes(passes: fn (mixed $value): bool => $value === 'foo'),

        // using static new method:
        Passes::new(passes: true),
    ],
);
```

**Passes parameters**

The following parameters are available:

```
use Tobento\Service\Validation\Rule\Passes;
use Tobento\Service\Validation\ValidatorInterface;
use Tobento\Service\Validation\ValidationInterface;

$rule = new Passes(passes: function(
    mixed $value,
    array $parameters,
    ValidatorInterface $validator,
    ValidationInterface $validation): bool
{
    return true;
});
```

If you have set up the validator with the [autowiring rule factory](#default-rules), the `passes` and `skipValidation` callable are autowired:

```
use Tobento\Service\Validation\Rule\Passes;

$rule = new Passes(passes: function(mixed $value, SomeService $service): bool {
    return true;
});
```

**Ensure Declared Closure Type**

By default, the declared type of a closure `$value` parameter will be automatically verified. If it does not match the input value type, the rule does not pass and the closure will never be executed.

```
use Tobento\Service\Validation\Rule\Passes;

$rule = new Passes(passes: fn (string|int $value): bool {
    return true;
});

// you may deactivate it, but then you will need to declare the value type as mixed:
$rule = new Passes(
    passes: fn (mixed $value): bool {
        return true;
    },
    verifyDeclaredType: false,
);
```

**Custom error message**

You may specify a custom error message:

```
use Tobento\Service\Validation\Rule\Passes;

$rule = new Passes(
    passes: true,
    errorMessage: 'Custom error message',
);
```

**Skip validation**

You may use the skipValidation parameter in order to skip validation under certain conditions:

```
$validation = $validator->validating(
    value: 'foo',
    rules: [
        // skips validation:
        new Passes(passes: true, skipValidation: true),

        // does not skip validation:
        new Passes(passes: true, skipValidation: false),

        // skips validation:
        new Passes(passes: true, skipValidation: fn (mixed $value): bool => $value === 'foo'),
    ],
);
```

### Custom Rule

[](#custom-rule)

```
use Tobento\Service\Validation\Rule;

class ListRule extends Rule
{
    /**
     * The error messages.
     */
    public const MESSAGES = [
        'passes' => ':attribute must be in list :parameters',
    ];

    /**
     * Determine if the validation rule passes.
     *
     * @param mixed $value The value to validate.
     * @param array $parameters Any parameters used for the validation.
     * @return bool
     */
    public function passes(mixed $value, array $parameters = []): bool
    {
        return in_array($value, $parameters);
    }
}
```

**With multiple validation methods**

See Rule\\Strings for demo.

**Needs Validation for validation**

See Rule\\Arr for demo.

**Needs Validator for validation**

See Rule\\Arr for demo.

**Skip validation when passes**

See rules for demo.

Rules Parser
------------

[](#rules-parser)

The role of the rules parser is to parse the [Rules Definition](#rules-definition).

### Default Rules Parser

[](#default-rules-parser)

See the [Rules Definition](#rules-definition) for more detail.

### Custom Rules Parser

[](#custom-rules-parser)

You may write your own parser for your needs implementing the following interface.

```
use Tobento\Service\Validation\RulesParserInterface;
use Tobento\Service\Validation\ParsedRule;
use Tobento\Service\Validation\RulesParserException;

interface RulesParserInterface
{
    /**
     * Parses the rules.
     *
     * @param string|array $rules
     * @return array
     *
     * @throws RulesParserException
     */
    public function parse(string|array $rules): array;
}
```

Messages
--------

[](#messages)

Messages are used for the validation [Error Messages](#error-messages).
Check out the [Message Service](https://github.com/tobento-ch/service-message) to learn more about messages in general.

### Messages Factory

[](#messages-factory)

With the message factory you can fully control how messages are created and modified.

```
use Tobento\Service\Validation\Message\MessagesFactory;
use Tobento\Service\Message\MessagesFactoryInterface;
use Tobento\Service\Message\MessageFactoryInterface;
use Tobento\Service\Message\ModifiersInterface;
use Psr\Log\LoggerInterface;

$messagesFactory = new MessagesFactory(
    messageFactory: null, // null|MessageFactoryInterface
    modifiers: null, // null|ModifiersInterface
    logger: null, // null|LoggerInterface
);

var_dump($messagesFactory instanceof MessagesFactoryInterface);
// bool(true)

$modifiers = $messagesFactory->modifiers();

var_dump($modifiers instanceof ModifiersInterface);
// bool(true)
```

**Parameters explanation**

ParameterDescription**messageFactory**Creating the messages. See [Message Service - Message Factory](https://github.com/tobento-ch/service-message#message-factory) for more detail.**modifiers**With modifiers you can modify messages such as translating for instance. See [Message Service - Modifiers](https://github.com/tobento-ch/service-message#modifiers) for more detail.**logger**You might set a looger to log messages.**Default modifiers**

If you do not set modifiers on the factory, the following modifiers are added:

```
use Tobento\Service\Message\Modifiers;
use Tobento\Service\Validation\Message\RuleParametersModifier;
use Tobento\Service\Message\Modifier\ParameterReplacer;

$modifiers = new Modifiers(
    // maps :attribute, :value, :parameters
    // based on the rule parameters
    new RuleParametersModifier(),

    // Default parameter replacer
    new ParameterReplacer(),
);
```

### Message Translation

[](#message-translation)

If you want to translate messages you may use the translator modifiers:
First you will need to install [Translation Service](https://github.com/tobento-ch/service-translation) though.

```
use Tobento\Service\Validation\Validator;
use Tobento\Service\Validation\Message\MessagesFactory;
use Tobento\Service\Validation\Message\RuleParametersModifier;
use Tobento\Service\Translation;
use Tobento\Service\Message\Modifier\Translator;
use Tobento\Service\Message\Modifier\ParameterTranslator;
use Tobento\Service\Message\Modifiers;
use Tobento\Service\Message\Modifier\ParameterReplacer;

$translator = new Translation\Translator(
    new Translation\Resources(
        new Translation\Resource('*', 'de', [
            'The :attribute must only contain letters [a-zA-Z]' => ':attribute muss aus Buchstaben [a-zA-Z] bestehen.',
            'title' => 'Titel',
        ]),
    ),
    new Translation\Modifiers(),
    new Translation\MissingTranslationHandler(),
    'de',
);

$messagesFactory = new MessagesFactory(
    modifiers: new Modifiers(
        new Translator(translator: $translator, src: '*'),
        new RuleParametersModifier(),
        new ParameterTranslator(
            parameters: [':attribute'],
            translator: $translator,
            src: '*'
        ),
        new ParameterReplacer(),
    )
);

$validator = new Validator(messagesFactory: $messagesFactory);

$validation = $validator->validate(
    data: [
        'title' => 'P3',
    ],
    rules: [
        'title' => 'alpha',
    ]
);

$errors = $validation->errors();

var_dump($errors->key('title')->first()->message());
// string(44) "Titel muss aus Buchstaben [a-zA-Z] bestehen."
```

Credits
=======

[](#credits)

- [Tobias Strub](https://www.tobento.ch)
- [All Contributors](../../contributors)

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance63

Regular maintenance activity

Popularity11

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity73

Established project with proven stability

 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 ~139 days

Recently: every ~59 days

Total

11

Last Release

229d ago

Major Versions

1.x-dev → 2.02025-10-01

PHP version history (2 changes)1.0.0PHP &gt;=8.0

2.0PHP &gt;=8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/055d6a1b5c2384bb179c75ab0b55914231d898fdc4dffeb30770f81200e52206?d=identicon)[TOBENTOch](/maintainers/TOBENTOch)

---

Top Contributors

[![tobento-ch](https://avatars.githubusercontent.com/u/16684832?v=4)](https://github.com/tobento-ch "tobento-ch (34 commits)")

---

Tags

phpvalidationpackagetobento

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[symfony/symfony

The Symfony PHP framework

31.4k87.2M2.2k](/packages/symfony-symfony)[laravel/framework

The Laravel Framework.

34.8k543.8M20.1k](/packages/laravel-framework)[cakephp/cakephp

The CakePHP framework

8.9k19.5M1.8k](/packages/cakephp-cakephp)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)[ecotone/ecotone

Enterprise architecture layer for Laravel and Symfony — CQRS, Event Sourcing, Durable Workflows (Sagas, Orchestrators), Projections, and Outbox messaging via PHP attributes.

564576.7k52](/packages/ecotone-ecotone)[wikimedia/parsoid

Parsoid, a bidirectional parser between wikitext and HTML5

187557.3k3](/packages/wikimedia-parsoid)

PHPackages © 2026

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