В настоящее время я читаю спецификацию PHP здесь: https://github.com/php/php-langspec
Теперь я видел список-характеристическая Спецификация Вот, в котором говорится, что для список-характеристическая построить как показано ниже, правая часть простое присваивание выражение должно быть выражением, которое обозначает массив:
список (список-выражение-списоквыбирать ) знак равно выражение
Но документация список с php.net Вот, дает пример, содержащий это:
$result = $pdo->query("SELECT id, name, salary FROM employees");
while (list($id, $name, $salary) = $result->fetch(PDO::FETCH_NUM)) {
//output $id, $name and $salary
}
Дело в том, что PDOStatement::fetch(PDO::FETCH_NUM)
возвращается FALSE
если больше нет строки Но правая часть Назначение выражение должен быть массивом — и FALSE
это не массив. Так что это приведет к фатальной ошибке, верно?
Я что-то пропустил в спецификации, или это действительно несоответствие?
Документация говорит следующее, где «list-intrinsic» является грамматикой, которая содержит все допустимые формы list(...)
можно иметь.
list-intrinsic должен использоваться в качестве левого операнда в выражении простого присваивания, правым операндом которого должно быть выражение, обозначающее массив (называемый исходным массивом).
Что обозначает массив? Документация говорит это:
Массив — это структура данных, которая содержит набор из нуля или более элементов. Элементы массива не обязательно должны иметь один и тот же тип, и тип элемента массива может меняться в течение срока его службы.
Я думаю, что вы правы, думая, что FALSE
, логическое, не квалифицируется как все, что обозначает массив как это не коллекция.
Что значит «должен» в этом контексте? Если мы прочитаем Соответствие Часть документации мы находим:
В этой спецификации «должен» должен интерпретироваться как требование к реализации или программе; и наоборот, «не должен» должен интерпретироваться как запрет.
Если требование «должен» или «не должен», которое появляется вне ограничения, нарушается, поведение не определено. Неопределенное поведение иначе указано в данной спецификации словами «неопределенное поведение» или пропуском любого явного определения поведения. Нет разницы в акценте между этими тремя; все они описывают «поведение, которое не определено».
Правильно ли вы полагаете, что должна возникать фатальная ошибка? Я думаю, что вы не правы, считая так. Если в разделе «семантика» не указано, что произойдет фатальная ошибка, отсутствие спецификации поведения или необходимость в ограничениях означает, что поведение этой части языка не определено. Это может работать. Может выдать ошибку, фатальную ошибку. Он может создать ИИ, который уничтожит всех нас, сделает Луну пурпурной или взорвет сервер. Это не определено.
Так что же происходит? Документация гласит под семантикой следующее:
Это присваивает целевым переменным ноль или более элементов исходного массива. В случае успеха он возвращает копию исходного массива. Если исходный массив на самом деле является значением NULL, это считается ошибкой, а возвращаемое значение из списка не определено.
Все элементы в исходном массиве, имеющие ключи типа string, игнорируются. Элемент, имеющий ключ int, равный 0, присваивается первой целевой переменной, элемент, имеющий ключ int, равный 1, присваивается второй целевой переменной и так далее, пока все целевые переменные не будут назначены. Любые другие элементы массива игнорируются. Если количество элементов исходного массива с ключами int меньше, чем целевых переменных, для неназначенных целевых переменных задается значение NULL и возникает нефатальная ошибка.
Тестирование дает следующие результаты:
$a = 1;
$z = FALSE;
$e = (list( $a, $b ) = $z);
var_dump($a); //NULL
var_dump($b); //NULL
var_dump($z); //FALSE
var_dump($e); //FALSE
По факту, $z = $e
для любого $z
кажется, даже если $z = NULL
, Никакое уведомление, предупреждение или ошибка не генерируются для любого значения, которое я тестировал, если только длина исходного массива не меньше количества переменных в выражении list-intrensic. В этом случае Notice: Undefined offset
Показано.
Кажется, что любое не повторяемое выражение обрабатывается так, как если бы оно было значением NULL (но это неопределенное поведение); в моей версии PHP кажется, что любое значение NULL обрезает назначение на полпути; он не будет повторяться, но выполняется предварительная часть присвоения NULL всем переменным.
Выражение while (list($id, $name, $salary) = $result->fetch(PDO::FETCH_NUM))
таким образом назначит NULL
в $id
, $name
а также $salary
и FALSE
значение завершит цикл while. Однако такое поведение не ожидается и не гарантируется спецификацией языка.
Это сделано намеренно в реализации php, чтобы разрешить этот сомнительный кусок кода:
while (list($key, $value) = each($array)) {
// ...
}
Результат each()
может быть false
в противном случае это вызвало бы неприятную ошибку, поэтому, хотя это поведение, по-видимому, противоречит спецификации, в основном это делается для обеспечения обратной совместимости.
Возможно, хотя и маловероятно, что следующая версия PHP отменит это поведение, но на этом этапе я бы предложил, чтобы спецификация могла быть отредактирована, чтобы отразить этот конкретный артефакт, хотя подразумеваемое неопределенное поведение также могло бы служить этой цели 🙂
Код для этого можно найти Вот; В настоящее время выражение в правой части поддерживает:
ArrayAccess
,В случае «чего-то еще» его просто назначат null
для всех переменных списка.
Никита Попов предложил следующее обновление спецификации как часть тянуть запрос:
list-intrinsic должен использоваться в качестве левого операнда в выражении простого присваивания, правым операндом которого должно быть выражение, обозначающее массив или объект, реализующий
ArrayAccess
интерфейс (называется исходным массивом).
…
Этот встроенный присваивает один или несколько элементов исходного массива целевым переменным. В случае успеха он возвращает копию исходного массива. Если исходный массив не является массивом или объект реализации
ArrayAccess
назначения не выполняются и возвращаемое значение равно NULL.
(Изменения подчеркнуты)