Отчет PDO, подготовленный - какие двоеточия используются в именах параметров?

Я видел много статей, использующих двоеточия (:) перед именованными параметрами при использовании PDO, и пару, которая не использует двоеточие. Я бы так же быстро не использовал двоеточие, просто потому, что он меньше нажатий клавиш и немного легче читать.

Кажется, он работает отлично для меня, но мне любопытно, есть ли что-то важное, что мне не хватает, когда дело доходит до использования двоеточий?

Например, это работает отлично:

function insertRecord ($conn, $column1, $comumn2) {
    try {
        $insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
        VALUES(:column1, :column2)');
        $insertRecord->execute(array(
                'column1' => $column1,
                'column2' => $column2
            ));
    }
    catch(PDOException $e) {
        echo $e->getMessage();
    }
}

В отличие от большинства разработчиков, которые также используют это:

function insertRecord ($conn, $column1, $comumn2) {
    try {
        $insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
        VALUES(:column1, :column2)');
        $insertRecord->execute(array(
                ':column1' => $column1,
                ':column2' => $column2
            ));
    }
    catch(PDOException $e) {
        echo $e->getMessage();
    }
}

Обратите внимание на двоеточия в параметрах оператора execute.

Я хотел бы понять, для чего нужны двоеточия.

Ответ 1

В инструкции SQL требуются колонии, чтобы указать, какие идентификаторы являются заполнителями.

Колонки вызовов execute() или bindParam() являются необязательными. Документация указывает их, но реализация достаточно умна, чтобы понять, что вы имеете в виду, если вы их оставите (что еще вы могли иметь в виду?).

Ответ 2

TL; DR Нет, вам ничего не хватает. Вы должны использовать двоеточия (:) с именованными заполнителями в строке SQL, но они не требуются при выполнении инструкции или параметров привязки. PHP выведет : если вы оставите это в этом контексте (см. второй раздел ниже для объяснения и доказательства из исходного кода для самого интерпретатора PHP).

Что работает (что вы можете делать в PHP)

Другими словами, это приемлемо:

$insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
    VALUES(:column1, :column2)');
//         ^         ^  note the colons

но это не так, потому что имена заполнителей неоднозначны и похожи на имена столбцов (или других):

