Я создаю функцию-оболочку для mysqli, чтобы мое приложение не слишком усложнялось кодом обработки базы данных.Частично это немного кода для параметризации вызовов SQL с использованием mysqli :: bind_param ().bind_param (), как вы знаете, требует ссылки.Так как это полуобобщающая оболочка, я в итоге выполняю этот вызов:
call_user_func_array(array($stmt, 'bind_param'), $this->bindArgs);
и получаю сообщение об ошибке:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
Вышеприведенное обсуждение предназначено для утверждения тех, кто скажет«В вашем примере ссылки вообще не нужны».
Мой «настоящий» код немного сложнее, чем кто-либо хочет прочитать, поэтому я свел код, приведший к этой ошибке, вследующий (надеюсь) иллюстративный пример:
class myclass {
private $myarray = array();
function setArray($vals) {
foreach ($vals as $key => &$value) {
$this->myarray[] =& $value;
}
$this->dumpArray();
}
function dumpArray() {
var_dump($this->myarray);
}
}
function myfunc($vals) {
$obj = new myclass;
$obj->setArray($vals);
$obj->dumpArray();
}
myfunc(array('key1' => 'val1',
'key2' => 'val2'));
Проблема заключается в том, что в myfunc () между вызовом setArray () и вызовом dumpArray () все элементы в $ obj-> myarray перестанет быть ссылками и станет просто ценностями.Это легко увидеть, посмотрев на вывод:
array(2) {
[0]=>
&string(4) "val1"
[1]=>
&string(4) "val2"
}
array(2) {
[0]=>
string(4) "val1"
[1]=>
string(4) "val2"
}
Обратите внимание, что массив находится в «правильном» состоянии в первой половине вывода здесь.Если бы это имело смысл, я мог бы сделать мой вызов bind_param () в этот момент, и это сработало бы.К сожалению, что-то ломается во второй половине вывода.Обратите внимание на отсутствие символа «&» в типах значений массива.
Что случилось с моими ссылками?Как я могу предотвратить это?Я ненавижу называть «ошибка PHP», когда я действительно не являюсь экспертом по языку, но может ли это быть одним из них?Это кажется очень странным для меня.В настоящее время я использую PHP 5.3.8 для своего тестирования.
Редактировать:
Как указали несколько человек, исправление заключается в изменении setArray () для принятияего аргумент по ссылке:
function setArray(&$vals) {
Я добавляю это примечание к документу, ПОЧЕМУ это работает.
PHP в целом и mysqli в частности, кажется, имеют немного странную концепциючто такое «ссылка».Посмотрите на этот пример:
$a = "foo";
$b = array(&$a);
$c = array(&$a);
var_dump($b);
var_dump($c);
Прежде всего, я уверен, что вам интересно, почему я использую массивы вместо скалярных переменных - это потому, что var_dump () не показывает никаких признаков того,скаляр - это ссылка, но это относится к элементам массива.
В любом случае, на этом этапе $ b [0] и $ c [0] являются ссылками на $ a.Все идет нормально.Теперь мы добавим наш первый ключ к работам:
unset($a);
var_dump($b);
var_dump($c);
$ b [0] и $ c [0] - все еще ссылки на одно и то же.Если мы изменим один, оба изменится.Но на что они ссылаются?Некоторое безымянное место в памяти.Конечно, сборка мусора гарантирует, что наши данные в безопасности и останутся таковыми, пока мы не перестанем ссылаться на них.
Для нашего следующего трюка мы сделаем следующее:
unset($b);
var_dump($c);
Now $c [0] - единственная оставшаяся ссылка на наши данные.И, воу!Волшебно, это больше не «ссылка».Не по мере var_dump () и не по mysqli :: bind_param ().
В руководстве по PHP написано, что на каждом есть отдельный флаг 'is_ref'часть данных.Тем не менее, этот тест показывает, что 'is_ref' фактически эквивалентен '(refcount> 1)'
Для забавы, вы можете изменить этот пример игрушки следующим образом:
$a = array("foo");
$b = array(&$a[0]);
$c = array(&$a[0]);
var_dump($a);
var_dump($b);
var_dump($c);
Примечаниечто все три массива имеют ссылочную метку на своих членах, что подтверждает идею, что 'is_ref' функционально эквивалентен '(refcount> 1)'.
Почему я не знаю mysqli:: bind_param () в первую очередь заботился бы об этом различии (или, возможно, это call_user_func_array () ... в любом случае), но, похоже, нам действительно нужно убедиться, что счетчик ссылок по крайней мере 2 для каждого члена $ this-> bindArgs в нашем вызове call_user_func_array () (см. Самое начало сообщения / вопроса).И самый простой способ сделать это (в этом случае) состоит в том, чтобы сделать setArray () передачей по ссылке.
Редактировать:
Для дополнительного удовольствия и игр я изменил свою исходную программу (здесь не показана), чтобы оставить ее эквивалентную setArray () для передачи по значению, и создать дополнительный массив, bindArgsCopy, содержащий точно то же самое, что и bindArgs , Это означает, что да, оба массива содержали ссылки на «временные» данные, которые были освобождены ко времени второго вызова. Как и следовало из анализа выше, это сработало. Это демонстрирует, что приведенный выше анализ не является артефактом внутренней работы var_dump () (по крайней мере, для меня это облегчение), а также демонстрирует, что важен счетчик ссылок, а не «временность» оригинала. хранение данных.
Итак. Я делаю следующее утверждение: В PHP для вызова call_user_func_array () (и, возможно, более) сказать, что элемент данных является «ссылкой», - это то же самое, что сказать, что счетчик ссылок элемента больше или равен 2. (игнорирование оптимизации внутренней памяти PHP для равнозначных скаляров)
Примечание администратора: я бы с удовольствием дал Марио кредит сайта за ответ, так как он первым предложил правильный ответ, но так как он написал это в комментарии, а не в реальном ответе, я не смог сделать это: - (