Jak rozwiązać „musi być wystąpieniem ciągu, podanym ciągiem” przed PHP 7?

217

Oto mój kod:

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

Co powoduje ten błąd:

Błąd krytyczny możliwy do uchwycenia: Argument 1 przekazany do phpwtf () musi być instancją ciągu, podano ciąg

To więcej niż trochę orwellowski, gdy PHP rozpoznaje i odrzuca pożądany typ w tym samym oddechu. Jest pięć świateł, do cholery.

Jaki jest odpowiednik podpowiedzi typu dla ciągów w PHP? Premia do odpowiedzi, która dokładnie wyjaśnia, co się tutaj dzieje.

leepowers
źródło
5
To dlatego, że robisz to źle. Twój kod nie powinien na początek działać. Przeczytaj o żonglowaniu typami w dokumentach PHP. PHP jest pisane dynamicznie i słabo. Możesz użyć (ciąg znaków), aby rzucić argument na ciąg znaków (tylko w treści funkcji), ale możesz tylko podpowiadać obiektom i tablicom tak, jak robisz to we fragmencie kodu.
Richard Knop,
7
Problem polega na tym, że popełniłeś mały błąd. Istnieją cztery światła!
CJ Dennis
@Gordon, testowałem na 5.6. Wciąż nie ma szczęścia.
Pacerier,
@Pacerier Śledź wiki.php.net/rfc, aby uzyskać najnowsze informacje.
Gordon,
3
Najwyraźniej podpowiedzi typu skalarnego (zgodnie z intuicyjnym oczekiwaniem, że OP będzie rzeczą powyżej) zostały ostatecznie zatwierdzone zgodnie z RFC dla PHP * 7 * według źródła . Zatwierdzone RFC najwyraźniej zapewnia również cukier składniowy do sprawdzania wartości zwracanych podczas sprawdzania typu, a także parametrów (argumentów). Minęło dużo czasu.
SeldomNeedy

Odpowiedzi:

204

Przed wersją PHP 7 podpowiedzi można używać tylko do wymuszania typów obiektów i tablic. Typy skalarne nie nadają się do typowania. W tym przypadku oczekiwany jest obiekt klasy string, ale dajesz mu (skalar) string. Komunikat o błędzie może być zabawny, ale na początku nie powinien działać. Biorąc pod uwagę dynamiczny system pisania, ma to w pewnym sensie zboczony sens.

Można jedynie ręcznie „wpisać wskazówkę” typów skalarnych:

function foo($string) {
    if (!is_string($string)) {
        trigger_error('No, you fool!');
        return;
    }
    ...
}
deceze
źródło
1
@deceze, czy istnieje składnia podpowiedzi typu odwrotnego? Np. Nic poza tablicami.
Pacerier,
@Pacerier Nie, nie ma.
deceze
4
Ta odpowiedź nie dotyczy PHP 7
Jose Nobile,
24

Z podręcznika PHP :

Wskazówki typu mogą być tylko typu obiektowego i tablicowego (od PHP 5.1). Tradycyjne podpowiedzi typu z int i string nie są obsługiwane.

Więc masz to. Komunikat o błędzie nie jest zbyt pomocny, ale ci to daję.

** Edycja 2017 **

PHP7 wprowadziło więcej deklaracji typu danych funkcji, a wspomniany link został przeniesiony do Argumentów funkcji: deklaracje typów . Z tej strony:

Prawidłowe typy

  • Nazwa klasy / interfejsu : parametr musi być instancją podanej klasy lub nazwy interfejsu. (od PHP 5.0.0)
  • self : parametr musi być instancją tej samej klasy, co ta, w której metoda jest zdefiniowana. Można tego użyć tylko w metodach klas i instancji. (od PHP 5.0.0)
  • tablica : parametr musi być tablicą. (od PHP 5.1.0) wywoływalny Parametr musi być prawidłowym wywoływalnym. PHP 5.4.0
  • bool : parametr musi być wartością logiczną. (od PHP 7.0.0)
  • float : parametr musi być liczbą zmiennoprzecinkową. (od PHP 7.0.0)
  • int : parametr musi być liczbą całkowitą. (od PHP 7.0.0)
  • ciąg : parametr musi być ciągiem. (od PHP 7.0.0)
  • iterable : parametr musi być tablicą lub instancją Traversable. (od PHP 7.1.0)

Ostrzeżenie

Aliasy dla powyższych typów skalarnych nie są obsługiwane. Zamiast tego są traktowane jak nazwy klas lub interfejsów. Na przykład użycie wartości logicznej jako parametru lub typu zwracanego będzie wymagało argumentu lub wartości zwracanej, która jest instancją klasy logicznej lub interfejsu logicznego, a nie typu bool:

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

Powyższy przykład wyświetli:

 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

