Helper DataFilter


1Présentation

Helper servant à filtrer ou valider des données, pour vérifier qu'elles 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.


2Définition de contrats

2.1Type et paramètres

Les contrats servent à définir le type de la donnée à filtrer ou à valider. Ils s'écrivent sous forme de chaîne de caractères ou de tableau associatif.

Un contrat contient au minimum le type que la donnée doit respecter.
Exemples : 'int', 'string'

Un contrat peut aussi prendre des paramètres supplémentaires.
Les paramètres peuvent s'écrire de deux manières différentes :

  • Ils peuvent être ajoutés à 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 (:).

    Exemple : 'int; default: 3'

  • Il est aussi possible d'écrire les contrats sous forme de tableau associatif. Le type et les paramètres sont autant de couples clé-valeur dans le tableau. Cette écriture est obligatoire lorsqu'il faut définir des sous-contrats.

    Exemple : ['type' => 'int', 'default' => 3]


2.2Types nullables

Tous les types sont nullables en préfixant un point d'interrogation : '?bool', '?false', '?true'


2.3Types multiples

Il est possible d'utiliser des types multiples. Par exemple : 'null|int|string', 'int|float'


2.4Validation automatique (pass-through)

Si le contrat fourni est la valeur null (et non pas une chaîne de caractères "null"), les données en entrée sont directement passées en sortie.


2.5Mode strict

Par défaut, l'objet DataFilter fonctionne en mode non strict : si nécessaire, il fait des transtypages (par exemple, convertir en entier une chaîne de caractères contenant des chiffres) ; pour certains paramètres, il sera permissif (par exemple, si un nombre dépasse le maximum défini, il retournera le maximum au lieu de lever une erreur).

