Declaraciones de tipo

Las declaraciones de tipos pueden ser añadidas a los argumentos de las funciones, valores de retorno, a partir de PHP 7.4.0, las propiedades de clase, y a partir de PHP 8.3.0, las constantes de clase. Aseguran que el valor es del tipo especificado en el momento de la llamada, de lo contrario se lanza un TypeError.

Cada tipo soportado por PHP, con la excepción del tipo resource, puede ser utilizado en una declaración de tipo por el usuario. Esta página contiene un registro de cambios de la disponibilidad de los diferentes tipos y la documentación sobre su uso en las declaraciones de tipo.

Nota:

Cuando una clase implementa un método de interfaz o reimplementa un método que ya ha sido definido por una clase padre, debe ser compatible con la definición mencionada. Un método es compatible si sigue las reglas de variance.

Historial de cambios

Versión Descripción
8.3.0 Añadido soporte para las constantes tipadas de clase, interfaz, trait y enum.
8.2.0 Añadido soporte de tipo DNF (Forma Normal Disyuntiva).
8.2.0 Añadido soporte del tipo true.
8.2.0 Los tipos null y false pueden ahora ser utilizados de manera autónoma.
8.1.0 Se ha añadido soporte para los tipos de intersección.
8.1.0 El retorno por referencia desde una función void ahora está desaconsejado.
8.1.0 Se ha añadido soporte para el tipo de retorno únicamente never.
8.0.0 Añadido soporte de mixed
8.0.0 Se ha añadido soporte para el tipo de retorno únicamente static.
8.0.0 Se ha añadido soporte para los tipos de unión.
7.4.0 Añadido soporte para el tipado de propiedades de clase.
7.2.0 Añadido soporte para object.
7.1.0 Añadido soporte para iterable.
7.1.0 Añadido soporte para void.
7.1.0 Se ha añadido soporte para los tipos nullable.

Notas de uso de los tipos atómicos

Los tipos atómicos tienen un comportamiento directo con algunas advertencias menores que se describen en esta sección.

Tipos escalares

Advertencia

Los alias para los tipos escalares (bool, int, float, string) no son soportados. En su lugar, son tratados como nombres de clase o interfaz. Por ejemplo, utilizar boolean como una declaración de tipo requiere que el valor sea una instanceof de la clase o interfaz boolean, en lugar de tipo bool :

<?php
function test(boolean $param) {}
test(true);
?>

Salida del ejemplo anterior en PHP 8:

Warning: "boolean" will be interpreted as a class name. Did you mean "bool"? Write "\boolean" to suppress this warning in /in/9YrUX on line 2

Fatal error: Uncaught TypeError: test(): Argument #1 ($param) must be of type boolean, bool given, called in - on line 3 and defined in -:2
Stack trace:
#0 -(3): test(true)
#1 {main}
  thrown in - on line 2

void

Nota:

El retorno por referencia desde una función void está obsoleto a partir de PHP 8.1.0, ya que tal función es contradictoria. Anteriormente, ya emitía los siguientes E_NOTICE cuando se llamaba : Only variable references should be returned by reference.

<?php
function &test(): void {}
?>

Tipos Callable

Este tipo no puede ser utilizado como declaración de tipo de propiedad de clase.

Nota: No es posible especificar la firma de la función.

Declaraciones de tipo sobre los parámetros de referencia

Si un parámetro pasado por referencia a una declaración de tipo, el tipo de la variable solo se verifica a la entrada de la función, al inicio de la llamada, pero no cuando la función es llamada nuevamente. Esto significa que una función puede modificar el tipo de la variable pasada por referencia.

Ejemplo #1 Parámetro tipado pasado por referencia

<?php
function array_baz(array &$param)
{
$param = 1;
}
$var = [];
array_baz($var);
var_dump($var);
array_baz($var);
?>

El resultado del ejemplo sería algo similar a:

int(1)

