Як вирішити "повинен бути екземпляр рядка, заданий рядок" перед PHP 7?


217

Ось мій код:

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

Що призводить до цієї помилки:

Фатальна помилка, що піддається запису: Аргумент 1, переданий phpwtf (), повинен бути екземпляром рядка, заданого рядка

Більше, ніж трохи орувельського, бачити, як PHP розпізнає та відхиляє потрібний тип на одному диханні. Є п'ять вогнів, чорт забирай.

Який еквівалент натяку на тип рядків у PHP? Бонусний розгляд відповіді, яка пояснює, що саме тут відбувається.


5
Ну, це тому, що ти робиш це неправильно. Для початку ваш код не повинен працювати. Читайте про тип жонглювання в документах PHP. PHP динамічний і слабкий. Ви можете використовувати (рядок) для наведення аргументу на рядок (хоча в тілі функції), але ви можете лише натякати на об'єкти та масиви, як у вашому фрагменті коду.
Річард Ноп

7
Проблема в тому, що ви зробили невелику помилку. Є чотири вогні!
CJ Dennis

@Gordon, я тестував 5,6. Ще не везе.
Pacerier

@Pacerier Будь ласка, слідкуйте за wiki.php.net/rfc для останніх подій.
Гордон

3
Мабуть, скалярне натяк на тип (як інтуїтивно очікувалося, що це буде справа вище), нарешті, було затверджено в рамках RFC для PHP * 7 * за даними джерела . Затверджений RFC, очевидно, також забезпечує синтаксичний цукор для перевірки типу повернених значень , а також параметрів (аргументів). Минуло давно.
SeldomNeedy

Відповіді:


204

До PHP 7 натяк типу можна використовувати лише для примусового використання типів об'єктів та масивів. Скалярні типи не піддаються наказуванню на тип. У цьому випадку stringочікується об’єкт класу , але ви надаєте йому (скалярний) string. Повідомлення про помилку може бути смішним, але для початку це не повинно працювати. Враховуючи систему динамічного набору тексту, це насправді має певний перекручений сенс.

Скалярні типи можна "ввести підказку" лише вручну :

function foo($string) {
    if (!is_string($string)) {
        trigger_error('No, you fool!');
        return;
    }
    ...
}

1
@deceze, чи є синтаксис для натяку на зворотний тип? Наприклад, нічого, крім масивів.
Pacerier

@Pacerier Ні, немає.
деге

4
Ця відповідь не дійсна для PHP 7
Хосе Нобіле

24

З посібника PHP :

Підказки типу можуть мати лише тип об'єкта та масиву (починаючи з PHP 5.1). Підказки традиційного типу за допомогою int та string не підтримуються.

Так у вас є. Повідомлення про помилку не дуже корисне, але я вам це дарую.

** 2017 Редагувати **

PHP7 представив більше оголошень про типи даних функцій, і вищезазначене посилання було переміщено до аргументів функції: Декларації типу . З цієї сторінки:

Дійсні типи

  • Назва класу / інтерфейсу : Параметр повинен бути екземпляром даного імені класу або інтерфейсу. (оскільки PHP 5.0.0)
  • self : Параметр повинен бути екземпляром того ж класу, що і той, для якого визначено метод. Це може бути використано лише для методів класу та примірника. (оскільки PHP 5.0.0)
  • масив : Параметр повинен бути масивом. (оскільки PHP 5.1.0) набирається параметр Параметр повинен бути дійсним для виклику. PHP 5.4.0
  • bool : Параметр повинен бути булевим значенням. (оскільки PHP 7.0.0)
  • float : Параметр повинен бути числом з плаваючою комою. (оскільки PHP 7.0.0)
  • int : Параметр повинен бути цілим числом. (оскільки PHP 7.0.0)
  • string : Параметр повинен бути рядком. (оскільки PHP 7.0.0)
  • ітерабельний : Параметр повинен бути або масивом, або екземпляром пройденого. (оскільки PHP 7.1.0)

Увага

