elie29/validator

A library for validating a context (POST, GET etc...) by running given rules.

v2.0.5 2019-12-06 09:41 UTC

README

Build Status Coverage Status

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) in order 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

  1. All Rules accept required, trim and messages options. required is false by default while trim is true.
  2. ArrayRule accepts min and max options. Empty value is cast to empty array [].
  3. BicRule
  4. BooleanRule accepts cast option.
  5. CallableRule accepts callable function.
  6. ChoicesRule accepts list option.
  7. CollectionRule accepts list and json options.
  8. CompareRule accepts sign and expected options. sign is CompareRule::EQ by default, expected is null by default.
  9. DateRule accepts format and separator options.
  10. EmailRule
  11. IpRule accepts flag option.
  12. JsonRule accepts decode option
  13. MatchRule requires pattern option.
  14. MultipleAndRule requires rules option.
  15. MultipleOrRule requires rules option.
  16. NumericRule accepts min, max and cast options.
  17. RangeRule accepts range option.
  18. StringRule accepts min and max options.
  19. TimeRule
  20. 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 $my_value = null;

    public function __construct(string $key, $value, array $params = [])
    {
        parent::__construct($key, $value, $params);

        if (isset($params['my_value'])) {
            $this->my_value = $params['my_value'];
        }

        // + in order to add 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 validatedContext method in order 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 than use Assertion or Assert as follow:

<?php

use Assert\Assertion;
use Elie\Validator\Rule\EmailRule;
use Elie\Validator\Rule\NumericRule;
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);

Assert::true($validator->validate(), $validator->getImplodedErrors());

// OR

Assertion::true($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 others.

The following is an example when a context - eg. $_POST - should contains a Json user data:

$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); // 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, validated value will be decoded into 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 validate 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 in order to validate a collection data (array or json) as follow:

$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);

assertThat($validator->validate(), is(true));

$users = $validator->getValidatedContext()['users'];

assertThat($users, arrayWithSize(2));

Development Prerequisites

Text file encoding

  • UTF-8

Code style formatter

  • Zend Framework coding standard

Composer commands

  • clean: Cleans all generated files
  • test: Launches unit test
  • test-coverage: Launches unit test with clover.xml file generation
  • cs-check: For code sniffer check
  • cs-fix: For code sniffer fix
  • phpstan: Launches PHP Static Analysis Tool
  • check: Launches clean, cs-check, test and phpstan