Fatal error: Uncaught TypeError: array_baz(): Argument #1 ($param) must be of type array, int given, called in - on line 9 and defined in -:2
Stack trace:
#0 -(9): array_baz(1)
#1 {main}
  thrown in - on line 2

Notas de uso de los tipos compuestos

Las declaraciones de tipo compuesto están sujetas a algunas restricciones y realizarán un control de redundancia en el momento de la compilación para evitar errores simples.

Precaución

Anterior a PHP 8.2.0 y la introducción de los tipos DNF, no era posible combinar las intersecciones de tipo con las uniones de tipo.

Tipos de uniones

Advertencia

No es posible combinar los dos tipos de singleton false y true juntos en una unión de tipo. Utilice en su lugar bool.

Precaución

Anterior a PHP 8.2.0, como false y null no podían ser utilizados como tipos autónomos, una unión de tipo compuesta únicamente de estos tipos no estaba permitida. Esto incluye los tipos siguientes : false, false|null y ?false.

Azúcar sintáctico de tipo nullable

Una declaración de tipo de base única puede ser marcada como valor NULL anteponiendo el tipo de un signo de interrogación (?). Así ?T y T|null son idénticos.

Nota: Esta sintaxis es soportada a partir de PHP 7.1.0, y es anterior a la soporte generalizado de los tipos de unión.

Nota:

También es posible obtener argumentos nullable haciendo de null el valor por defecto. Esto no es recomendado, ya que si el valor por defecto es modificado en una clase hija, se desencadenará una violación de compatibilidad de tipo ya que el tipo null deberá ser añadido a la declaración de tipo. Este comportamiento también está obsoleto a partir de PHP 8.4.

Ejemplo #2 Forma antigua de hacer los argumentos nullable

<?php
class C {}

function
f(C $c = null) {
var_dump($c);
}

f(new C);
f(null);
?>

El resultado del ejemplo sería:

object(C)#1 (0) {
}
NULL

Tipos duplicados y redundantes

Para detectar errores simples en las declaraciones de tipo compuesto, los tipos redundantes que pueden ser detectados sin realizar una carga de clase resultarán en un error de compilación. Esto incluye :

  • Cada tipo resuelto por nombre solo puede ocurrir una vez. Los tipos como int|string|INT o Countable&Traversable&COUNTABLE generan un error.
  • El uso de mixed resulta en un error.
  • Para los tipos de uniones :
    • Si bool es utilizado, false o true no puede ser utilizado adicionalmente.
    • Si object es utilizado, los tipos de clase no pueden ser utilizados adicionalmente.
    • Si iterable es utilizado, array y Traversable no pueden ser utilizados adicionalmente.
  • Para los tipos de intersecciones :
    • El uso de un tipo que no es un tipo de clase genera un error.
    • El uso de self, parent o static resulta en un error.
  • Para los tipos DNF :
    • Si un tipo más genérico es utilizado, el más restrictivo es redundante.
    • Uso de dos tipos de intersección idénticos.

Nota: Esto no garantiza que el tipo sea « mínimo », ya que esto requeriría cargar todos los tipos de clase utilizados.

Por ejemplo, si A y B son alias de clase, entonces A|B sigue siendo una unión de tipo legal, aunque sea posible reducir a A o B. Asimismo, si la clase B extends A {}, entonces A|B también es una unión de tipo legal, aunque podría ser reducida al tipo A únicamente.

<?php
function foo(): int|INT {} // No autorizado
function foo(): bool|false {} // No autorizado
function foo(): int&Traversable {} // No autorizado
function foo(): self&Traversable {} // No autorizado

use A as B;
function
foo(): A|B {} // No autorizado ("use" forma parte de la resolución de nombres)
function foo(): A&B {} // No autorizado ("use" forma parte de la resolución de nombres)

class_alias('X', 'Y');
function
foo(): X|Y {} // Autorizado (la redundancia solo se conoce en tiempo de ejecución)
function foo(): X&Y {} // Autorizado (la redundancia solo se conoce en tiempo de ejecución)
?>

Ejemplos

Ejemplo #3 Declaración de tipo de clase de base

<?php
class C {}
class
D extends C {}

// Esta no extiende C.
class E {}

function
f(C $c) {
echo
get_class($c)."\n";
}

f(new C);
f(new D);
f(new E);
?>

Salida del ejemplo anterior en PHP 8:

C
D

Fatal error: Uncaught TypeError: f(): Argument #1 ($c) must be of type C, E given, called in /in/gLonb on line 14 and defined in /in/gLonb:8
Stack trace:
#0 -(14): f(Object(E))
#1 {main}
  thrown in - on line 8

Ejemplo #4 Declaración de tipo de interfaz de base

<?php
interface I { public function f(); }
class
C implements I { public function f() {} }

// Esta no implementa I.
class E {}

function
f(I $i) {
echo
get_class($i)."\n";
}

f(new C);
f(new E);
?>

Salida del ejemplo anterior en PHP 8:

C

Fatal error: Uncaught TypeError: f(): Argument #1 ($i) must be of type I, E given, called in - on line 13 and defined in -:8
Stack trace:
#0 -(13): f(Object(E))
#1 {main}
  thrown in - on line 8

Ejemplo #5 Declaración de tipo de retorno de base

<?php
function sum($a, $b): float {
return
$a + $b;
}

// Note que un float será devuelto.
var_dump(sum(1, 2));
?>

El resultado del ejemplo sería:

float(3)

Ejemplo #6 Retorno de un objeto

<?php
class C {}

function
getC(): C {
return new
C;
}

var_dump(getC());
?>

El resultado del ejemplo sería:

object(C)#1 (0) {
}

Ejemplo #7 Declaración de tipo de argumento nullable

<?php
class C {}

function
f(?C $c) {
var_dump($c);
}

f(new C);
f(null);
?>

El resultado del ejemplo sería:

object(C)#1 (0) {
}
NULL

Ejemplo #8 Declaración de tipo de retorno nullable

<?php
function get_item(): ?string {
if (isset(
$_GET['item'])) {
return
$_GET['item'];
} else {
return
null;
}
}
?>

Ejemplo #9 Declaración de tipo para las propiedades de clase

<?php
class User {
public static
string $foo = 'foo';

public
int $id;
public
string $username;

public function
__construct(int $id, string $username) {
$this->id = $id;
$this->username = $username;
}
}
?>

Tipado estricto

Por defecto, PHP convertirá los valores de un tipo incorrecto al tipo escalar esperado siempre que sea posible. Por ejemplo, una función, que espera como parámetro una string, a la que se pasa un int recibirá una variable de tipo string.

Es posible activar el modo de tipado estricto archivo por archivo. En el modo estricto, solo una variable que coincida exactamente con el tipo esperado en la declaración será aceptada, de lo contrario se lanzará un TypeError. La única excepción a esta regla es que un valor de tipo int puede pasar una declaración de tipo float.

Advertencia

Las llamadas a funciones desde funciones internas no serán afectadas por la declaración strict_types.

Para activar el modo estricto, se utiliza la expresión declare con la declaración strict_types :

Nota:

El tipado estricto se aplica a las llamadas de función realizadas desde dentro de un archivo cuyo tipado estricto está activo, y no a las funciones declaradas en ese archivo. Si un archivo cuyo tipado estricto no está activado realiza una llamada a una función que ha sido definida en un archivo cuyo tipo estricto está activo, la preferencia del llamante (modo coercitivo) será respetada y el valor será forzado.

Nota:

El tipado estricto solo está definido para las declaraciones de tipo escalar.

Ejemplo #10 Tipado estricto para los valores de argumentos

<?php
declare(strict_types=1);

function
sum(int $a, int $b) {
return
$a + $b;
}

var_dump(sum(1, 2));
var_dump(sum(1.5, 2.5));
?>

Salida del ejemplo anterior en PHP 8:

int(3)

