Почему я получаю сообщение об ошибке «Не могу передать параметр 2 по ссылке», когда использую bindParam с постоянным значением?

Я использую этот код, и я вне разочарования:

try {
    $dbh = new PDO('mysql:dbname=' . DB . ';host=' . HOST, USER, PASS);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $dbh->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
catch(PDOException $e)
{
    ...
}
$stmt = $dbh->prepare('INSERT INTO table(v1, v2, ...) VALUES(:v1, :v2, ...)');
$stmt->bindParam(':v1', PDO::PARAM_NULL); // --> Here's the problem

PDO::PARAM_NULL, null, '', все они терпят неудачу и выдают эту ошибку:

Неустранимая ошибка: невозможно передать параметр 2 по ссылке в /opt/...


person Nacho    schedule 08.09.2009    source источник


Ответы (9)


Вам нужно использовать bindValue, а не bindParam

bindParam берет переменную по ссылке и не извлекает значение во время вызова bindParam. Я нашел это в комментарии к документам PHP:

bindValue(':param', null, PDO::PARAM_INT);

P.S. У вас может возникнуть соблазн сделать это bindValue(':param', null, PDO::PARAM_NULL);, но это сработало не у всех (спасибо Уиллу Шейверу за сообщение).

person JasonWoof    schedule 08.09.2009
comment
Я не уверен, в чем разница между этими двумя, но я исследую некоторые из них. Спасибо, ваш ответ тоже был великолепен. - person Nacho; 08.09.2009
comment
Я думаю, что это может быть лучший ответ, чем мой (если он действительно работает) - person Joe Phillips; 04.03.2010
comment
мои тесты на PHP 5.3.8 + Mysql показывают, что нет никакой разницы в сгенерированном запросе между ними. Важная часть, по-видимому, заключается в том, что переданное значение равно NULL, а не '' или '0' - person Odin; 06.11.2011
comment
У меня были проблемы с PDO::PARAM_NULL в MySql 5.1.53, но PDO::PARAM_INT с нулевым значением отлично работал. - person Will Shaver; 05.12.2011
comment
Odin: Я думаю, что самая важная часть — передать все три параметра :). в вопросе ign он передавал PDO::PARAM_NULL как значение, а не как тип (и вообще не передавал тип). - person JasonWoof; 17.12.2011
comment
К вашему сведению: в большинстве случаев вы вполне можете использовать bindValue() вместо bindParam(); для всего, а не только для значения NULL. Я не могу вспомнить ни одного случая, когда вам нужно было бы привязать параметр к ссылке на переменную PHP, но это то, что делает bindParam(). - person low_rents; 03.12.2015

При использовании bindParam() вы должны передавать переменную, а не константу. Итак, перед этой строкой вам нужно создать переменную и установить для нее значение null.

$myNull = null;
$stmt->bindParam(':v1', $myNull, PDO::PARAM_NULL);

Вы получите такое же сообщение об ошибке, если попытаетесь:

$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);
person Joe Phillips    schedule 08.09.2009
comment
Вы правильно поняли, я только что понял, что делал это раньше в своем коде, $null = PDO::PARAM_NULL; благодаря. - person Nacho; 08.09.2009
comment
В этом случае лучше использовать bindValue(), если вы все равно собираетесь создавать заполнители. bindParam() изначально предназначен для выполнения запроса, а затем изменения переменных и повторного выполнения без повторной привязки параметров. bindValue() привязывается немедленно, bindParam() только при выполнении. - person Hugo Zink; 07.10.2015
comment
Я обнаружил, что значение null должно быть строчным в строке $myNull = null. $myNull = NULL НЕ работает. - person raphael75; 28.11.2017

При использовании столбцов INTEGER (которые могут быть NULL) в MySQL PDO имеет некоторое (для меня) неожиданное поведение.

Если вы используете $stmt->execute(Array), вы должны указать литерал NULL и не можете указать NULL по ссылке на переменную. Так что это не сработает:

// $val is sometimes null, but sometimes an integer
$stmt->execute(array(
    ':param' => $val
));
// will cause the error 'incorrect integer value' when $val == null

Но это будет работать:

// $val again is sometimes null, but sometimes an integer
$stmt->execute(array(
    ':param' => isset($val) ? $val : null
));
// no errors, inserts NULL when $val == null, inserts the integer otherwise

Пробовал это на MySQL 5.5.15 с PHP 5.4.1

person ChrisF    schedule 23.12.2012
comment
$stmt-›execute(array( ':param' =› !empty($val) ? $val : null )); Используйте !empty, потому что для пустой строки вы также хотели бы установить значение null в базе данных. - person Shehzad Nizamani; 30.06.2017
comment
@ShehzadNizamani Только если тип столбца целочисленный, как в его примере, да. Но в остальном isset() лучше коррелирует с NULL. Пустая строка также является значением. - person StanE; 17.11.2017
comment
@ShehzadNizamani как насчет значения 0? empty() вернет для него true. и ваш код будет хранить null вместо допустимого целочисленного значения 0 - person Your Common Sense; 13.02.2021

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

bindValue(':param', $n = null, PDO::PARAM_INT);

Надеюсь это поможет.

person Pedro Guglielmo    schedule 24.09.2013

У меня была та же проблема, и я нашел это решение, работающее с bindParam :

    bindParam(':param', $myvar = NULL, PDO::PARAM_INT);
person user1719210    schedule 21.04.2015

Если вы хотите вставить NULL только тогда, когда value равно empty или '', но вставьте value, когда он будет доступен.

A) Получает данные формы с помощью метода POST и вызывает функцию вставки с этими значениями.

insert( $_POST['productId'], // Will be set to NULL if empty    
        $_POST['productName'] ); // Will be to NULL if empty                                

Б) Проверяет, не было ли поле заполнено пользователем, и вставляет NULL, если это так.

public function insert( $productId, $productName )
{ 
    $sql = "INSERT INTO products (  productId, productName ) 
                VALUES ( :productId, :productName )";

    //IMPORTANT: Repace $db with your PDO instance
    $query = $db->prepare($sql); 

    //Works with INT, FLOAT, ETC.
    $query->bindValue(':productId',  !empty($productId)   ? $productId   : NULL, PDO::PARAM_INT); 

    //Works with strings.
    $query->bindValue(':productName',!empty($productName) ? $productName : NULL, PDO::PARAM_STR);   

    $query->execute();      
}

Например, если пользователь ничего не вводит в поле productName формы, тогда $productName будет SET, а EMPTY. Итак, вам нужно проверить, если это empty(), и если это так, то вставить NULL.

Протестировано на PHP 5.5.17

Удачи,

person Arian Acosta    schedule 18.10.2014
comment
Или вы можете использовать функцию MySQL IFNULL() в строке запроса. Вот так: $sql = "INSERT INTO products ( productId, productName ), VALUES ( IFNULL(:productId, NULL), IFNULL(:productName, NULL) )"; - person starleaf1; 10.12.2014
comment
Мне нравится чистота вашего решения. Я только что протестировал его, но, к сожалению, он вставляет пустые строки вместо NULL, когда пользователь ничего не пишет на входе. Это просто вопрос предпочтений, но я предпочитаю NULL вместо пустых строк. - person Arian Acosta; 15.12.2014

На основе других ответов, но с немного большей ясностью в отношении того, как на самом деле использовать это решение.

Если, например, у вас есть пустая строка для значения времени, но вы хотите сохранить ее как нулевое значение:

  if($endtime == ""){
    $db->bind(":endtime",$endtime=NULL,PDO::PARAM_STR);
  }else{
    $db->bind("endtime",$endtime);
  }

Обратите внимание, что для значений времени вы должны использовать PARAM_STR, так как время хранится в виде строк.

person Vincent    schedule 10.03.2020

В моем случае я использую:

  • SQLite,

  • подготовленные операторы с заполнителями для обработки неизвестного количества полей,

  • Запрос AJAX, отправленный пользователем, где все является строкой, и нет таких вещей, как значение NULL и

  • Мне крайне необходимо вставить NULL, так как это не нарушает ограничения внешнего ключа (приемлемое значение).

Предположим, теперь пользователь отправляет с сообщением: $_POST[field1] со значением value1, которое может быть пустой строкой "" или "null" или "NULL".

Сначала я делаю заявление:

$stmt = $this->dbh->prepare("INSERT INTO $table ({$sColumns}) VALUES ({$sValues})");

где {$sColumns} — это что-то вроде field1, field2, ..., а {$sValues} — мои заполнители ?, ?, ....

Затем я собираю данные $_POST, связанные с именами столбцов, в массив $values и заменяю на NULLs:

  for($i = 0; $i < \count($values); $i++)
     if((\strtolower($values[$i]) == 'null') || ($values[$i] == ''))
        $values[$i] = null;

Теперь я могу выполнить:

$stmt->execute($values);

и среди прочего обходить ограничения внешнего ключа.

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

person centurian    schedule 29.11.2018

Попробуй это.

$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null
person hector teran    schedule 04.05.2017
comment
Прежде чем пытаться ответить на такой старый вопрос, вы должны сначала прочитать другие ответы. Может быть, на него уже был дан ответ. - person Your Common Sense; 04.05.2017