Estoy creando una función de envoltura alrededor de mysqli para que mi aplicación no tenga que ser demasiado complicada con el código de manejo de la base de datos. Parte de eso es un poco de código para parametrizar las llamadas SQL usando mysqli :: bind_param(). bind_param(), como ya sabrá, requiere referencias. Ya que es una envoltura semi general, termino haciendo esta llamada:Mi matriz de referencias de PHP se está "convirtiendo mágicamente" en una serie de valores ... ¿por qué?
call_user_func_array(array($stmt, 'bind_param'), $this->bindArgs);
y me sale un mensaje de error:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
La discusión anterior es que Forstall quienes digan "Usted don' Necesito referencias en su ejemplo ".
Mi código "real" es un poco más complicado de lo que nadie quiere leer, así que he hierve el código que conduce a este error en el siguiente (con suerte) ejemplo ilustrativo:
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'));
El problema parece ser que, en myfunc(), entre la llamada a setArray() y la llamada a dumpArray(), todos los elementos en $ obj-> myarray dejan de ser referencias y se convierten en valores. Esto se puede ver fácilmente mirando a la salida:
array(2) {
[0]=>
&string(4) "val1"
[1]=>
&string(4) "val2"
}
array(2) {
[0]=>
string(4) "val1"
[1]=>
string(4) "val2"
}
Tenga en cuenta que la matriz está en el estado "correcto" en la primera mitad de la producción aquí. Si tenía sentido hacerlo, podría hacer mi llamada bind_param() en ese punto, y funcionaría. Lamentablemente, algo se rompe en la segunda mitad de la salida. Tenga en cuenta la falta de "&" en los tipos de valor de matriz.
¿Qué pasó con mis referencias? ¿Cómo puedo evitar que esto suceda? Odio llamar "error de PHP" cuando realmente no soy un experto en idiomas, pero ¿podría ser este? Me parece muy extraño. Estoy usando PHP 5.3.8 para mis pruebas en este momento.
Editar:
Como más de una persona señaló, la solución es cambiar setArray() para aceptar su argumento por referencia:
function setArray(&$vals) {
estoy añadiendo a esta nota documente POR QUÉ esto parece funcionar.
PHP en general, y mysqli en particular, parecen tener un concepto un tanto extraño de lo que es una "referencia". Observe este ejemplo:
$a = "foo";
$b = array(&$a);
$c = array(&$a);
var_dump($b);
var_dump($c);
En primer lugar, estoy seguro de que se esté preguntando por qué estoy usando matrices en lugar de variables escalares - es porque var_dump() no muestra ninguna indicación de si un escalar es una referencia, pero lo hace para los miembros de la matriz.
De todos modos, en este punto, $ b [0] y $ c [0] son ambas referencias a $ a. Hasta aquí todo bien. Ahora tiramos nuestra primera llave en las obras:
unset($a);
var_dump($b);
var_dump($c);
$ b [0] y $ c [0] se sigue manteniendo referencias a la misma cosa. Si cambiamos uno, ambos seguirán cambiando. Pero, ¿a qué se refieren? Alguna ubicación sin nombre en la memoria. Por supuesto, la recolección de basura asegura que nuestros datos estén seguros, y lo seguirá siendo, hasta que dejemos de referirnos a ellos.
Para nuestro siguiente truco, hacemos esto:
unset($b);
var_dump($c);
Ahora $ c [0] es la única referencia que queda a nuestros datos. Y, whoa! Mágicamente, ya no es una "referencia". No por la medida de var_dump(), y tampoco por la medida de mysqli :: bind_param().
El PHP manual dice que hay una marca separada, 'is_ref' en cada dato. Sin embargo, esta prueba parece sugerir que 'is_ref' es en realidad equivalente a '(refcount> 1)'
Para divertirse, puede modificar este ejemplo juguete de la siguiente manera:
$a = array("foo");
$b = array(&$a[0]);
$c = array(&$a[0]);
var_dump($a);
var_dump($b);
var_dump($c);
Tenga en cuenta que los tres las matrices tienen la marca de referencia en sus miembros, lo que respalda la idea de que 'is_ref' es funcionalmente equivalente a '(refcount> 1)'.
Me supera el hecho de que mysqli :: bind_param() se preocupe por esta distinción en primer lugar (o quizás sea call_user_func_array() ... de cualquier forma), pero parece que lo que "realmente" necesitamos asegurar es que el recuento de referencias es al menos para cada miembro de $ this-> bindArgs en nuestra llamada a call_user_func_array() (vea el comienzo de la publicación/pregunta). Y la forma más fácil de hacerlo (en este caso) es hacer que setArray() pase por referencia.
Editar:
Para la diversión y juegos extra, he modificado mi programa original (no se muestra aquí) para dejar su equivalente a setArray() pasan por valor, y para crear una matriz adicional gratuita , bindArgsCopy, que contiene exactamente lo mismo que bindArgs. Lo que significa que, sí, ambas matrices contenían referencias a datos "temporales" que fueron desasignados por el momento de la segunda llamada. Según lo predicho por el análisis anterior, esto funcionó. Esto demuestra que el análisis anterior no es un artefacto del funcionamiento interno de var_dump(), al menos, un alivio para mí, y también demuestra que es el recuento de referencias lo que importa, no la "temporalidad" del original. almacenamiento de datos.
So. Hago la siguiente afirmación: en PHP, para call_user_func_array() (y probablemente más), decir que un elemento de datos es una "referencia" es lo mismo que decir que el recuento de referencias del elemento es mayor o igual que 2 (ignorando las optimizaciones de memoria interna de PHP para escalares de igual valorada)
nota Administrivialidades: me encantaría darle Mario el crédito sitio para la respuesta, ya que fue el primero en sugerir la respuesta correcta, pero ya que él lo escribió en un comentario, no una respuesta real, no podría hacerlo :-(
Wouldn' t el método 'setArray()' también necesita obtener la matriz como parámetro de referencia? De lo contrario, solo está creando referencias a una matriz temporal. – mario
@mario: Usted, señor, tiene toda la razón, ya que esto soluciona mi problema. Lo cual plantea la pregunta ... _¿Por qué soluciona esto el problema? Esperaría que una referencia a una matriz temporal siga siendo una referencia, solo una referencia a algo que solo se mantiene en la memoria debido a la referencia misma. Supongo que debería leer sobre la administración de la memoria PHP, si puedo encontrar un documento de este tipo. –
Sí, put & $ vals en la función setArray – malletjo