Pour exécuter le filtrage en mode strict, il faut passer le paramètre strict à true (voir comment utiliser l'objet DataFilter plus bas).

Le contrat peut aussi forcer une exécution stricte ou non stricte :

  • Pour forcer une évaluation stricte, il faut ajouter le signe égal (=) avant le type.
    Exemples : '=int', '=string'
  • Pour forcer une évaluation non stricte, il faut ajouter le signe tilde (~) avant le type.
    Exemples : '~int', '~string'

2.6Valeur par défaut

En utilisant le paramètre default, il est possible de fournir une valeur par défaut, qui sera utilisée si la donnée n'est pas valide. La valeur par défaut doit être du même type que le type qu'on cherche à valider.

Exemples :

'bool; default: false'

[
    'type'    => 'list',
    'default' => [1, 2, 3],
]

2.7Autres paramètres

  • min : Valeur minimale (pour un nombre, une date, une heure ou une coordonnée géographique).
  • max : Valeur maximale (pour un nombre, une date, une heure ou une coordonnée géographique).
  • minLen : Longueur minimale (pour une chaîne de caractères ou une URL).
  • maxLen : Longueur maximale (pour une chaîne de caractères ou une URL).
  • mask : Expression régulière (pour une chaîne de caractères, une adresse courriel ou une URL).
  • format : Format d'entrée et de sortie pour une date/heure.
  • inFormat : Format d'entrée pour une date/heure.
  • outFormat : Format de sortie pour une date/heure.
  • values : Valeurs acceptées par une énumération.
  • contract : Contrat de validation du contenu d'une liste.
  • keys : Liste des clés attendues dans un tableau associatif.

3Types scalaires

3.1null

  • La donnée en entrée doit forcément être nulle.
  • N'accepte aucun paramètre

Exemples :

'null'
['type' => 'null']

3.2false, true, bool

false
  • mode strict : la donnée doit être égale à false.
  • mode non strict : la donnée doit être égale à false ou toute valeur que PHP transtype automatiquement en false (null, 0, chaîne vide, tableau vide).
  • Paramètre accepté : default

Exemples 

// valide une donnée fausse
'false'
['type' => 'false']

// retourne false même si la donnée avait une autre valeur
'=false; default: false'
true
  • mode strict : la donnée doit être égale à true.
  • mode non strict : la donnée doit être égale à true ou toute valeur que PHP transtype automatiquement en true (nombre différent de 0, chaîne non vide, tableau non vide, objet).
  • Paramètre accepté : default

Exemples :

// valide une donnée vraie
'true'
['type' => 'true']

// valide une donnée vraie, obligatoirement en mode non strict
'~true'
bool
  • mode strict : la donnée doit être égale à true ou false.
  • mode non strict : la donnée est transtypée en booléen.
  • Paramètre accepté : default

Exemples :

// valide un booléen
'bool'
['type' => 'bool']

// booléen, faux par défaut
'bool; default: false'
[
    'type'    => 'bool',
    'default' => true,
]

3.3int, float

int
  • mode strict : la donnée doit être un entier.
  • mode non strict : la donnée doit être un booléen (converti en 0 ou 1), un entier, un flottant (converti en entier) ou une chaîne de caractères contenant des chiffres (convertie en entier).
  • Paramètres acceptés : default, min, max

Exemples :

// valide un entier
'int'

// valide un entier de manière obligatoirement non stricte, avec une valeur par défaut
'~int; default: 3'

// valide un entier de manière obligatoirement stricte, supérieur ou égal à 5
'=int; min: 5'

// entier entre 5 et 8, et une valeur par défaut
'int; min: 5; max: 8; default: 6'

// valide un entier
['type' => 'int']

// avec une valeur minimale
[
    'type' => 'int',
    'min'  => 5,
]
float
  • mode strict : la donnée doit être un flottant.
  • mode non strict : la donnée doit être un booléen (converti en 0.0 ou 1.0), un flottant, un entier (converti en flottant) ou une chaîne de caractères contenant des chiffres (convertie en flottant).
  • Paramètres acceptés : default, min, max

Exemples :

// valide un flottant
'float'

// valide un flottant de manière obligatoirement non stricte, avec une valeur minimale
'~int; min: 2.7'

// valide un flottant de manière obligatoirement stricte, avec une valeur maximale
[
    '=type' => 'float',
    'max'   => 18.5,
]

3.4string

  • mode strict : la donnée doit être une chaîne de caractères.
  • mode non strict : la donnée doit être une chaîne, un booléen (converti en "true" ou "false"), ou un scalaire (converti en chaîne).
  • Paramètres acceptés : default, minLen, maxLen, mask

Exemples :

// valide une chaîne, avec une valeur par défaut
'string; default: abc'

// valide une chaîne de 3 à 12 caractères de long
'string; minLen: 3; maxLen: 12'

// valide une chaîne qui répond à une expression régulière
'string; mask: ^[Bb][Oo0]..[Oo0].r$'
// équivalent
[
    'type' => 'string',
    'mask' => '^[Bb][Oo0]..[Oo0].r$',
]

4Types avancés

4.1email, url, uuid

email
  • La donnée doit être une adresse courriel valide.
  • Paramètre accepté : default, mask

Exemples :

// valide une adresse courriel, avec une valeur par défaut
'email; default: contact@domain.com'

// avec une expression régulière
'email; mask: @domain.com$'

// avec une expression régulière et une valeur par défaut
[
    'type'    => 'email',
    'default' => 'contact@domain.com',
    'mask'    => '@domain.com$',
]
url
  • La donnée doit être une URL valide.
  • Paramètres acceptés : default, minLen, maxLen, mask

Exemples :

// valide une URL
'url'

// URL de moins de 200 caractères de long
'url; maxLen: 200'

// avec une expression régulière
[
    'type' => 'url',
    'mask' => 'https?:..www.domain.com/.$',
]
uuid
  • La donnée doit être un UUID valide.
  • Paramètre accepté : default

Exemples :

// valide un UUID avec une valeur par défaut
'uuid; default: 123e4567-e89b-12d3-a456-426614174003'

4.2date, time, datetime

date
  • Si la donnée est un entier, un flottant ou une chaîne contenant des chiffres, elle est interprétée comme un timestamp Unix.
  • Si la donnée est une chaîne, elle doit respecter le format d'entrée (Y-m-d par défaut).
  • Mode :
    • strict : la date doit être correcte (la date 2026/12/33 est refusée).
    • non strict : la date est convertie (2026/12/33 devient 2027/01/02).
  • La donnée est retournée en respectant le format de sortie demandé (Y-m-d par défaut).
  • Paramètres acceptés : default, format, inFormat, outFormat, min, max

Exemples :

// valide une date en spécifiant le format de sortie
'date; outFormat: d/m/Y'

// en spécifiant le format d'entrée, pour une date après le premier janvier 2000
'date; inFormat: d/m/Y; min: 01/01/2000'
time
  • Si la donnée est un entier, un flottant ou une chaîne contenant des chiffres, elle est interprétée comme un timestamp Unix.
  • Si la donnée est une chaîne, elle doit respecter le format d'entrée (H:i:s par défaut).
  • Mode :
    • strict : l'heure doit être correcte (l'heure 13:65:34 est refusée).
    • non strict : l'heure est convertie (13:65:34 devient 14:05:34).
  • La donnée est retournée en respectant le format de sortie demandé (H:i:s par défaut).
  • Paramètres acceptés : default, format, inFormat, outFormat, min, max

