¿Qué hacen los tipos estrictos en PHP?

147

He visto la siguiente nueva línea en PHP 7, pero nadie realmente explica lo que significa. Lo busqué en Google y de lo único que hablan es si lo habilitarás o no como una encuesta.

declare(strict_types = 1);

¿Qué hace? ¿Cómo afecta mi código? ¿Debería hacerlo?

Alguna explicación estaría bien.

sufuko
fuente
55
eche un vistazo a esto también php.net/manual/en/… para strictly_types directive
scaisEdge

Respuestas:

153

Del blog Treehouse :

Con PHP 7 ahora hemos agregado tipos escalares. Específicamente: int, float, string y bool.

Al agregar sugerencias de tipo escalar y habilitar requisitos estrictos, se espera que se puedan escribir programas PHP más correctos y autodocumentados. También le da más control sobre su código y puede hacer que el código sea más fácil de leer.

Por defecto, las declaraciones de tipo escalares no son estrictas, lo que significa que intentarán cambiar el tipo original para que coincida con el tipo especificado por la declaración de tipo. En otras palabras, si pasa una cadena que comienza con un número en una función que requiere un flotante, tomará el número desde el principio y eliminará todo lo demás. Pasar un flotante a una función que requiere un int se convertirá en int (1).

Por defecto, PHP lanzará valores del tipo incorrecto en el tipo escalar esperado si es posible. Por ejemplo, una función que recibe un número entero para un parámetro que espera una cadena obtendrá una variable de tipo cadena.

Tipos estrictos deshabilitados ( eval ):

<?php

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

  echo AddIntAndFloat(1.4, '2');
  /*
  * without strict typing, PHP will change float(1.4) to int(1)
  * and string('2') to float(2.0) and returns int(3)
  */

Es posible habilitar el modo estricto por archivo. En modo estricto, solo se aceptará una variable de tipo exacto de la declaración de tipo, o se lanzará un TypeError. La única excepción a esta regla es que se puede dar un número entero a una función que espera un flotante. Las llamadas a funciones desde las funciones internas no se verán afectadas por la declaración estricta_tipos.

Para habilitar el modo estricto, la declaración de declaración se usa con la declaración estricta_tipos:

Tipos estrictos habilitados ( eval ):

<?php declare(strict_types=1);

  function AddIntAndFloat(int $a, float $b): int
  {
      return (string) $a + $b;
  }

  echo AddIntAndFloat(1.4,'2');
  // Fatal error: Uncaught TypeError: Argument 1 passed to AddIntAndFloat() must be of the type int, float given
  echo AddIntAndFloat(1,'2');
  // Fatal error: Uncaught TypeError: Argument 2 passed to AddIntAndFloat() must be of the type float, string given

  // Integers can be passed as float-points :
  echo AddIntAndFloat(1,1);
  // Fatal error: Uncaught TypeError: Return value of AddIntAndFloat() must be of the type integer, string returned

Ejemplo de trabajo:

<?php

declare(strict_types=1);

function AddFloats(float $a, float $b) : float
{
    return $a+$b;
}

$float = AddFloats(1.5,2.0); // Returns 3.5

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

$int = AddFloatsReturnInt($float,1.5); // Returns 5

function Say(string $message): void // As in PHP 7.2
{
    echo $message;
}

Say('Hello, World!'); // Prints "Hello, World!"

function ArrayToStdClass(array $array): stdClass
{
    return (object) $array;
}

$object = ArrayToStdClass(['name' => 'azjezz','age' => 100]); // returns an stdClass

function StdClassToArray(stdClass $object): array
{
    return (array) $object;
}

$array = StdClassToArray($object); // Returns array

function ArrayToObject(array $array): object // As of PHP 7.2
{
    return new ArrayObject($array);
}

function ObjectToArray(ArrayObject $object): array
{
    return $object->getArrayCopy();
}

var_dump( ObjectToArray( ArrayToObject( [1 => 'a' ] ) ) ); // array(1 => 'a');
saif
fuente
3
El primer error fatal mencionado en su ejemplo de tipos estrictos habilitados es incorrecto. Como se menciona en la documentación: "La única excepción a esta regla es que se puede dar un número entero a una función que espera un flotante". La llamada a la función no fallaría en el int. Sin embargo, sería en la cuerda.
Paul
63

strict_types afecta la coerción de tipo.

El uso de sugerencias de tipo sin strict_typespuede provocar errores sutiles.

Antes de los tipos estrictos, int $xsignificaba " $xdebe tener un valor coercible para un int". Cualquier valor que se pueda forzar a an intpasaría la sugerencia de tipo, que incluye:

  • un int propio ( 242),
  • un flotador ( 10.17),
  • un bool ( true),
  • nullo
  • una cadena con dígitos iniciales ( "13 Ghosts").

Al configurar strict_types=1, le dice al motor que int $xsignifica "$ x solo debe ser una int int, no se permite ninguna coerción de tipo". Tiene una gran seguridad de que está obteniendo exactamente y solo lo que se le dio, sin ninguna conversión y pérdida potencial.

Ejemplo:

<?php
function get_quantity(): int {
    return '100 apples';
}
echo get_quantity() . PHP_EOL;

Produce un resultado potencialmente confuso:

Notice: A non well formed numeric value encountered in /Users/bishop/tmp/pmkr-994/junk.php on line 4
100

La mayoría de los desarrolladores esperarían, creo, una intpista que significa "solo un int". Pero no lo hace, significa "algo así como un int". Habilitar estricta_tipos proporciona el comportamiento probable esperado y deseado:

<?php declare(strict_types=1);

function get_quantity(): int {
    return '100 apples';
}
echo get_quantity() . PHP_EOL;

Rendimientos:

Fatal error: Uncaught TypeError: Return value of get_quantity() must be of the type int, string returned in example.php:4

Creo que hay dos lecciones aquí, si usa sugerencias de tipo:

  • Uso strict_types=1, siempre.
  • Convierta avisos en excepciones, en caso de que olvide agregar el strict_typespragma.
obispo
fuente
1
Esto ya ha sido bien respondido como hace casi un año;)
emix
66
De hecho, voté por la otra respuesta @emix. Sin embargo, sentí que no se tocó la cuestión de "debería hacerlo". También sentí que un ejemplo más compacto y sorprendente alentaría a las personas a usar strict_types.
obispo
1
Creo que esta pregunta aborda sucintamente el "¿Debería hacerlo?" parte de la pregunta del OP. Regresar a PHP después de un paréntesis y esto fue muy útil.
Darragh Enright