简体   繁体   English

对数组和循环使用 bind_param

[英]Using bind_param with arrays and loops

according to this example for prepared statements I first bind parameters and then set values for the parameters.根据这个准备好的语句示例,我首先绑定参数,然后为参数设置值。

Let's assume I have a 2-dim array $alias假设我有一个二维数组 $alias

$array1 = [
    'id' => 1,
    'tstamp' => 123456789,
    'alias' => '$alias',
];

$array2 = [
    'id' => 1,
    'tstamp' => 123456789,
    'alias' => '$alias2',

];

$alias = [$array1, $array2];

Why is this code working为什么这段代码有效

$insert = 'INSERT INTO abcdef VALUES (?,?,?)';
$insertStmt = $conn->prepare($insert);
foreach ($alias as $array) {
    $insertStmt->bind_param('iis', $array['id'], $array['tstamp'], $array['alias']);
    $insertStmt->execute();
}

and this not?这不是吗?

$insert = 'INSERT INTO abcdef VALUES (?,?,?)';
$insertStmt = $conn->prepare($insert);
$insertStmt->bind_param('iis', $array['id'], $array['tstamp'], $array['alias']);
foreach ($alias as $array) {
   $insertStmt->execute();
}

If I have to bind the parameters all the time there's a lot more trafic, isn't it?如果我必须一直绑定参数,那么流量会更多,不是吗?

bind_param() binds by reference to a specific zval container . bind_param()通过引用特定的zval 容器进行绑定。 On each iteration of the loop a new array symbol table is allocated with its own zval containers.在循环的每次迭代中,都会为新的数组符号表分配自己的 zval 容器。 If at the time of binding the zval containers do not exist they will be created.如果在绑定时 zval 容器不存在,它们将被创建。 This can be shown with the following code:这可以用以下代码显示:

$insertStmt = $conn->prepare('INSERT INTO abcdef VALUES (?,?,?)');
$insertStmt->bind_param('sss', $array['id'], $array['tstamp'], $array['alias']);
var_dump($array);

outputs:输出:

array (size=3)
  'id' => null
  'tstamp' => null
  'alias' => null

Even though we didn't declare the $array anywhere, the binding implicitly created it with null values.即使我们没有在任何地方声明$array ,绑定也隐式地使用空值创建了它。 Bindings will keep on pointing to this empty array.绑定将继续指向这个空数组。

Of course when we start iterating the alias array, the $array will be created anew each time.当然,当我们开始迭代别名数组时,每次都会重新创建$array The old array symbol table, to which we bound the parameters is now gone.我们绑定参数的旧数组符号表现在已经消失了。 We haven't bound anything to the new array.我们还没有将任何东西绑定到新数组。

To solve this you can simply move the bind_param() inside the foreach loop as:要解决这个问题,您可以简单地将bind_param()移动到foreach循环内,如下所示:

$insertStmt = $conn->prepare('INSERT INTO abcdef VALUES (?,?,?)');
foreach ($alias as $array) {
    // bind to the new zval containers every iteration
    $insertStmt->bind_param('sss', $array['id'], $array['tstamp'], $array['alias']);
    $insertStmt->execute();
}

If I have to bind the parameters all the time there's a lot more traffic, isn't it?如果我必须一直绑定参数,那么流量会更多,不是吗?

The traffic is generated on prepare and execute events.流量是在prepareexecute事件上生成的。

Having said that, you can re-arrange your a little to move ->bind_param() outside the loop.话虽如此,您可以重新安排您的一点->bind_param()移到循环之外。 The simpler approach is to bind variables instead of array:更简单的方法是绑定变量而不是数组:

$array1 = ['id' => 1, 'tstamp' => 123456789, 'alias' => '$array1'];
$array2 = ['id' => 1, 'tstamp' => 123456789, 'alias' => '$array2'];
$arrays = [$array1, $array2];

$insert = 'INSERT INTO abcdef VALUES (?,?,?)';
$insertStmt = $conn->prepare($insert);

$bv_id      = NULL;
$bv_tstamp  = NULL;
$bv_alias   = NULL;
$insertStmt->bind_param('iis', $bv_id, $bv_tstamp, $bv_alias);

foreach ($arrays as $array) {
    // you could use extract($array, ...) below
    // but its use is highly discouraged
    $bv_id     = $array['id'];
    $bv_tstamp = $array['tstamp'];
    $bv_alias  = $array['alias'];
    $insertStmt->execute();
}

If you must use arrays then use this approach:如果必须使用数组,请使用以下方法:

$array = ['id' => null, 'tstamp' => null, 'alias' => null];
$insertStmt->bind_param('iis', $array['id'], $array['tstamp'], $array['alias']);
foreach ($arrays as $copy) {
    foreach ($copy as $k => $v) {
        $array[$k] = $v;
    }
    $insertStmt->execute();
}

Notice that I used a different name for the loop variable inside the foreach construct (we need to keep track of the array that is bound to the statement and loop variables get overwritten on each iteration).请注意,我在foreach构造中为循环变量使用了不同的名称(我们需要跟踪绑定到语句的数组,并且循环变量在每次迭代时都会被覆盖)。 You will have to manually copy the values from the loop variable to the bound array.您必须手动将值从循环变量复制到绑定数组。

Why the second one fails: 为什么第二个失败:

PHP is "procedural", not "declarative". PHP是“过程的”,而不是“声明的”。 That is, when you say "bind_param" it does the binding right then and there, using whatever you give it. 也就是说,当您说“ bind_param”时,它会随即使用您提供的任何内容进行绑定。 Since $array is not yet defined (it is coming in the foreach ), it probably plugs NULLs into the query. 由于$array尚未定义(它在foreach ),它可能会将NULL插入查询中。

Performance: 性能:

Binding is a relatively minor text escaping and substitution. 绑定是相对较小的文本转义和替换。 It does not involve a roundtrip to the server, which would be costly. 它不涉及服务器往返,这将是昂贵的。

Bottom Line: Do the binding; 底线:进行装订; don't worry about the performance, the benefit (prevention of certain types of hacking) far outweighs it. 不必担心性能,其好处(防止某些类型的黑客入侵)远胜于它。

PHP problem: PHP问题:

foreach ($alias as $array) {

--> ->

foreach ($alias as &$array) {

That change may (or may not?) make the second for 'work'. 这种改变可能(或可能不会?)成为“工作”的第二要点。 This is because of the quirky way that PHP handles "references". 这是因为PHP处理“引用”的方式古怪。

Variables (eg, $array) is passed as a "reference" (think "pointer") to a value (an array in this case). 变量(例如$ array)作为“引用”(例如“指针”)传递给值(在这种情况下为数组)。 But, as soon as you start to modify the value, it copies the target over ("copy on write"), thereby disconnecting it from the original copy. 但是,一旦您开始修改该值,它就会将目标复制过来(“写入时复制”),从而将其与原始副本断开连接。

When declaring a function, you can use & in front of an argument in order to avoid the COW and keep the reference pointing to the original thing. 声明函数时,可以在参数前使用& ,以避免出现COW并使引用指向原始内容。

I'm guessing that such is needed in this case, but I worry that it is not sufficient. 猜想在这种情况下需要这样做 ,但是我担心这还不够。

foreach($alias as $array) is probably throwing away the previous value of $array and providing a new one. foreach($alias as $array)可能会舍弃$array的先前值并提供一个新值。
foreach($alias as &$array) is probably hanging on to the reference, thereby allowing you to change the contents of $array, and have it reflected in $alias . foreach($alias as &$array)可能会挂在引用上,从而允许您更改$ array的内容,并将其反映在$alias

Alas, that is not quite what you need. ,那不是您真正需要的。

Maybe & is needed in some other way. 也许&需要其他方式。 Maybe here? 也许在这里? (I don't even know if this is syntactically valid.) (我什至不知道这在语法上是否有效。)

$alias = [&$array1, &$array2];

In your case在你的情况下

this code is working此代码正在工作

$insert = 'INSERT INTO abcdef VALUES (?,?,?)';
$insertStmt = $conn->prepare($insert);
foreach ($alias as $array) {
    $insertStmt->bind_param('iis', $array['id'], $array['tstamp'], $array['alias']);
    $insertStmt->execute();
}

because for array iteration we use for loop or foreach to extract key by key array values so in this code value in your $array variable will set one by one value from $alias array因为对于数组迭代,我们使用 for 循环或 foreach 来逐键提取数组值,因此在此代码中,您 $array 变量中的值将从 $alias 数组中设置一个一个值

And in this code在这段代码中

$insert = 'INSERT INTO abcdef VALUES (?,?,?)';
$insertStmt = $conn->prepare($insert);
$insertStmt->bind_param('iis', $array['id'], $array['tstamp'], $array['alias']);
foreach ($alias as $array) {
   $insertStmt->execute();
}

You have used $array variable which is not intialized or declared in any upper area scope so it will get null values so every time execute function will get null value....您已经使用了 $array 变量,该变量未在任何上部区域范围内初始化或声明,因此它将获得空值,因此每次执行函数都会获得空值....

You need to set Mysql Error reporting to get error. 您需要设置Mysql错误报告以获取错误。 Below is code that throwing error in case of the wrong code that's not working. 以下是在错误代码无法正常工作时抛出错误的代码。 And the reason why it's not working is, because the variable $array is undefined or having null values that's why bind_param is not working. 而且它不起作用的原因是,因为变量$ array未定义或具有空值,这就是bind_param不起作用的原因。 And since it's Mysql level error, So you need to enable Mysql error reporting in your code. 由于这是Mysql级别的错误,因此您需要在代码中启用Mysql错误报告。

Below is the complete code that throws error in case of your wrong code. 下面是完整的代码,如果您输入了错误的代码,它将引发错误。

<?php

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$array1 = [
    'id' => 1,
    'tstamp' => 123456789,
    'alias' => '$alias',
];

$array2 = [
    'id' => 1,
    'tstamp' => 123456789,
    'alias' => '$alias2',
];

$alias = [$array1, $array2];

$servername = "localhost";
$username = "root";
$password = "root";
$dbname = "testdv";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

try {
    $insert = 'INSERT INTO test VALUES (?,?,?)';
    $insertStmt = $conn->prepare($insert);
    $insertStmt->bind_param('iis', $array['id'], $arrays['tstamp'], $arrays['alias']);
    foreach ($alias as $array) {
        $insertStmt->execute();
    }
} catch (\Exception $e) {
    echo $e->getMessage();
}

Hope it helps! 希望能帮助到你!

Thanks 谢谢

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM