Declarar estructuras de matriz en PHP

Feb 20 2023
Desafortunadamente, no es posible definir tipos de matrices explícitos como lo hacen otros lenguajes de programación como Java: con suerte, algunas herramientas como PHPStan o Psalm nos ayudan a analizar el código de forma estática, lo que significa que no ejecutan sino que verifican el código en busca de inconsistencias basadas en en los comentarios de PHP. Hay 3 formas diferentes de definir el tipo de elementos en una matriz de PHP: esta es una forma elegante de definir una lista de elementos de un cierto tipo, el problema es que se vuelve ambigua y los clientes que usan este tipo de lista no pueden saber si Las claves son números enteros, flotantes o cadenas.

Desafortunadamente, no es posible definir tipos de matrices explícitos como lo hacen otros lenguajes de programación, como Java:

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

Con suerte, algunas herramientas como PHPStan o Psalm nos ayudan a analizar el código de forma estática, lo que significa que no se ejecutan, sino que verifican el código en busca de inconsistencias en función de los comentarios de PHP.

Hay 3 formas diferentes de definir el tipo de elementos en una matriz de PHP:

  1. Manera heredada:User[]
  2. Forma de lista: array<int,mixed>&list<mixed>
  3. Forma del objeto:array{0:int, foo:?string, bar:mixed}

Esta es una forma elegante de definir una lista de elementos de cierto tipo, el problema es que se vuelve ambigua y los clientes que usan este tipo de lista no pueden saber si las claves son números enteros, flotantes o cadenas.
Además, los analizadores estáticos no fallarán si intenta obtener un elemento por clave con un tipo incorrecto, en estos escenarios, sugeriría usar array<int,mixed>.

Al final, cuanto más explícito, mejor.

/** @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 

Usamos listas cuando tenemos una matriz de elementos con el mismo tipo.
Para hacer eso, usamos la sintaxis de diamantes para declarar los tipos de clave y el valor: array<int,mixed>. Hay un atajo muy útil cuando la clave es un entero incremental automático: 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);

Usamos la forma del objeto cuando la matriz no es una colección de objetos sino un mapa que contiene información.
Para hacer eso, usamos la sintaxis de corchetes y definimos el nombre de la clave y el tipo, podemos dividir por comas si hay varios elementos: .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;
}

Por supuesto, es posible representar cualquier estructura de matriz compleja en PHP con estos comentarios de PHP. Ejemplo:

/**
 * @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(),
        ],
    ],
    // ...
];

Recursos:

  • Salmo | formas de matriz
  • PHPStan | Tipos de PHPDoc