当我使用bindParam绑定一个常量值时,为什么会出现“无法按引用传递参数2”错误?

113

我正在使用这段代码,但已经非常沮丧了:

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, '', 这些都失败了并抛出了以下错误:

Fatal error: 在 /opt/... 中不能通过引用传递参数2。

11个回答

148

你需要使用 bindValue,而不是 bindParam

bindParam 通过引用来取一个变量,并且在调用 bindParam 的时候不会拉取值。我在 PHP 文档的评论中找到了这个信息:

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

顺便说一下,你可能会尝试这样做bindValue(':param', null, PDO::PARAM_NULL);但并不适用于所有人(感谢Will Shaver的报告)。


我不确定这两者之间的区别,但我会进行一些调查。谢谢,你的回答也很好。 - Ignacio
3
我认为这可能比我的答案更好(如果确实有效)。 - Joe Phillips
我的 PHP 5.3.8 + Mysql 测试表明,在生成的查询中两者之间没有任何区别。重要的部分似乎是传递的值为 NULL 而不是 '' 或 '0'。 - Odin
2
我在MySql 5.1.53上使用PDO::PARAM_NULL遇到了问题,但是当使用空值时,PDO::PARAM_INT运作得非常好。 - Will Shaver
2
Odin:我认为最重要的部分是传递所有三个参数:)。在ign的问题中,他将PDO :: PARAM_NULL作为值传递,而不是作为类型(并且根本没有传递类型)。 - JasonWoof
1
FYI:在大多数情况下,您完全可以使用bindValue()而不是bindParam();对于所有内容,而不仅仅是NULL值。我想不出任何需要将参数绑定到PHP变量的引用的情况 - 但这就是bindParam()的作用。 - low_rents

49

使用 bindParam() 时,必须传递一个变量而不是常量。因此,在该行之前,您需要创建一个变量并将其设置为 null

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

如果你尝试以下代码,也会得到相同的错误信息:

$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);

没错,我刚意识到我在我的代码中已经做过了,$null = PDO::PARAM_NULL; 谢谢。 - Ignacio
1
在这种情况下最好使用bindValue(),如果你无论如何都要使用占位符。bindParam()最初是用于执行查询,然后更改变量并重新执行而不再次绑定参数。bindValue()立即绑定,bindParam()只有在执行时才绑定。 - user3714134
1
我发现在 $myNull = null 这一行中,null 必须小写。$myNull = NULL 是不起作用的。 - raphael75

28

在MySQL中使用可为空的INTEGER列时,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上尝试了这个。


1
$stmt->execute(array( ':param' => !empty($val) ? $val : null )); 使用!empty,因为对于空字符串,您也希望在数据库中将其设置为null。 - Shehzad Nizamani
1
@ShehzadNizamani 只有当列类型为整数时,像他的示例一样,才是正确的。否则 isset() 更适用于 NULL。空字符串也是一个值。 - StanE
@ShehzadNizamani 0 值怎么办?empty() 会返回 true。你的代码将会存储空值而不是合法的 0 整数值。 - Your Common Sense

9

对于那些仍然遇到问题(无法通过引用传递参数2),请定义一个带有空值的变量,而不是仅将null传递给PDO:

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

希望这能帮到你。

5

我遇到了同样的问题,经过使用bindParam找到了解决方案:

    bindParam(':param', $myvar = NULL, PDO::PARAM_INT);

4

有些回答已经给出了你应该做什么的例子。但他们并没有真正解释为什么你应该做其中的一件事。

bindParam方法是用于与循环(或重复语句)等类似情况配合使用的。它绑定一个变量引用。因此,像下面这样的内容:

$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
$stmt->bindParam(':v1', $i, PDO::PARAM_INT);
for ($i = 0; $i < 10; $i++) {
    $stmt->execute();
}

将0到9的值插入表中。

显然,这是一个非常简单的示例,可以使用其他更有效的方法来实现。您可以在此处使用更复杂的逻辑。但基本思想是绑定一个变量的引用,然后您可以更改变量的值。

您可以通过在调用bindParam之前创建一个变量来避免需要引用。但在您的情况下,您不特别想绑定到变量引用。您只想绑定一个值。因此,请使用bindValue来完成这个任务。

您大多数情况下只需使用bindValue即可。但为了说明为什么存在这两种方法,让我们重写先前的示例以使用bindValue而不是bindParam

$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
for ($i = 0; $i < 10; $i++) {
    $stmt->bindValue(':v1', $i, PDO::PARAM_INT);
    $stmt->execute();
}

这样做是可行的,但你必须在每次循环中调用 bindValue,而你只需要调用一次 bindParam 就可以了。但你并没有像那样做,所以你可以直接

$stmt->bindValue(':v1', null, PDO::PARAM_INT);

正如接受的答案所述,一切都会按照预期工作。因为您想要绑定一个值,而不是一个变量引用。


3

如果只有当value为空或''时才想插入NULL,而当其可用时则插入value

A) 使用POST方法接收表单数据,并使用这些值调用insert函数。

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

B) 评估用户是否未填写字段,并在此情况下插入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
祝好运,

3
你可以在查询语句中使用MySQL的IFNULL()函数来完成。像这样:$sql = "INSERT INTO products(productId, productName) VALUES(IFNULL(:productId,NULL), IFNULL(:productName,NULL))"; - starleaf1
我喜欢你的解决方案的简洁性。我刚刚测试了一下,但不幸的是,当用户没有在输入框中输入任何内容时,它会插入空字符串而不是NULL。这只是个人偏好问题,但我更喜欢使用NULL而不是空字符串。 - Arian Acosta

-1

所以你只需要添加一个额外的 If 语句,正确地将你的变量更改为 NULL,然后再调用 bindParam()。这是我为我的情况想出来的一个例子(我被卡在这里好几天了,试图插入一个新的数据库记录,并为一列设置 NULL 值):

 if ($this->companyid == 'NULL' || $this->companyid == NULL) {
            $this->companyid = NULL;
            $this->companyname = NULL;
            $stmt->bindParam(':companyid', $this->companyid);
            $stmt->bindParam(':companyname', $this->companyname);
        } else {
            $stmt->bindParam(':companyid', $this->companyid);
            $stmt->bindParam(':companyname', $this->companyname);
        }

这段代码毫无意义,条件语句中使用赋值运算符。假设您想将字符串“NULL”转换为 null 值,仅针对这个非常不寻常(且无关)的情况,只需要一行代码:$this->companyid = $this->companyid === 'NULL' ? NULL : $this->companyid; - Your Common Sense
$this->companyid == NULL 这个条件仍然没有意义,因为你基本上写的是 if ($this->companyid == NULL) {$this->companyid = NULL;} - Your Common Sense
你可能需要使用 if ($this->companyid == 'NULL' || !$this->companyid) 来覆盖所有空值(例如 false、空字符串、零等)以及字面上的 null。但是,你的问题仍然超出了这个问题的范围。 - Your Common Sense

-1

基于其他答案,但更清晰地说明如何实际使用此解决方案。

例如,如果您的时间值为空字符串,但您想将其保存为 null:

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

请注意,对于时间值,您应该使用PARAM_STR,因为时间存储为字符串。

-2

试一下。

$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null

1
在尝试回答这样一个老问题之前,您应该先阅读其他答案。也许它已经被回答了。 - Your Common Sense

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接