$insertRecord = $conn->prepare('INSERT INTO Table1 (column1, column2)
    VALUES(column1, column2)');
//         ^        ^  no colons

В отличие от этого, двоеточия являются необязательными при использовании PDOStatement::bindParam() или PDOStatement::execute(). Обе эти работы в основном идентичны: *

$insertRecord->execute(array(
    ':column1' => $column1,
    ':column2' => $column2
));
// or
$insertRecord->execute(array(
    'column1' => $column1,
    'column2' => $column2
));

Почему это работает (Исходный исходный код PHP)

Почему это работает? Ну, для этого нам нужно войти в c - языковой исходный код для самого PHP. Чтобы сохранить актуальность, я использовал последний источник из github (PHP 7), но тот же базовый анализ относится к более ранним версиям.

Язык PHP ожидает, что именованные заполнители имеют двоеточие в SQL, как указано в документах. И документация для PDOStatement::bindParam() указывает, что параметр должен иметь форму :name, когда вы привязываете параметр к заполнителю. Но это не совсем так, по следующим причинам.

Там нет риска двусмысленности, когда приходит время связывать параметры или выполнять оператор, потому что SQL-заполнитель должен иметь один и только один двоеточие. Это означает, что интерпретатор PHP может сделать критическое предположение и сделать это безопасно. Если вы посмотрите pdo_sql_parser.c в исходном коде PHP, особенно в строке 90, вы можете увидеть действительный список символов в заполнителе, а именно буквенно-цифровые символы (цифры и буквы), подчеркивания и двоеточие. Следуя логике кода в этом файле, немного сложно и сложно объяснить здесь. Мне грустно говорить, что это связано с множеством операторов goto — но короткая версия состоит в том, что только первый символ может быть двоеточием.

Проще говоря, :name является допустимым заполнителем в SQL, но name и ::name не являются.

Это означает, что парсер может с уверенностью предположить к моменту получения bindParam() или execute(), что параметр с именем name должен быть действительно :name. То есть, он может просто добавить : до остальной части имени параметра. Фактически, это именно то, что он делает, в pdo_stmt.c, начиная со строки 362:

if (param->name) {
    if (is_param && param->name[0] != ':') {
        char *temp = emalloc(++param->namelen + 1);
        temp[0] = ':';
        memmove(temp+1, param->name, param->namelen);
        param->name = temp;
    } else {
        param->name = estrndup(param->name, param->namelen);
    }
}

Что это значит, в слегка упрощенном псевдокоде:

if the parameter has a name then
    if the parameter name does not start with ':' then
        allocate a new string, 1 character larger than the current name
        add ':' at the start of that string
        copy over the rest of the name to the new string
        replace the old string with the new string
    else
        call estrndup, which basically just copies the string as-is (see https://github.com/php/php-src/blob/1c295d4a9ac78fcc2f77d6695987598bb7abcb83/Zend/zend_alloc.h#L173)

Итак, name (в контексте bindParam() или execute()) становится :name, что соответствует нашему SQL, и PDO совершенно счастлив.

Лучшие практики

Технически, в любом случае работает, поэтому вы можете сказать, что это проблема предпочтения. Но в случае, если это не очевидно, это недостаточно хорошо документировано. Мне пришлось очень глубоко погрузиться в исходный код, чтобы понять это, и это теоретически может измениться в любое время. Для обеспечения согласованности, удобочитаемости и упрощения поиска в вашей среде IDE используйте двоеточие.


* Я говорю, что они работают "в основном" одинаково, потому что приведенный выше код c накладывает крайне незначительный штраф за уход из двоеточия. Он должен выделять больше памяти, строить новую строку и заменять старую строку. Тем не менее, это наказание в наносекундном диапазоне за такое имя, как :name. Это может стать измеримым, если вы склонны давать свои параметры очень длинным (например, 64 Кб) именам, и у вас их много, и в этом случае у вас есть другие проблемы... Наверное, ничто из этого не имеет значения, во всяком случае, поскольку двоеточие добавляет крайне небольшое наказание во времени, чтобы читать и разбирать файл, поэтому эти два сверхмалых штрафа могут даже компенсироваться. Если вы беспокоитесь о производительности на этом уровне, у вас гораздо более холодные проблемы, чтобы вы не спали по ночам, чем остальные. Кроме того, в этот момент вы, вероятно, должны создать свой webapp в чистом ассемблере. </sarcasm>

Ответ 3

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

Ответ 4

Это личное предпочтение, некоторые полагают, что это однозначно, но я не вижу в этом ничего двусмысленного. Это параметр.

Как некоторые люди любят делать нумерованные параметры (используя?) вместо названных параметров.

Ответ 5

Да, это абсурдно-кроваво-безопасно, но есть и возможность быть небезопасной. Вы можете спросить, как такой контраст существует одновременно? Ну, в программировании мира ИМХО нет окончательности.

Безопасность:

Так как PHP 5.1 PDO поставляется с PHP как встроенная функция, с этого времени добавление двоеточия к не имеющемуся двоеточию с именем параметра отсутствует. Сказав, что через 10 лет это не будет проблемой для сообщества PHP, чтобы отказаться от него. Почему действительно?

Опасное:

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

Заполнитель в основном различим со специальным символом (символами)/форматом, например, как вы набираете заполнители printf %d %s, а не d s. Вам нужно только правильно следовать формату заполнителя и не пытаться удалить его на PHP-круге.

Когда он не задокументирован, у него есть - даже эпсилон - шанс быть пропущенным.

Ответ 6

В официальной документации показан только синтаксис с двоеточиями:

$insertRecord->execute(array(
    ':column1' => $column1,
    ':column2' => $column2
));

Кроме того, внутренне (исходный код PDO), если отсутствует ведущий двоеточие, он будет добавлен автоматически.
Поэтому вы должны использовать синтаксис WITH colons.