Exemples :

// valide une heure en spécifiant le même format en entrée et en sortie
'time; format: H:i;'

// valide une heure entre 15:00 et 17:00
'time; min: 15:00:00; max: 17:00:00'
datetime
  • Si la donnée est un entier, un flottant ou une chaîne contenant des chiffres, elle est interprétée comme un timestamp Unix.
  • Si la donnée est une chaîne, elle doit respecter le format d'entrée (Y-m-d H:i:s par défaut).
  • Mode :
    • strict : la date/heure doit être correcte (la date/heure 2026/12/33 13:65:34 est refusée).
    • non strict : la date/heure est convertie (2026/12/33 13:65:34 devient 2027/01/02 14:05:34).
  • La donnée est retournée en respectant le format de sortie demandé (Y-m-d H:i:s par défaut).
  • Paramètres acceptés : default, format, inFormat, outFormat, min, max

Exemples :

// valide une date et heure en spécifiant le format d'entrée
'datetime; inFormat: d/m/Y H:i'

// valide une date et heure en fournissant un timestamp en sortie, et en spécifiant le format d'entrée et l'intervalle de temps autorisé
[
    'type'      => 'datetime',
    'inFormat'  => 'd/m/Y H:i:s',
    'outFormat' => 'U',
    'min'       => '2000-01-01 00:00',
    'max'       => '2050-12-31 23:59',
]

4.3isbn, ean

isbn
  • La donnée doit être un ISBN valide.
  • Paramètre accepté : default

Exemples :

// valide un ISBN avec un ISBN 10 par défaut
'isbn; default: 0-306-40615-2'
// pareil sans les tirets
'isbn; default: 0306406152'

// avec un ISBN 13 par défaut
'isbn; default: 978-3-16-148410-0'
// pareil sans les tirets
'isbn; default: 9783161484100'
ean
  • La donnée doit être un EAN valide.
  • Paramètre accepté : default

Exemples :

// valide un EAN avec une valeur par défaut
'ean; default: 4006381333931'

4.4ip, ipv4, ipv6

ip
  • La donnée doit être une adresse IP (IPv4 ou IPv6) valide.
  • Paramètre accepté : default

Exemples :

// valide une adresse IP avec une IPv4 par défaut
'ip: default: 127.0.0.1'

// valide une adresse IP avec une IPv6 par défaut
[
    'type'    => 'ip',
    'default' => '::1',
]
ipv4
  • La donnée doit être une adresse IPv4 valide.
  • Paramètre accepté : default

Exemple :

// valide une adresse IPv4 avec une valeur par défaut
'ipv4; default: 127.0.0.1'
ipv6
  • La donnée doit être une adresse IPv6 valide.
  • Paramètre accepté : default

Exemple :

// valide une adresse IPv6 avec une valeur par défaut
'ipv6; default: ::1'

4.5mac, port

mac
  • La donnée doit être une adresse MAC valide.
  • Paramètre accepté : default

Exemple :

