Как решить «должен быть экземпляр строки, заданной строки» до PHP 7?


217

Вот мой код:

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

Что приводит к этой ошибке:

Исправляемая фатальная ошибка: аргумент 1, передаваемый в phpwtf (), должен быть экземпляром string, string

Больше, чем маленький Оруэллиан, видит, как PHP распознает и отклоняет желаемый тип на одном дыхании. Есть пять огней, черт побери.

Что эквивалентно типу хинтинга для строк в PHP? Бонус за рассмотрение ответа, который точно объясняет, что здесь происходит.


5
Ну, это потому, что вы делаете это неправильно. Ваш код не должен работать, для начала. Читайте о жонглировании типов в PHP документах. PHP динамически типизирован и слабо типизирован. Вы можете использовать (string) для приведения аргумента к строке (но только в теле функции), но вы можете только подсказывать объекты и массивы, как вы делаете в своем фрагменте кода.
Ричард Кноп

7
Проблема в том, что вы допустили небольшую ошибку. Есть четыре огня!
CJ Деннис

@ Гордон, я тестировал на 5.6. Все еще не повезло.
Pacerier

@Pacerier Пожалуйста, следуйте wiki.php.net/rfc для последних событий.
Гордон

3
По-видимому, скалярная подсказка типов (как интуитивно ожидается, что OP будет выше) была наконец одобрена в соответствии с RFC для PHP * 7 * в соответствии с источником . Утвержденный RFC, по-видимому, также предоставляет синтаксический сахар для проверки возвращаемых значений, а также параметров (аргументов). Это было долгое время в будущем.
SeldomNeedy

Ответы:


204

До PHP 7 хинтинг типов можно использовать только для принудительного использования типов объектов и массивов. Скалярные типы не являются Hin-Hintable. В этом случае stringожидается объект класса , но вы даете ему (скаляр) string. Сообщение об ошибке может быть забавным, но оно не должно работать с самого начала. Учитывая динамическую систему типирования, это действительно имеет какой-то извращенный смысл.

Вы можете только вручную «ввести подсказку» скалярных типов:

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

1
@deceze, есть ли синтаксис для хинтинга обратного типа? Например, что угодно, кроме массивов.
Pacerier

@Pacerier Нет, нет.
deceze

4
Этот ответ не подходит для PHP 7
Jose Nobile

24

Из руководства PHP :

Тип Подсказки могут быть только типа объекта и массива (начиная с PHP 5.1). Подсказка традиционного типа с использованием int и string не поддерживается.

Итак, у вас есть это. Сообщение об ошибке не очень полезно, но я дам вам это.

** 2017 Редактировать **

В PHP7 появилось больше объявлений типов данных функций, и вышеупомянутая ссылка была перемещена в Аргументы функций: Объявления типов . С этой страницы:

Допустимые типы

  • Имя класса / интерфейса : параметр должен быть экземпляром данного класса или имени интерфейса. (начиная с PHP 5.0.0)
  • self : параметр должен быть экземпляром того же класса, что и класс, для которого определен метод. Это можно использовать только для методов класса и экземпляра. (начиная с PHP 5.0.0)
  • массив : параметр должен быть массивом. (начиная с PHP 5.1.0) callable Параметр должен быть допустимым вызываемым. PHP 5.4.0
  • bool : параметр должен быть логическим значением. (начиная с PHP 7.0.0)
  • float : параметр должен быть числом с плавающей запятой. (начиная с PHP 7.0.0)
  • int : Параметр должен быть целым числом. (начиная с PHP 7.0.0)
  • строка : параметр должен быть строкой. (начиная с PHP 7.0.0)
  • iterable : параметр должен быть либо массивом, либо экземпляром Traversable. (начиная с 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

Последнее предупреждение действительно важно для понимания ошибки "Аргумент должен иметь тип string, string string"; поскольку в качестве типа аргумента в основном разрешены только имена классов / интерфейсов, 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 возможно stringбыть a, stringза исключением случаев, когда это на самом деле 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

Да яник ты прав. Но мы не можем заставить весь ввод быть строкой, верно? вот почему исключение приходит в игру. Мы можем комбинировать проверки и ловить исключения для остальных;)
subosito

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