PHP 8.4.1 Released!

Declaraciones de tipo

Las declaraciones de tipo pueden ser añadidas a los argumentos de función, valores de retorno y desde PHP 7.4.0, propiedades de clase. Éstas declaraciones aseguran que el valor es del tipo especificado en el momento de la llamada, de no ser así se lanzará una excepción de tipo TypeError.

Nota:

Cuando se sobreescribe el método de un padre, el método hijo debe coincidir con cualquier tipo de declaración de retorno del padre. Si el padre no tiene definida un tipo de retorno, entonces el método hijo puede realizarla.

Tipos simples

Tipo Descripción Versión
Nombre de la Clase/Interfaz El valor debe ser una instancia de la clase o de la interfaz.  
self El valor debe ser una instanceof de la misma clase que aquella en la que se utiliza la declaración de tipo. Solamente se puede usar en clases.  
parent El valor debe ser una instanceof del padre de la clase en la que se usa la declaración de tipo. Solo se puede usar en clases.  
array El valor debe ser un array.  
callable El valor debe ser un callable válido. No puede ser usado como una declaración de tipo de propiedad de clase.  
bool El valor debe ser un valor booleano.  
float El valor debe ser un número de coma flotante.  
int El valor debe ser un número entero.  
string El valor debe ser un string.  
iterable El valor debe ser un array o una instanceof de la clase Traversable. PHP 7.1.0
object El valor debe ser un objeto. PHP 7.2.0
mixed El valor puede ser cualquier valor. PHP 8.0.0
Advertencia

No se admiten los alias para los tipos escalares anteriores. En su lugar, se tratan como nombres de clase o interfaz. Por ejemplo, usar boolean como declaración de tipo requerirá que el valor sea una instanceof de clase o interfaz boolean, en lugar de tipo bool:

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

Output of the above example in 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

mixed

mixed es equivalente a union type object|resource|array|string|int|float|bool|null. Disponible a partir de PHP 8.0.0.

Ejemplos

Ejemplo #1 Declaración de tipo de clase básica

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

// Esta clase no extiende de C.
class E {}

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

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

Output of the above example in 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 #2 Declaración de tipo de interfaz básica

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

// Esta clase no implementa I.
class E {}

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

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

Output of the above example in 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 #3 Declaración de tipo de retorno básica

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

// Se devolverá un valor float.
var_dump(suma(1, 2));
?>

El resultado del ejemplo sería:

float(3)

Ejemplo #4 Retornando un objeto

<?php
class C {}

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

var_dump(getC());
?>

El resultado del ejemplo sería:

object(C)#1 (0) {
}

Tipo Nullable

A partir de PHP 7.1.0, las declaraciones de tipo pueden ser marcadas como nullable anteponiendo un interrogante(?) en el nombre de tipo. Esto conlleva a que el valor puede ser del tipo específico o null.

Ejemplo #5 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 #6 Declaración de tipo de retorno Nullable

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

Nota:

Es posible lograr argumentos anulables haciendo null el valor por defecto. Esto no se recomienda ya que se rompe durante la herencia.

Ejemplo #7 Forma antigua de hacer argumentos nullable por defecto

<?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 compuestos

Es posible combinar tipos simples en tipos compuestos. PHP permite combinar tipos de las siguientes maneras:

  • Union de tipos simples. A partir de 8.0.0.
  • Intersección de tipos de clases (interfaces y nombres de clase). A partir de PHP 8.1.0.
Precaución

No es posible combinar tipos intersección con tipos unión.

Tipos unión

Una declaración de tipo de unión acepta valores de múltiples tipos simples diferentes, en lugar de uno solo. Los tipos de unión se especifican mediante la sintaxis T1|T2|.... Los tipos de unión están disponibles a partir de PHP 8.0.0.

Tipos unión Nullable

El tipo null se admite como parte de las uniones, de modo que T1|T2|null se puede usar para crear una unión nullable. La notación ?T existente se considera una forma abreviada del caso común de T|null.

Precaución

null no puede ser usado como tipo standalone.

false pseudo-type

The false literal type is supported as part of unions, and is included as for historical reasons many internal functions return false instead of null for failures. A classic example of such a function is strpos().

Precaución

false no se puede usar como tipo independiente (incluido tipo independiente nullable). Por este motivo false, false|null y ?false no están permitidos.

Precaución

El literal true no existe.

Tipos Intersección

Una declaración de tipo de Intersección acepta valores que satisfacen múltiples declaraciones de tipo de clase, en lugar de una sola. Los tipos Intersección se especifican usando la sintaxis T1&T2&... Los tipos Intersección están disponibles a partir de PHP 8.1.0.

Tipos duplicados y redundantes

Para detectar errores simples en declaraciones de tipos compuestos, los tipos redundantes que se puedan detectar sin realizar la carga de clase dará como resultado un error en tiempo de compilación. Esto incluye:

  • Solamente podrá resolverse un mismo tipo una vez. Los tipos como int|string|INT o Countable&Traversable&COUNTABLE darán como resultado un error.
  • Usar el tipo mixed dará como resultado un error.
  • Para los tipos Unión:
    • Si se usa el tipo bool, false no puede ser usado adicionalmente.
    • Si se usa el tipo object, los tipos para clase no pueden ser usados adicionalmente.
    • Si se usa el tipo iterable, array y Traversable no podrán ser usados adicionalmente.
  • Para los tipos Intersección:
    • El uso de un tipo que no sea un tipo de clase dará como resultado un error.
    • El uso de self, parent, o static resultará en error.

Nota: Esto no garantiza que el tipo sea "mínimo", porque hacerlo requeriría cargar todos los tipos de clase usados.

Por ejemplo, si A y B son alias de clase, entonces A|B sigue siendo un tipo de unión legal, aunque podría reducirse a A o B. De manera similar, si la clase B extends A {}, entonces A|B es también un tipo de unión legal, aunque podría reducirse a solo A.

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

use A as B;
function
foo(): A|B {} // No permitido ("use" es parte de la resolución del nombre)
function foo(): A&B {} // No permitido ("use" es parte de la resolución del nombre)

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

Tipos de solo lectura

void

void es un tipo de retorno que indica que la función no devuelve un valor. Por lo tanto, no puede ser parte de una declaración de tipo de unión. Disponible a partir de PHP 7.1.0.

Nota:

Queda obsoleto el retorno por referencia desde una funcion void a partir de PHP 8.1.0, debido a que dicha función es contradictoria. Previamente emitía el siguiente E_NOTICE cuando se llamaba: Only variable references should be returned by reference

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

never

never es un tipo de retorno que indica que la función no retorna. Esto significa que llama a exit(), lanza una excepción o es un ciclo infinito. Por lo tanto, no puede ser parte de una declaración de tipo de unión. Disponible a partir de PHP 8.1.0.

never es, en el lenguaje de la teoría de tipos, el tipo inferior. Lo que significa que es el subtipo de cualquier otro tipo y puede reemplazar cualquier otro tipo de retorno durante la herencia.

static

El valor debe ser una instancia de la misma clase en la que se llama al método. Disponible a partir de PHP 8.0.0.

Tipificación estricto

Por defecto, PHP forzará los valores del tipo incorrecto en la declaración de tipo escalar esperada si es posible. Por ejemplo, una función a la que se le da un int para un parámetro que espera una string obtendrá una variable de tipo string.

Es posible habilitar el modo estricto por archivo. En modo estricto, solo se aceptará un valor que corresponda exactamente a la declaración de tipo. De lo contrario, se lanzará un TypeError. La única excepción a esta regla es que un valor int pasará una declaración de tipo float.

Advertencia

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

Para habilitar el modo estricto, se usa el constructor declare con la declaración strict_types=1.