Fatal error: Uncaught TypeError: sum(): Argument #1 ($a) must be of type int, float given, called in - on line 9 and defined in -:4
Stack trace:
#0 -(9): sum(1.5, 2.5)
#1 {main}
  thrown in - on line 4

Ejemplo #11 Tipado coercitivo para los valores de argumentos

<?php
function sum(int $a, int $b) {
return
$a + $b;
}

var_dump(sum(1, 2));

// Estos serán forzados a enteros: ¡note la salida a continuación!
var_dump(sum(1.5, 2.5));
?>

El resultado del ejemplo sería:

int(3)
int(3)

Ejemplo #12 Tipado estricto para los valores de retorno

<?php
declare(strict_types=1);

function
sum($a, $b): int {
return
$a + $b;
}

var_dump(sum(1, 2));
var_dump(sum(1, 2.5));
?>

El resultado del ejemplo sería:

int(3)

Fatal error: Uncaught TypeError: sum(): Return value must be of type int, float returned in -:5
Stack trace:
#0 -(9): sum(1, 2.5)
#1 {main}
  thrown in - on line 5
add a note

User Contributed Notes 3 notes

up
26
toinenkayt (ta at ta) [iwonderr] gmail d
3 years ago
While waiting for native support for typed arrays, here are a couple of alternative ways to ensure strong typing of arrays by abusing variadic functions. The performance of these methods is a mystery to the writer and so the responsibility of benchmarking them falls unto the reader.

PHP 5.6 added the splat operator (...) which is used to unpack arrays to be used as function arguments. PHP 7.0 added scalar type hints. Latter versions of PHP have further improved the type system. With these additions and improvements, it is possible to have a decent support for typed arrays.

<?php
declare (strict_types=1);

function
typeArrayNullInt(?int ...$arg): void {
}

function
doSomething(array $ints): void {
(function (?
int ...$arg) {})(...$ints);
// Alternatively,
(fn (?int ...$arg) => $arg)(...$ints);
// Or to avoid cluttering memory with too many closures
typeArrayNullInt(...$ints);

/* ... */
}

function
doSomethingElse(?int ...$ints): void {
/* ... */
}

$ints = [1,2,3,4,null];
doSomething ($ints);
doSomethingElse (...$ints);
?>

Both methods work with all type declarations. The key idea here is to have the functions throw a runtime error if they encounter a typing violation. The typing method used in doSomethingElse is cleaner of the two but it disallows having any other parameters after the variadic parameter. It also requires the call site to be aware of this typing implementation and unpack the array. The method used in doSomething is messier but it does not require the call site to be aware of the typing method as the unpacking is performed within the function. It is also less ambiguous as the doSomethingElse would also accept n individual parameters where as doSomething only accepts an array. doSomething's method is also easier to strip away if native typed array support is ever added to PHP. Both of these methods only work for input parameters. An array return value type check would need to take place at the call site.

If strict_types is not enabled, it may be desirable to return the coerced scalar values from the type check function (e.g. floats and strings become integers) to ensure proper typing.
up
18
crash
3 years ago
The documentation lacks the information, that it's possible to change the return type of a method defined in an interface when the interface's methods return type is defined as `mixed`.

From the RFC:

"The mixed return type could be narrowed in a subclass as this is covariant and is allowed in LSP." (https://wiki.php.net/rfc/mixed_type_v2)

This means the following code is valid in PHP 8.0:

<?php

interface ITest
{
public function
apfel(): mixed; // valid as of 8.0
}

class
Test implements ITest
{
public function
apfel(): array // more explicit
{
return [];
}
}

var_dump((new Test())->apfel());
?>

You can see the result here: https://3v4l.org/PXDB6
up
1
harl at gmail dot com
4 months ago
For DNF type declarations (which lack an example), they're a mix of intersection and union types that look like this:
<?php

function send(c1|(c2&c3) $f) {}

?>

It's a union type where some of its options are intersection types, which are wrapped in parentheses ($f is something that is either a c1, or something that is both a c2 and a c3).
To Top