Ostatnie ostrzeżenie jest tak naprawdę istotne dla zrozumienia błędu „Argument musi być typu ciąg, podany ciąg”; ponieważ głównie jako nazwy argumentów dozwolone są tylko nazwy klas / interfejsów, PHP próbuje zlokalizować „klasę” nazwy klasy, ale nie może jej znaleźć, ponieważ jest to typ prymitywny, dlatego nie udaje się z tym niezręcznym błędem.

Yanick Rochon
źródło
8

PHP pozwala na „podpowiedzi”, gdzie podajesz klasę, aby określić obiekt. Zgodnie z instrukcją PHP „Wskazówki dotyczące typów mogą być tylko typu obiektowego i tablicowego (od PHP 5.1). Tradycyjne podpowiedzi typu z int i łańcuchem nie są obsługiwane”. Błąd jest mylący z powodu wyboru „łańcucha” - wstaw „myClass” na swoim miejscu, a błąd będzie brzmiał inaczej: „Argument 1 przekazany do phpwtf () musi być instancją myClass, podano ciąg znaków”

Surrealistyczne sny
źródło
3

Jak już powiedzieli inni, podpowiedzi typów działają obecnie tylko dla typów obiektów. Ale myślę, że konkretny błąd, który wywołałeś, może polegać na przygotowaniu nadchodzącego typu łańcucha SplString .

Teoretycznie zachowuje się jak ciąg znaków, ale ponieważ jest to obiekt, przejdzie weryfikację typu obiektu. Niestety nie ma go jeszcze w PHP 5.3, może pojawić się w wersji 5.4, więc nie przetestowałem tego.

Mario
źródło
2

W PHP 7.0 deklaracje typu pozwalają skalarne typy, więc te typy są już dostępne: self, array, callable, bool, float, int, string. Pierwsze trzy były dostępne w PHP 5, ale ostatnie cztery są nowe w PHP 7. Jeśli użyjesz czegoś innego (np. integerLub boolean), będzie to interpretowane jako nazwa klasy.

Więcej informacji znajduje się w podręczniku PHP .

TwoStraws
źródło
0

Może nie jest to bezpieczne i ładne, ale jeśli musisz:

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"));
Patrick
źródło
5
Nadal mogłem stworzyćnew string(array(1,2,3))
Jimmy T.
0

Wystąpił ten błąd podczas wywoływania funkcji z kontrolera Laravel do pliku PHP.

Po kilku godzinach znalazłem problem: użyłem $ this z funkcji statycznej.

ecairol
źródło
Using $this when not in object contextjest rzeczywiście tajemniczą wiadomością.
Ben Fransen
0

(pierwotnie opublikowane przez leepowers w swoim pytaniu)

Komunikat o błędzie jest mylący z jednego ważnego powodu:

Nazwy typów pierwotnych nie są zastrzeżone w PHP

Poniżej przedstawiono wszystkie prawidłowe deklaracje klas:

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

Mój błąd polegał na tym, że komunikat o błędzie dotyczył wyłącznie pierwotnego typu łańcucha - słowo „instancja” powinno mnie zatrzymać. Przykład ilustrujący dalej:

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')]);

Wynik:

$ s1 - prymitywny ciąg? tak - instancja ciągu? Nie

$ s2 - prymitywny ciąg? instancja typu no-string? tak

W PHP jest możliwe, stringże jest stringwyjątkiem, chyba że tak naprawdę jest string. Podobnie jak w przypadku każdego języka korzystającego z niejawnej konwersji typu, kontekst jest wszystkim.

użytkownik719662
źródło
-1

Myślę, że rzutowanie typu na php wewnątrz bloku, String na PHP nie jest obiektem, jak wiem:

<?php
function phpwtf($s) {
    $s = (string) $s;
    echo "$s\n";
}
phpwtf("Type hinting is da bomb");
subosito
źródło
2
Możesz dodać sprawdzanie poprawności is_string () w swojej funkcji, aby zapobiec przekazywaniu innej wartości do funkcji.
subosito,
1
(string) $smoże $szgłosić błąd, jeśli jest obiektem, którego nie można rzutować na łańcuch (nie ma __toString()zaimplementowanej metody), więc nie jest to takie proste
Yanick Rochon,
Tak, Yanick masz rację. Ale nie możemy zmusić wszystkich danych wejściowych do ciągów, prawda? dlatego pojawia się wyjątek. Możemy połączyć rodzaj sprawdzania poprawności i wyłapać wyjątek dla reszty;)
subosito
Mówię tylko, że należy unikać rzutowania zmiennej na ciąg bez uprzedniego sprawdzenia, czy zmienną można rzutować, głównie dlatego, że wychwytywanie wyjątków jest kosztowne i prowadzi do złych wzorców projektowych / nawyków kodowania.
Yanick Rochon