DataFilter helper


1Presentation

Helper used to validate data, to verify that it respects a contract. This can be useful to verify that the input data of an API is as expected.


2Contract examples

2.1Automatic validation (pass-through)

null

2.2Types

Scalar types:

'null'
'false'
'true'
'bool'
'int'
'float'
'string'
'email'
'url'

All types are nullable by prefixing a question mark ::

'?bool'
'?false'
'?true'
'?int'
'?float'
'?string'
'?email'
'?url'
'?enum'
'?list'
'?assoc'

Multiple types can be used. For example:

'null|int|string'
'int|float'

2.3Parameters

In addition to the type definition, contracts can take parameters.

Parameters can be written in two different ways:

  • They can be added to the contract configuration string, following the type definition. Parameters are then separated by semicolons (;), and the parameter name is separated from its value by a colon (:).
  • Contracts can also be written as associative arrays. The type and parameters are as many key/value pairs in the array. This is mandatory when sub-contracts must be defined.

Scalar types with a default value:

// boolean, false by default
'bool;default:false'
[
    'type'    => 'bool',
    'default' => false,
]
// integer, 100 by default
'int;default:100'
[
    'type'    => 'int',
    'default' => 100,
]
// character string, "abc" by default
'string; default: abc'
[
    'type'    => 'string',
    'default' => 'abc',
]

Number (int or float) with a minimum and/or maximum value:

// integer greater or equal to 1
'int; min:1'
[
    'type' => 'int',
    'min'  => 1,
]
// float value between -8.12 and +8.12
'float; min:-8.12; max:8.12'
[
    'type' => 'float',
    'min'  => -8.12,
    'max'  => 8.12,
]

String with a minimum and/or maximum length, or a regular expression mask:

// string of 1 to 12 characters
'string; minlen: 1; maxlen: 12'
[
    'type'   => 'string',
    'minlen' => 1,
    'maxlen' => 12,
]
// string validating a regular expression
'string; mask: ^[Bb][Oo0]..[Oo0].r$'
[
    'type' => 'string',
    'mask' => '^[Bb][Oo0]..[Oo0].r$',
]

Email address, possibly with a regular expression:

'email'
// email address ending with "@domain.com"
'email; mask: @domain.com$'
[
    'type' => 'email',
    'mask' => '@domain.com$',
]

URL, possibly with minimum size, maximum size and/or regular expression:

'url'
// 15- to 35-character URL ending in "domain.com
'url; minlen: 15; maxlen: 35; mask: domain.com$'
[
    'type' => 'url',
    'minlen' => 15,
    'maxlen' => 35,
    'mask' => 'domain.com$',
]

Enum type with an optional default value:

'enum; values: red, green, blue; default: red'
[
    'type'    => 'enum',
    'values'  => ['red', 'green', 'blue'],
    'default' => 'red',
]

List type with a contract definition used to filter its values as integers:

[
    'type'     => 'list',
    'contract' => 'int',
]

Associative array with the definition of its keys (some of them with a defined type):

[
    'type' => 'assoc',
    'keys' => [
        'id' => 'int',
        'name',
        'dateCreation',
    ]
]

List type with a contract definition used to defined its values as associative arrays:

[
    'type'     => 'list',
    'contract' => [
        'type' => 'assoc',
        'keys' => ['id', 'name'],
    ]
]

Associative array with keys definition (all of them with a type, one is not mandatory):

[
    'type' => 'assoc',
    'keys' => [
        'id'   => 'int',
        'name' => [
            'type'      => 'string',
            'mandatory' => false,
        ]
    ]
]

Complex example:

[
       'type' => 'assoc',
       'keys' => [
               'id'          => 'int',
               'isCreated'   => 'bool',
               'name'        => [
                       'type'    => 'string',
                       'default' => 'abc',
               ],
               'color'       => [
                       'type'      => 'enum',
                       'values'    => ['red', 'green', 'blue'],
                       'default'   => 'red',
                       'mandatory' => false,
               ],
               'creator'     => [
                       'type' => 'assoc',
                       'keys' => [
                               'id' => 'int',
                               'name',
                               'dateCreation',
                       ],
               ],
               'children'    => [
                       'type'      => 'list',
                       'mandatory' => false,
                       'contract'  => [
                               'type' => 'assoc',
                               'keys' => [
                                       'id' => 'int',
                                       'name',
                               ]
                       ],
               ],
               'identifiers' => [
                       'type'     => 'list',
                       'contract' => 'int',
               ],
       ],
]

3Usage

The \Temma\Utils\DataFilter object offers a static method process(). This method takes two parameters, the data to filter and the contract to use, and returns the filtered data. If the contract's syntax is not correct, the method raises a \Temma\Exceptions\IO exception. If the data doesn't validate the contract, the method raises a \Temma\Exceptions\Application exception.

Example:

use \Temma\Utils\Datafilter as TµDataFilter;
use \Temma\Exceptions\IO as TµIOException;
use \Temma\Exceptions\Application AS TµApplicationException;
use \Temma\Base\Log as TµLog;

$contract = 'enum; values: admin, member, guest';
/* equivalent to the previous line:
$contract = [
    'type'   => 'enum',
    'values' => ['admin', 'member', 'guest'],
];
*/
try {
    $data = TµDatafilter::process($data, $contract);
} catch (TµIOException $ie) {
    TµLog::log('myapp', 'WARN', "Invalid contract.");
    throw $ie;
} catch (TµApplicationException $ae) {
    TµLog::log('myapp', 'WARN', "Invalid data.");
    throw $ae;
}