¿Cómo resolver "debe ser una instancia de cadena, cadena dada" antes de PHP 7?

217

Aquí está mi código:

function phpwtf(string $s) {
    echo "$s\n";
}
phpwtf("Type hinting is da bomb");

Lo que resulta en este error:

Error fatal detectable: el argumento 1 pasado a phpwtf () debe ser una instancia de cadena, cadena dada

Es más que un poco Orwelliano ver que PHP reconoce y rechaza el tipo deseado al mismo tiempo. Hay cinco luces, maldita sea.

¿Cuál es el equivalente de las sugerencias de tipo para cadenas en PHP? Consideración adicional a la respuesta que explica exactamente lo que está sucediendo aquí.

potencias de sotavento
fuente
55
Bueno, eso es porque lo estás haciendo mal. Su código no debe funcionar, para empezar. Lea sobre malabarismo de tipos en documentos PHP. PHP es de tipo dinámico y de tipo débil. Puede usar (cadena) para emitir un argumento a cadena (aunque solo en el cuerpo de la función), pero solo puede insinuar objetos y matrices como lo hace en su fragmento de código.
Richard Knop
77
El problema es que cometiste un pequeño error. ¡Hay cuatro luces!
CJ Dennis
@ Gordon, probé en 5.6. Aún sin suerte.
Pacerier
@Pacerier Siga wiki.php.net/rfc para conocer los últimos desarrollos.
Gordon
3
Aparentemente, la sugerencia de tipo escalar (como OP intuitivamente esperaba que fuera algo anterior) finalmente se aprobó bajo un RFC para PHP * 7 * según la fuente . Aparentemente, el RFC aprobado también proporciona azúcar sintáctica para los valores de retorno de verificación de tipo , así como los parámetros (argumentos). Ha pasado mucho tiempo en la venida.
SeldomNeedy

Respuestas:

204

Antes de PHP 7, las sugerencias de tipo solo se pueden usar para forzar los tipos de objetos y matrices. Los tipos escalares no son de tipo imprimible. En este caso, stringse espera un objeto de la clase , pero le está dando un (escalar) string. El mensaje de error puede ser divertido, pero para empezar no se supone que funcione. Dado el sistema de tipeo dinámico, esto realmente tiene algún tipo de sentido pervertido.

Solo puede "escalar manualmente " tipos escalares:

function foo($string) {
    if (!is_string($string)) {
        trigger_error('No, you fool!');
        return;
    }
    ...
}
difunto
fuente
1
@deceze, ¿hay una sintaxis para las sugerencias de tipo inverso? Por ejemplo, cualquier cosa menos matrices.
Pacerier
@Pacerier No, no lo hay.
deceze
44
Esta respuesta no es válida para PHP 7
Jose Nobile
24

Del manual de PHP :

Las Sugerencias de tipo solo pueden ser del tipo de objeto y matriz (desde PHP 5.1). La sugerencia de tipo tradicional con int y string no es compatible.

Entonces lo tienes. El mensaje de error no es realmente útil, aunque te doy eso.

** Edición 2017 **

PHP7 introdujo más declaraciones de tipo de datos de función, y el enlace antes mencionado se ha movido a Argumentos de función: Declaraciones de tipo . Desde esa página:

Tipos válidos

  • Nombre de clase / interfaz : el parámetro debe ser una instancia de la clase o nombre de interfaz dados. (desde PHP 5.0.0)
  • self : el parámetro debe ser una instancia de la misma clase en la que se define el método. Esto solo se puede usar en métodos de clase e instancia. (desde PHP 5.0.0)
  • matriz : el parámetro debe ser una matriz. (desde PHP 5.1.0) invocable El parámetro debe ser invocable válido. PHP 5.4.0
  • bool : el parámetro debe ser un valor booleano. (desde PHP 7.0.0)
  • flotante : el parámetro debe ser un número de coma flotante. (desde PHP 7.0.0)
  • int : el parámetro debe ser un número entero. (desde PHP 7.0.0)
  • cadena : el parámetro debe ser una cadena. (desde PHP 7.0.0)
  • iterable : el parámetro debe ser una matriz o una instancia de Traversable. (desde PHP 7.1.0)

Advertencia

Los alias para los tipos escalares anteriores no son compatibles. En cambio, se tratan como nombres de clase o interfaz. Por ejemplo, usar boolean como parámetro o tipo de retorno requerirá un argumento o valor de retorno que sea una instancia de la clase o interfaz boolean, en lugar de un tipo bool:

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

El ejemplo anterior generará:

 Fatal error: Uncaught TypeError: Argument 1 passed to test() must be an instance of boolean, boolean given, called in - on line 1 and defined in -:1

