Поиск…


Что такое Тип Жонглирование?

PHP - это свободно типизированный язык. Это означает, что по умолчанию он не требует, чтобы операнды в выражении имели одинаковые (или совместимые) типы. Например, вы можете добавить число в строку и ожидать, что оно будет работать.

var_dump ("This is example number " . 1);

Выход будет:

string (24) «Это пример номер 1»

PHP выполняет это путем автоматического литья несовместимых типов переменных в типы, которые позволяют выполнить запрошенную операцию. В приведенном выше случае он будет отличать целочисленный литерал 1 в строку, что означает, что он может быть объединен с предыдущим строковым литералом. Это называется жонглированием типа. Это очень мощная функция PHP, но это также функция, которая может привести вас к большому вытягиванию волос, если вы не знаете об этом и можете даже привести к проблемам безопасности.

Рассмотрим следующее:

if (1 == $variable) {
    // do something
}

Цель состоит в том, что программист проверяет, что переменная имеет значение 1. Но что произойдет, если переменная $ имеет значение «1 с половиной» вместо? Ответ может вас удивить.

$variable = "1 and a half";
var_dump (1 == $variable);

Результат:

BOOL (истина)

Почему это произошло? Это связано с тем, что PHP понял, что строка «1 с половиной» не является целым числом, но это должно быть для того, чтобы сравнить ее с целым числом 1. Вместо того, чтобы терпеть неудачу, PHP инициирует жонглирование типа и пытается преобразовать переменную в целое число. Он делает это, беря все символы в начале строки, которую можно отличить до целого и отбросить. Он останавливается, как только он сталкивается с символом, который нельзя рассматривать как число. Поэтому «1 с половиной» передается целому числу 1.

Конечно, это очень надуманный пример, но он служит для демонстрации проблемы. Следующие несколько примеров будут охватывать некоторые случаи, когда я столкнулся с ошибками, вызванными манипуляцией типа, которая произошла в реальном программном обеспечении.

Чтение из файла

При чтении из файла мы хотим узнать, когда мы достигли конца этого файла. Зная, что fgets() возвращает false в конце файла, мы можем использовать это как условие для цикла. Однако, если данные, возвращаемые из последнего чтения, являются тем, что оценивается как логическое значение false , это может привести к преждевременному завершению цикла чтения файла.

$handle = fopen ("/path/to/my/file", "r");

if ($handle === false) {
    throw new Exception ("Failed to open file for reading");
}

while ($data = fgets($handle)) {
    echo ("Current file line is $data\n");
}

fclose ($handle);

Если файл читается содержит пустую строку, то в while цикл будет завершен в этой точке, так как пустая строка вычисляется как логическое значение false .

Вместо этого мы можем явно проверить логическое false значение, используя строгие операторы равенства :

while (($data = fgets($handle)) !== false) {
    echo ("Current file line is $data\n");
}

Обратите внимание, что это надуманный пример; в реальной жизни мы использовали бы следующий цикл:

while (!feof($handle)) {
    $data = fgets($handle);
    echo ("Current file line is $data\n");
}

Или замените все это на:

$filedata = file("/path/to/my/file");
foreach ($filedata as $data) {
    echo ("Current file line is $data\n");
}

Переключить сюрпризы

Операторы switch используют нестрогое сравнение для определения совпадений. Это может привести к некоторым неприятным неожиданностям . Например, рассмотрим следующее утверждение:

switch ($name) {
    case 'input 1':
        $mode = 'output_1';
        break;
    case 'input 2':
        $mode = 'output_2';
        break;
    default:
        $mode = 'unknown';
        break;
}

Это очень простой оператор и работает как ожидалось, когда $name является строкой, но может вызвать проблемы в противном случае. Например, если $name является целым числом 0 , тогда во время сравнения будет выполняться манипуляция типа. Тем не менее, это буквальное значение в выражении case, которое вызывает жонглирование, а не условие в инструкции switch. Строка "input 1" преобразуется в целое число 0 которое соответствует входному значению целого числа 0 . Результатом этого является то, что если вы задаете значение целого числа 0 , первый случай всегда выполняется.

Есть несколько решений этой проблемы:

Явное литье

Значение может быть типаж в строку перед сравнением:

switch ((string)$name) {
...
}

Или также можно использовать функцию, известную для возврата строки:

switch (strval($name)) {
...
}

Оба этих метода гарантируют, что значение имеет тот же тип, что и значение в операторах case .

Избегайте switch

Использование оператора if даст нам контроль над тем, как выполняется сравнение, что позволяет нам использовать строгие операторы сравнения :

if ($name === "input 1") {
    $mode = "output_1";
} elseif ($name === "input 2") {
    $mode = "output_2";
} else {
    $mode = "unknown";
}

Строгая типизация

Начиная с PHP 7.0, некоторые из вредоносных эффектов жонглирования типа могут быть смягчены при строгой типизации . Включив этот оператор declare в качестве первой строки файла, PHP будет принудительно вводить объявления типов параметров и декларации типа возвращаемого типа, TypeError исключение TypeError .

declare(strict_types=1);

Например, этот код, используя определения типа параметра, будет бросать захватывающее исключение типа TypeError при запуске:

<?php
declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}

echo sum("1", 2);

Аналогично, этот код использует декларацию типа возврата; он также генерирует исключение, если он пытается вернуть что-либо, кроме целого:

<?php
declare(strict_types=1);

function returner($a): int {
    return $a;
}

returner("this is a string");


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow