Helper DataFilter


1Présentation

Helper servant à valider des données, pour vérifier qu'elle respectent un contrat. Cela peut-être utile pour vérifier que les données en entrée d'une API sont conformes à ce qui est attendu.


2Exemples de contrats

2.1Validation automatique (pass-through)

null

2.2Types

Types scalaires :

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

Tous les types sont nullables en préfixant un point d'interrogation :

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

Il est possible d'utiliser des types multiples. Par exemple :

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

2.3Paramètres

En plus de la définition de type, les contrats peuvent prendre des paramètres.

Les paramètres peuvent s'écrire de deux manières différentes :

  • Ils peuvent être ajouté à la chaîne de configuration du contrat, à la suite de la définition de type. Les paramètres sont alors séparés par des caractères point-virgule (;), et le nom du paramètre est séparé de sa valeur par un caractère deux-points (:).
  • Il est aussi possible d'écrire les contrats sous forme de tableau associatif. Le type est les paramètres sont autant de couples clé/valeur dans le tableau. Cette écriture est obligatoire lorsqu'il faut définir des sous-contrats.

Types scalaires avec une valeur par défaut :

// booléen, faux par défaut
'bool;default:false'
[
    'type'    => 'bool',
    'default' => false,
]
// entier, 100 par défaut
'int;default:100'
[
    'type'    => 'int',
    'default' => 100,
]
// chaîne de caractère, "abc" par défaut
'string; default: abc'
[
    'type'    => 'string',
    'default' => 'abc',
]

Nombre (entier ou flottant) avec une valeur minimale et/ou maximale :

// entier supérieur ou égal à 1
'int; min:1'
[
    'type' => 'int',
    'min'  => 1,
]
// flottant valant entre -8,12 et +8,12
'float; min:-8.12; max:8.12'
[
    'type' => 'float',
    'min'  => -8.12,
    'max'  => 8.12,
]

Chaîne de caractère avec une longueur minimale et/ou maximale, ou devant valider une expression régulière :

// chaîne de 1 à 12 caractères
'string; minlen: 1; maxlen: 12'
[
    'type'   => 'string',
    'minlen' => 1,
    'maxlen' => 12,
]
// chaîne validant une expression régulière
'string; mask: ^[Bb][Oo0]..[Oo0].r$'
[
    'type' => 'string',
    'mask' => '^[Bb][Oo0]..[Oo0].r$',
]

Adresse mail, éventuellement avec une expression régulière :

'email'
// adresse mail qui se termine par "@domain.com"
'email; mask: @domain.com$'
[
    'type' => 'email',
    'mask' => '@domain.com$',
]

URL, éventuellement avec une taille minimale, une taille maximale et/ou une expression régulière :

'url'
// URL de 15 à 35 caractères se terminant par "domain.com"
'url; minlen: 15; maxlen: 35; mask: domain.com$'
[
    'type' => 'url',
    'minlen' => 15,
    'maxlen' => 35,
    'mask' => 'domain.com$',
]

Énumération, avec ou sans valeur par défaut :

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

Liste, avec un contrat servant à valider que tous ses éléments sont des nombres entiers :

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

Tableau associatif, avec la définition de ses clés :

'assoc; keys: id, name'
[
    'type' => 'assoc',
    'keys' => [
        'id',
        'name',
    ]
]

Tableau associatif, avec la définition de ses clés, certaines ayant un type associé :

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

Liste avec un contrat qui définit que ses valeurs doivent être des tableaux associatifs dont les clés sont définies :

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

Tableau associatif dont les clés sont définies. Les clés sont typées, et l'une d'elles est optionnelle :

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

Exemple complexe :

[
    'type' => 'assoc',
    'keys' => [
        'id'          => 'int',
        'isCreated'   => 'bool',
        'name'        => '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',
        ],
    ],
]

3Utilisation

L'objet \Temma\Utils\DataFilter offre une méthode statique process(). Cette méthode prend en paramètre la donnée à filtrer et le contrat à utiliser, et elle retourne la donnée filtrée. Si la syntaxe du contrat est incorrecte, la méthode lève une exception \Temma\Exceptions\IO. Si la donnée ne respecte pas le contrat, la méthode lève une exception \Temma\Exceptions\Application.

Exemple d'utilisation :

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';
/* équivalent à la ligne précédente :
$contract = [
    'type'   => 'enum',
    'values' => ['admin', 'member', 'guest'],
];
*/
try {
    $data = TµDatafilter::process($data, $contract);
} catch (TµIOException $ie) {
    TµLog::log('myapp', 'WARN', "Contrat incorrect.");
    throw $ie;
} catch (TµApplicationException $ae) {
    TµLog::log('myapp', 'WARN', "Données incorrectes.");
    throw $ae;
}