// valide une adresse MAC, avec une adresse par défaut
'mac; default: 00:1A:2B:3C:4D:5E'
port
  • mode strict : la donnée doit être un entier compris entre 1 et 65 535.
  • mode non strict : la donnée doit pouvoir être convertie en entier compris entre 1 et 65 535.
  • Paramètres acceptés : default, min, max

Exemple 

// valide un port root (inférieur à 1024)
'port; max: 1024'

4.6slug, json, color

slug
  • mode strict : la donnée doit être une chaîne de caractères ne contenant que des caractères minuscules non accentués (a à z) et des tirets (-).
  • mode non strict : la donnée est convertie en utilisant la méthode \Temma\Utils\Text::urlize().
  • Paramètre accepté : default
json
  • La donnée doit être une chaîne de caractères contenant un flux JSON valide.
  • Paramètre accepté : default
color
  • La donnée doit être une chaîne de caractères contenant une couleur hexadécimale valide, commençant ou non par un caractère dièse (#).
  • La donnée retournée commence forcément par un caractère dièse, et est en minuscules.
  • Paramètre accepté : default

4.7geo, phone

geo
  • La donnée doit être une coordonnée géographique.
  • Paramètre accepté : default

Exemple 

// coordonnées de Paris par défaut
'geo; default: 48.8566, 2.3522'
phone

Valide des numéros de téléphone qui remplissent l'une de ces conditions :

  • Commencer par 00 suivi de 1 à 15 chiffres.
  • Commencer par + suivi de 1 à 15 chiffres.
  • Contenir 1 à 15 chiffres.

Sachant que :

  • mode strict : le numéro retourné est débarrassé des caractères espace, tiret, point et parenthèses.
  • mode non strict : les caractères espace, tiret, point et parenthèses sont préservés.
  • Paramètre accepté : default

5Types complexes

5.1enum

  • Énumération pouvant prendre l'une des valeurs listées.
  • Paramètres acceptés : default, values

Exemples :

// énumération avec trois valeurs possibles, et une valeur par défaut
'enum; values: red, green, blue; default: red'

// équivalent
[
    'type'    => 'enum',
    'values'  => ['red', 'green', 'blue'],
    'default' => 'red',
]

5.2array, list, assoc

array
  • mode strict : la donnée doit être un tableau, quel que soit son contenu.
  • mode non strict : si la donnée n'est pas un tableau, elle est placée dans un tableau qui est retourné.
  • Paramètre accepté : default
list
  • La donnée doit être un tableau dont tous les éléments sont du même type (défini dans un sous-contrat).
  • Paramètres acceptés : default, contract
// liste dont tous les éléments sont des nombres entiers
'list; contract: int'

// équivalent
[
    'type'     => 'list',
    'contract' => 'int',
]
assoc
  • La donnée doit être un tableau associatif, dont les clés peuvent être définies.
  • Paramètres acceptés : default, keys

Exemple : tableau associatif avec les clés 'id' et 'name'

'assoc; keys: id, name'

// équivalent
[
    'type' => 'assoc',
    'keys' => [
        'id',
        'name',
    ]
]

Exemple : Liste dont les éléments sont des tableaux associatifs dont les clés sont définies :

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

Exemple : tableau associatif avec les clés 'id' (obligatoire) et 'name' (optionnelle)

'assoc; keys: id, name?'

// équivalent
[
    'type' => 'assoc',
    'keys' => [
        'id',
        'name?',
    ]
]

// équivalent
[
    'type' => 'assoc',
    'keys' => [
        'id',
        'name' => [
            'mandatory' => false,
        ],
    ]
]

Exemple : définition du type associé à certaines clés

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

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

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

// équivalent
[
    'type' => 'assoc',
    'keys' => [
        'id'    => 'int',
        'name?' => 'string',
    ]
]

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.

Un troisième paramètre optionnel permet de spécification si la validation doit se faire en mode strict ou non. Par défaut, la validation est non stricte.

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;
}

// exécution en mode strict
try {
    $data = TµDataFilter::process($data, $contract, true);
} catch (\Exception $e) {
    TµLog::log('myapp', 'WARN', "Erreur de validation.");
    throw $e;
}