Déclarer des structures de tableaux en PHP

Feb 20 2023
Malheureusement, il n'est pas possible de définir des types de tableaux explicites comme le font d'autres langages de programmation tels que Java : avec un peu de chance, certains outils comme PHPStan ou Psalm nous aident à analyser le code de manière statique, ce qui signifie qu'ils ne s'exécutent pas mais vérifient le code pour les incohérences basées sur sur les commentaires PHP. Il y a 3 manières différentes de définir le type d'éléments dans un tableau PHP : C'est une manière à la mode de définir une liste d'éléments d'un certain type, le problème est qu'elle devient ambiguë, et les clients qui utilisent ce type de liste ne peuvent pas savoir si les clés sont des entiers, des flottants ou des chaînes.

Malheureusement, il n'est pas possible de définir des types de tableaux explicites comme le font d'autres langages de programmation tels que Java :

List<User> users = new ArrayList<>();

Heureusement, certains outils comme PHPStan ou Psalm nous aident à analyser le code de manière statique, ce qui signifie qu'ils ne s'exécutent pas mais vérifient le code pour les incohérences basées sur les commentaires PHP.

Il existe 3 manières différentes de définir le type des éléments dans un tableau PHP :

  1. Manière héritée :User[]
  2. Forme de la liste : array<int,mixed>&list<mixed>
  3. Forme de l'objet :array{0:int, foo:?string, bar:mixed}

C'est une manière à la mode de définir une liste d'éléments d'un certain type, le problème est qu'elle devient ambiguë et les clients qui utilisent ce type de liste ne peuvent pas savoir si les clés sont des entiers, des flottants ou des chaînes.
De plus, les analyseurs statiques n'échoueront pas si vous essayez d'obtenir un élément par clé avec un type incorrect, dans ces scénarios, je suggérerais d'utiliser array<int,mixed>.

Au final, plus c'est explicite, mieux c'est.

/** @var User[] $users */
$users = [ ... ];

# Are keys autoincremental integers, random numbers, maybe strings... ?
$firstUser = $users[ ? ];

# Static analyzers won't complain if you use an incorrect type 

Nous utilisons des listes lorsque nous avons un tableau d'éléments du même type.
Pour cela, nous utilisons la syntaxe diamant pour déclarer les types de la clé et la valeur : array<int,mixed>. Il existe un raccourci très pratique lorsque la clé est un entier auto-incrémental : list<mixed>.

/** @var array<string,User> $users */
$users = [
    'jesus' => new User('Jesus Valera'),
    'chema' => new User('Chema Valera'),
];

$userJesus = $users['jesus'];
$userChema = $users['chema'];

$users[0]; # ERROR: "Offset 0 does not exist on array<string, User>"

---

/** @var list<User> $users */
$users = [ ... ];

$firstUser = reset($users);
$lastUser = end($users);

Nous utilisons la forme d'objet lorsque le tableau n'est pas une collection d'objets mais une carte contenant des informations.
Pour ce faire, nous utilisons la syntaxe des accolades et définissons le nom de la clé et le type, nous pouvons séparer par des virgules s'il y a plusieurs éléments : .array{foo:int, bar:string}

/** @var array{id:int, birthdate: DateTimeImmutable} */
$additionalInfo = [ ... ];

$id = $additionalInfo['id']; # int
$birthdate = $additionalInfo['birthdate']; # DateTimeImmutable

final class User
{
    /** @var list<Comment> */
    public readonly array $comments;

    /** @var array{id:int, birthdate: DateTimeImmutable} */
    public readonly array $additionalInfo;
}

Bien sûr, il est possible de représenter n'importe quelle structure de tableau complexe en PHP avec ces commentaires PHP. Exemple:

/**
 * @var list<
 *   array{
 *     uuid?: string,
 *     content: array{name: string, foo: ?stdClass},
 *   }
 * > $array
 */
$array = [
    0 => [
        'uuid' => 'XXXX-XXX-XXX-XXXX',
        'content' => [
            'name' => 'str',
            'foo' => null,
        ],
    ],
    1 => [
        'content' => [
            'name' => 'str',
            'foo' => new stdClass(),
        ],
    ],
    // ...
];

Ressources:

  • Psaume | Formes de tableau
  • PHPStan | Types PHPDoc