La última advertencia es realmente significativa para comprender el error "Argumento debe de tipo cadena, cadena dada"; como la mayoría de los nombres de clase / interfaz están permitidos como tipo de argumento, PHP intenta localizar una "cadena" de nombre de clase, pero no puede encontrar ninguna porque es un tipo primitivo, por lo tanto falla con este incómodo error.

Yanick Rochon
fuente
8

PHP permite "insinuar" dónde proporciona una clase para especificar un objeto. De acuerdo con el manual de PHP, "Las Sugerencias de tipo solo pueden ser del tipo objeto y matriz (desde PHP 5.1). La sugerencia de tipo tradicional con int y string no es compatible". El error es confuso debido a su elección de "cadena": coloque "myClass" en su lugar y el error se leerá de manera diferente: "El argumento 1 pasado a phpwtf () debe ser una instancia de myClass, cadena dada"

Sueños surrealistas
fuente
3

Como ya han dicho otros, la sugerencia de tipos actualmente solo funciona para tipos de objetos. Pero creo que el error particular que ha activado podría estar en preparación del próximo tipo de cadena SplString .

En teoría, se comporta como una cadena, pero como es un objeto pasaría la verificación del tipo de objeto. Desafortunadamente, aún no está en PHP 5.3, podría venir en 5.4, así que no lo he probado.

mario
fuente
2

A partir de PHP 7.0 declaraciones de tipo permiten tipos escalares, por lo que estos tipos están ahora disponibles: self, array, callable, bool, float, int, string. Los primeros tres estaban disponibles en PHP 5, pero los últimos cuatro son nuevos en PHP 7. Si usa algo más (por ejemplo, integero boolean), se interpretará como un nombre de clase.

Consulte el manual de PHP para más información .

TwoStraws
fuente
0

Tal vez no sea seguro y bonito, pero si debes:

class string
{
    private $Text;
    public function __construct($value)
    {
        $this->Text = $value;
    }

    public function __toString()
    {
        return $this->Text;
    }
}

function Test123(string $s)
{
    echo $s;
}

Test123(new string("Testing"));
Patricio
fuente
55
Todavía podría crear unnew string(array(1,2,3))
Jimmy T.
0

Recibí este error al invocar una función desde un controlador Laravel a un archivo PHP.

Después de un par de horas, encontré el problema: estaba usando $ this desde una función estática.

ecairol
fuente
Using $this when not in object contextes de hecho un mensaje críptico.
Ben Fransen
0

(publicado originalmente por leepowers en su pregunta)

El mensaje de error es confuso por una gran razón:

Los nombres de tipo primitivos no están reservados en PHP

Las siguientes son todas las declaraciones de clase válidas:

class string { }
class int { }
class float { }
class double { }

Mi error fue pensar que el mensaje de error se refería únicamente al tipo primitivo de cadena: la palabra 'instancia' debería haberme dado pausa. Un ejemplo para ilustrar más a fondo:

class string { }
$n = 1234;
$s1 = (string)$n;
$s2 = new string();
$a = array('no', 'yes');
printf("\$s1 - primitive string? %s - string instance? %s\n",
        $a[is_string($s1)], $a[is_a($s1, 'string')]);
printf("\$s2 - primitive string? %s - string instance? %s\n",
        $a[is_string($s2)], $a[is_a($s2, 'string')]);

Salida:

$ s1 - cadena primitiva? sí, ¿instancia de cadena? No

$ s2 - cadena primitiva? no - instancia de cadena? si

En PHP es posible que stringa sea a stringexcepto cuando en realidad es a string. Como con cualquier lenguaje que use conversión de tipo implícito, el contexto lo es todo.

usuario719662
fuente
-1

Creo que la conversión de texto en php en el bloque interior, String en PHP no es un objeto, como sé:

<?php
function phpwtf($s) {
    $s = (string) $s;
    echo "$s\n";
}
phpwtf("Type hinting is da bomb");
subosito
fuente
2
Puede agregar la validación is_string () dentro de su función para evitar que se pase otro valor a la función.
subosito
1
(string) $spuede arrojar un error si $ses un objeto que no se puede convertir en cadena (no tiene un __toString()método implementado), por lo que no es tan fácil como eso
Yanick Rochon,
Sí Yanick tienes razón. Pero no podemos forzar que toda la entrada sea una cadena ¿verdad? Es por eso que la excepción viene a jugar. Podemos combinar validaciones y capturar excepciones para el resto;)
subosito
Solo digo que se debe evitar convertir alguna variable en una cadena sin verificar primero si la variable se puede convertir, principalmente porque capturar excepciones es costoso y conduce a patrones de diseño / hábitos de codificación incorrectos.
Yanick Rochon