Псевдоніми для вищезазначених скалярних типів не підтримуються. Натомість вони трактуються як імена класів або інтерфейсів. Наприклад, для використання булевого типу в якості параметра або типу повернення знадобиться аргумент або повернене значення, яке є екземпляром булевого класу або інтерфейсу, а не типу bool:

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

Наведений вище приклад виведе:

 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

Останнє попередження насправді є важливим для розуміння помилки "Аргумент повинен вводити рядок, наданий рядок"; Оскільки в якості аргументу дозволено в основному лише імена класів / інтерфейсів, PHP намагається знайти ім’я класу "string", але не може знайти жодного, оскільки це примітивний тип, тому не вдасться з цією незграбною помилкою.


8

PHP дозволяє "натякати", де ви подаєте клас, щоб вказати об'єкт. Згідно з посібником з PHP, "Підказки типу можуть бути лише типом об'єкта та масиву (оскільки PHP 5.1). Натяки на традиційні типи за допомогою int та string не підтримуються." Помилка заплутана через те, що ви вибрали "string" - поставте "myClass" на його місце, і помилка буде читати інакше: "Аргумент 1, переданий phpwtf (), повинен бути екземпляром myClass, рядок заданий"


3

Як уже говорили інші, натяк на тип наразі працює лише для типів об'єктів. Але я думаю, що певна помилка, яку ви викликали, може бути в підготовці майбутнього рядка SplString .

Теоретично він поводиться як рядок, але оскільки це об'єкт, він би пройшов перевірку типу об'єкта. На жаль, його ще немає в PHP 5.3, можливо, в 5.4, тому не перевіряли цього.


2

У РНР 7.0 оголошення типу дозволяють скалярні типи, так що ці типи тепер доступні: self, array, callable, bool, float, int, string. Перші три були доступні в PHP 5, але останні чотири є новими в PHP 7. Якщо ви використовуєте що-небудь інше (наприклад, integerабо boolean), що буде інтерпретуватися як назва класу.

Додаткову інформацію див. У посібнику PHP .


0

Можливо, не безпечно і красиво, але якщо потрібно:

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"));

5
Я все ще міг створитиnew string(array(1,2,3))
Джиммі Т.

0

Я отримав цю помилку, коли викликав функцію з контролера Laravel у файл PHP.

Через пару годин я виявив проблему: я використовував $ this із статичної функції.


Using $this when not in object contextнасправді криптовалюта.
Бен Франсен

0

(спочатку опублікував leepowers у своєму запитанні)

Повідомлення про помилку є заплутаним з однієї великої причини:

Імена примітивних типів не зберігаються в PHP

Нижче наведено всі дійсні декларації класу:

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

Моя помилка полягала в тому, що повідомлення про помилку стосувалося виключно строкового примітивного типу - слово "екземпляр" повинно було зробити мені паузу. Приклад для подальшої ілюстрації:

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

Вихід:

$ s1 - примітивний рядок? так - рядковий екземпляр? немає

$ s2 - примітивний рядок? ні - рядковий екземпляр? так

У PHP можливо, щоб a stringбув, за stringвинятком випадків, коли це насправді a string. Як і для будь-якої мови, яка використовує неявне перетворення типів, контекст - це все.


-1

Я думаю, що набір тексту на php на внутрішньому блоці, String на PHP не є об'єктом, як я знаю:

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

2
Ви можете додати перевірку is_string () всередині функції, щоб запобігти переданню іншого значення функції.
subosito

1
(string) $sможе призвести до помилки, якщо $sце об’єкт, який не можна ввести в рядок (не застосовується __toString()метод), тож це не так просто
Yanick Rochon

Так, Янік, ти маєш рацію. Але ми не можемо змусити весь вхід бути рядковим правильно? тому виняток приходить до гри. Ми можемо комбінувати різновиди валідацій та
вилучити

Я просто кажу, що слід уникати введення певної змінної в рядок, не перевіряючи спочатку, чи може бути змінена зміна, головним чином, тому що вилучення винятків коштує дорого і призводить до поганих моделей дизайну / кодування звичок.
Янік Рошон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.