Рекурсия в подготовленных высказываниях - PullRequest
8 голосов
/ 26 апреля 2010

Я использую PDO и готовлю все свои заявления в первую очередь по соображениям безопасности. Однако у меня есть часть моего кода, которая действительно выполняет одно и то же утверждение много раз с разными параметрами, и я подумал, что именно в этом случае подготовленные операторы действительно сияют. Но они на самом деле нарушают код ...

Основная логика кода такова.

function someFunction($something) {
  global $pdo;

  $array = array();

  static $handle = null;
  if (!$handle) {
    $handle = $pdo->prepare("A STATEMENT WITH :a_param");
  }

  $handle->bindValue(":a_param", $something);
  if ($handle->execute()) {
    while ($row = $handle->fetch()) {
      $array[] = someFunction($row['blah']);
    }
  }

  return $array;
}

Это выглядело хорошо для меня, но было пропущено много строк. В конце концов я понял, что дескриптор оператора был изменен (выполнен с другим параметром), что означает, что вызов fetch в цикле while будет работать только один раз, затем функция снова вызывает себя и набор результатов изменяется.

Поэтому мне интересно, как лучше всего использовать подготовленные операторы PDO рекурсивным способом.

Одним из способов может быть использование fetchAll (), но в руководстве говорится, что это требует значительных накладных расходов. Весь смысл в том, чтобы сделать его более эффективным.

Еще одна вещь, которую я мог бы сделать, это не использовать статический дескриптор, а вместо этого создавать новый каждый раз. Я полагаю, что, поскольку строка запроса одинакова, драйвер MySQL внутренне будет использовать подготовленный оператор в любом случае, поэтому есть небольшая нагрузка на создание нового дескриптора при каждом рекурсивном вызове. Лично я думаю, что это победит.

Или есть какой-нибудь способ переписать это?

Ответы [ 3 ]

3 голосов
/ 26 апреля 2010

Вы не можете вложить дескрипторы операторов: вам нужно закрыть ранее открытый дескриптор, прежде чем открывать другой в течение одного сеанса.

На самом деле, PDO делает автоматически, когда вы запускаете новую подготовку.

Когда вы вызываете функцию рекурсивно:

  • Первоначальный дескриптор выделен (1)
  • Первая запись извлечена из (1)
  • Функция вызывается рекурсивно. Значение (1) находится в стеке рекурсии.
  • Новый дескриптор выделен (2), аннулирует (1)
  • Первая запись извлечена из (2)
  • Функция возвращает
  • Вы пытаетесь получить следующую запись (1) и терпите неудачу, поскольку она неверна

Таким образом, MySQL не поддерживает рекурсию на его стороне, и это означает, что вам придется делать это на PHP стороне, используя fetchAll.

0 голосов
/ 07 декабря 2014

Если вы используете те же переменные (из-за pdo bindValue), каждое значение времени совпадает с первым. Так что это не удастся:

foreach ($bind_params as $key => $value) {
    $stmt->bindParam(":$key", $value);
}

результат:

$key[0] = $value[0];
$key[1] = $value[0];
$key[2] = $value[0];
$key[3] = $value[0];

Итак, вы хотите сделать некрасивый трюк, тогда:

        $i = 0;
        foreach ($bind_params as $key => $value) {
            $i++;
            $$i = $value;
            $stmt->bindParam(":$key", $$i);
        }

результат:

$key[0] = $value[0];
$key[1] = $value[1];
$key[2] = $value[2];
$key[3] = $value[3];
0 голосов
/ 26 апреля 2010

Настоящая проблема в том, что $handle является статическим. Статические переменные являются проблематичными для рекурсии, когда необходимо сохранять состояние при рекурсивном вызове, а не только для подготовленных операторов. В этом случае рекурсивный вызов выполняет новый запрос, отбрасывая предыдущее состояние. PDO::fetchAll действительно единственный вариант, если вы хотите один подготовленный запрос.

В зависимости от того, что является оператором, вы можете переписать его, чтобы он возвращал все результаты сразу, после чего создавая дерево.

...