Nota:

El tipado estricto se aplica a las llamadas de función realizadas desde dentro del archivo con el tipado estricto habilitado, no a las funciones declaradas de ese archivo. Si un archivo sin strict_types activado realiza una llamada a una función que se definió en un archivo con strict_types activado, la preferencia será respetada (escritura coercitiva) y se forzará el valor.

Nota:

La tipificación estricta solo está definida para declaraciones de tipos escalares.

Ejemplo #8 Tipificación estricta para los valores de los argumentos

<?php
declare(strict_types=1);

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

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

Output of the above example in PHP 8:

int(3)

Fatal error: Uncaught TypeError: suma(): 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 #9 Tipificación coercitiva para valores de los argumentos

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

var_dump(suma(1, 2));

// Estos serán forzados a números enteros: ¡observe el resultado a continuación!
var_dump(suma(1.5, 2.5));
?>

El resultado del ejemplo sería:

int(3)
int(3)

Ejemplo #10 Tipificación estricta para valores de retorno

<?php
declare(strict_types=1);

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

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

El resultado del ejemplo sería:

int(3)

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

Tipificación coercitiva con tipos Unión

Cuando strict_types no está activado, las declaraciones de tipo escalar están sujetas a coerciones de tipo limitadas. Si el tipo exacto del valor no es parte de la unión, entonces el tipo de destino se elige en el siguiente orden de preferencia:

  1. int
  2. float
  3. string
  4. bool
Si el tipo existe en la unión, y el valor se puede forzar al tipo bajo la semántica de verificación de tipo existente de PHP, entonces se elige el tipo. De lo contrario, se prueba el siguiente tipo.

Precaución

Como excepción, si el valor es una cadena y tanto int como float son parte de la unión, el tipo preferido está determinado por la semántica de "cadena numérica" ​​existente. Por ejemplo, para "42" se elige int, mientras que para "42.0" se elige float.

Nota:

Los tipos que no forman parte de la lista de preferencias anterior no son objetivos elegibles para la coerción implícita. En particular, no ocurre ninguna coerción implícita para los tipos null y false.

Ejemplo #11 Ejemplo de tipos siendo forzados a formar parte del tipo Unión

<?php
// int|string
42 --> 42 // tipo exacto
"42" --> "42" // tipo exacto
new ObjectWithToString --> "Resultado de __toString()"
// object nunca compatible con int, se resuelve como string
42.0 --> 42 // float compatible con int
42.1 --> 42 // float compatible con int
1e100 --> "1.0E+100" // float demasiado largo para tipo int, se resuelve como string
INF --> "INF" // float demasiado largo para tipo int, se resuelve como string
true --> 1 // bool compatible con int
[] --> TypeError // array no compatible con int o string

// int|float|bool
"45" --> 45 // string numérico int
"45.0" --> 45.0 // string numérico float

"45X" --> true // no es string numérico, se resuelve como bool
"" --> false // no es string numérico, se resuelve como bool
"X" --> true // no es string numérico, se resuelve como bool
[] --> TypeError // array no compatible con int, float or bool
?>

Misc

Ejemplo #12 Tipificación de parámetros pasados por referencia

Los tipos declarados de parámetros pasados por referencia se verifican en la entrada de la función, pero no cuando la función devuelve el retorno, por lo que después de que la función haya devuelvo el retorno, el tipo de argumento puede haber cambiado.

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

Output of the above example in PHP 8:

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

Ejemplo #13 Capturando TypeError

<?php
declare(strict_types=1);

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

try {
var_dump(suma(1, 2));
var_dump(suma(1.5, 2.5));
} catch (
TypeError $e) {
echo
'Error: ', $e->getMessage();
}
?>

Output of the above example in PHP 8:

int(3)
Error: suma(): Argument #1 ($a) must be of type int, float given, called in - on line 10
add a note

User Contributed Notes 2 notes

up
22
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
13
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
To Top