elie29 / validator
A library for validating a context (POST, GET etc...) by running given rules.
Installs: 8 743
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 1
Forks: 1
Open Issues: 0
pkg:composer/elie29/validator
Requires (Dev)
- mockery/mockery: ^1.2
- phpunit/phpunit: ^11.5
- symfony/var-dumper: ^6.4
README
Introduction
A library for validating a context (POST, GET, etc...) by running given rules.
Installation
Run the command below to install via Composer:
composer require elie29/validator
Getting Started
Validator requires one or several rules (constraints) to validate a given context.
A basic example with $_POST
<?php use Elie\Validator\Rule\EmailRule; use Elie\Validator\Rule\MultipleAndRule; use Elie\Validator\Rule\NumericRule; use Elie\Validator\Rule\RangeRule; use Elie\Validator\Rule\StringRule; use Elie\Validator\Validator; /** * A key could have multiple rules * - name could not be empty (required and minimum 1 character length) * - age could be empty (non-existent, null or '') otherwise NumericRule is applied * - age could be empty or among several values * - email is required and should be a valid string email */ $rules =[ ['name', StringRule::class, StringRule::MIN => 1, StringRule::REQUIRED => true], ['age', NumericRule::class, NumericRule::MAX => 60], ['age', RangeRule::class, RangeRule::RANGE => [30, 40, 50]], // Use composition instead of validating the key twice ['email', MultipleAndRule::class, MultipleAndRule::REQUIRED => true, MultipleAndRule::RULES => [ [StringRule::class, StringRule::MAX => 255], [EmailRule::class], ]], ]; $validator = new Validator($_POST, $rules, true); // stop processing on error. $validator->validate(); // bool depends on $_POST content
Available rules
- All Rules accept
required,trimandmessagesoptions.requiredis false by default whiletrimis true. - ArrayRule accepts
minandmaxoptions. Empty value is cast to an empty array []. - BicRule validates Bank Identifier Code ( SWIFT-BIC).
- BooleanRule accepts
castoption. - CallableRule accepts
callablefunction. - ChoicesRule accepts
listoption. - CollectionRule accepts
rulesandjsonoptions. - CompareRule accepts
signandexpectedoptions.signis CompareRule::EQ by default,expectedis null by default. - DateRule accepts
formatandseparatoroptions. - EmailRule validates email addresses.
- IpRule accepts
flagoption. - JsonRule accepts
decodeoption. - MatchRule requires
patternoption. - MultipleAndRule requires
rulesoption (all rules must pass). - MultipleOrRule requires
rulesoption (at least one rule must pass). - NumericRule accepts
min,maxandcastoptions. - RangeRule accepts
rangeoption. - StringCleanerRule removes invisible characters from strings.
- StringRule accepts
minandmaxoptions. - TimeRule validates time format.
- Your own rule
How to add a new rule
You need to implement RuleInterface or to extend AbstractRule
<?php use Elie\Validator\Rule\AbstractRule; class MyValueRule extends AbstractRule { public const INVALID_MY_VALUE = 'invalidMyValue'; protected mixed $my_value = null; public function __construct(int|string $key, mixed $value, array $params = []) { parent::__construct($key, $value, $params); if (isset($params['my_value'])) { $this->my_value = $params['my_value']; } // + to add a non-existent key $this->messages += [ $this::INVALID_MY_VALUE => '%key%: %value% my message %my_value%' ]; } public function validate(): int { $run = parent::validate(); if ($run !== $this::CHECK) { return $run; } if ($this->value !== $this->my_value) { return $this->setAndReturnError($this::INVALID_MY_VALUE, [ '%my_value%' => $this->stringify($this->my_value) ]); } return $this::VALID; } }
Validated Context
Once validate is called, we can use the validatedContext method to retrieve all validated values from the original context.
By default, all keys set in the 'rules' array will be found in the validatedContext array. However, if we don't want to append non-existing keys, we should call appendExistingItemsOnly(true) before validation.
Assertion Integration
Instead of using assertion key by key, you can validate the whole context and then use Assertion or Assert as follows:
<?php use Assert\Assertion; use Elie\Validator\Rule\EmailRule; use Elie\Validator\Rule\NumericRule; use Elie\Validator\Rule\RuleInterface; use Elie\Validator\Rule\StringRule; use Elie\Validator\Validator; use Webmozart\Assert\Assert; $rules =[ ['age', NumericRule::class, NumericRule::MAX => 60], ['name', StringRule::class, StringRule::MIN => 1, StringRule::REQUIRED => true], ['email', EmailRule::class, EmailRule::REQUIRED => true], ]; $validator = new Validator($_POST, $rules); // Using webmozart/assert Assert::true($validator->validate(), $validator->getImplodedErrors()); // OR using beberlei/assert Assertion::true($validator->validate(), $validator->getImplodedErrors()); // OR using PHPUnit in tests $this->assertSame(RuleInterface::VALID, $validator->validate(), $validator->getImplodedErrors());
Partial Validation
Sometimes we need to validate the context partially, whenever we have a JSON item or keys that depend on each other.
The following is an example when a context - e.g., $_POST - should contain JSON user data:
use Elie\Validator\Rule\JsonRule; use Elie\Validator\Rule\MatchRule; use Elie\Validator\Rule\NumericRule; use Elie\Validator\Validator; $rules = [ ['user', JsonRule::class, JsonRule::REQUIRED => true], ]; $validator = new Validator($_POST, $rules); Assertion::true($validator->validate()); // this assertion validates that the user is in JSON format $validatedPost = $validator->getValidatedContext(); // But we need to validate user data as well (suppose it should contain name and age): $rules = [ ['name', MatchRule::class, MatchRule::PATTERN => '/^[a-z]{1,20}$/i'], ['age', NumericRule::class, NumericRule::MAX => 80], ]; $validator->setRules($rules); // Decode user as it is a valid JSON $user = json_decode($validatedPost['user'], true); $validator->setContext($user); // the new context is now user data Assertion::true($validator->validate()); // this assertion validates user data /* Validate accepts a boolean argument - mergedValidatedContext - which is false by default. If set to true, $validator->getValidatedContext() would return: array:4 [▼ "email" => "elie29@gmail.com" "user" => "{"name": "John", "age": 25}" "name" => "John" "age" => 25 ] */
Partial Validation with a multidimensional array
Usually with JsonRule, we could expect a multidimensional array. In this case, the validation process will be similar to Partial Validation without merging data:
$rules = [ // With json-decode, a validated value will be decoded into an array ['users', JsonRule::class, JsonRule::REQUIRED => true, JsonRule::DECODE => true], ]; $validator = new Validator([ 'users' => '[{"name":"John","age":25},{"name":"Brad","age":42}]' ], $rules); Assertion::true($validator->validate()); // this validates that users is a valid JSON format // But we need to validate all user data as well (suppose it should contain name and age): $validator->setRules([ ['name', MatchRule::class, MatchRule::PATTERN => '/^[a-z]{1,20}$/i'], ['age', NumericRule::class, NumericRule::MAX => 80], ]); $validatedContext = $validator->getValidatedContext(); $users = $validatedContext['users']; Assertion::isArray($users); foreach ($users as $user) { // each user is a new context $validator->setContext($user); // do not merge data !! Assertion::true($validator->validate()); // we could validate all users and determine which ones are invalid! }
A new CollectionRule has been added to validate collection data (array or JSON) as follows:
$rules = [ ['users', CollectionRule::class, CollectionRule::JSON => true, CollectionRule::RULES => [ ['name', MatchRule::class, MatchRule::PATTERN => '/^[a-z]{1,20}$/i'], ['age', NumericRule::class, NumericRule::MAX => 80], ]], ]; $data = [ 'users' => '[{"name":"John","age":25},{"name":"Brad","age":42}]' ]; $validator = new Validator($data, $rules); $this->assertSame(RuleInterface::VALID, $validator->validate()); $users = $validator->getValidatedContext()['users']; $this->assertCount(2, $users);
Development Prerequisites
Text file encoding
- UTF-8
Composer commands
composer test: Runs unit tests without coveragecomposer test-coverage: Runs unit tests with code coverage (requires Xdebug)composer cover: Runs tests with coverage and starts a local server to view coverage report at http://localhost:5001composer clean: Cleans all generated files (build directory and clover.xml)