Tworzę funkcję otoki wokół mysqli, więc moja aplikacja nie musi być zbyt skomplikowana z kodem obsługi bazy danych. Częścią tego jest trochę kodu do sparametryzowania wywołań SQL za pomocą mysqli :: bind_param(). bind_param(), jak być może wiesz, wymaga referencji. Ponieważ jest to pół-ogólnie wrapper, ja skończyć podejmowania tego połączenia:Moja tablica referencyjna PHP jest "magicznie" stająca się zbiorem wartości ... dlaczego?
call_user_func_array(array($stmt, 'bind_param'), $this->bindArgs);
i pojawia się komunikat o błędzie:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
Powyższa dyskusja jest Forstall tych, którzy mówią „don” t w twoim przykładzie potrzebują referencji ".
Mój „prawdziwy” Kod jest nieco bardziej skomplikowana, niż ktoś chce czytać, więc ja gotuje kod prowadzące do tego błędu w następującym (miejmy nadzieję) ilustrującego przykładu:
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'));
Problem wydaje się, że w myfunc(), pomiędzy wywołaniem setArray() a wywołaniem dumpArray(), wszystkie elementy w $ obj-> myarray przestają być referencjami i stają się zamiast nich tylko wartościami. Można to łatwo zauważyć patrząc na wyjściu:
array(2) {
[0]=>
&string(4) "val1"
[1]=>
&string(4) "val2"
}
array(2) {
[0]=>
string(4) "val1"
[1]=>
string(4) "val2"
}
Zauważ, że tablica jest w „prawidłowym” państwa w pierwszej połowie wyjściu tutaj. Jeśli miałoby to sens, mógłbym wywołać moje wywołanie bind_param() w tym miejscu i to działałoby. Niestety coś się psuje w drugiej połowie produkcji. Zwróć uwagę na brak "&" dla typów wartości macierzy.
Co stało się z moimi referencjami? Jak mogę temu zapobiec? Nienawidzę nazywać "PHP bug", kiedy naprawdę nie jestem ekspertem od języków, ale czy to może być jeden? Wydaje mi się to bardzo dziwne. Używam PHP 5.3.8 do moich testów w tej chwili.
Edit:
jako więcej niż jedna osoba wskazała, poprawka jest zmiana setArray(), aby zaakceptować swój argument przez odniesienie:
function setArray(&$vals) {
Dodaję tę notatkę do dokument DLACZEGO to wydaje się działać.
PHP ogólnie, a mysqli w szczególności, wydaje się mieć nieco dziwną koncepcję tego, czym jest "referencja". Obserwuj ten przykład:
$a = "foo";
$b = array(&$a);
$c = array(&$a);
var_dump($b);
var_dump($c);
Przede wszystkim, jestem pewien, że zastanawiasz się, dlaczego używam zamiast tablic zmiennych skalarnych - to dlatego var_dump() nie wykazały żadnego wskazania, czy skalara jest referencją, ale dotyczy członków tablicy.
W każdym razie, $ b [0] i $ c [0] oznaczają odniesienia do $ a. Jak na razie dobrze. Teraz rzucamy nasz pierwszy klucz do prac:
unset($a);
var_dump($b);
var_dump($c);
$ b [0] i $ c [0] są zarówno nadal odwołuje się do tego samego. Jeśli zmienimy jeden, oba będą się zmieniać. Ale do czego one się odnoszą? Pewna nienazwana lokalizacja w pamięci. Oczywiście, zbieranie śmieci zapewnia, że nasze dane są bezpieczne i tak pozostaną, dopóki nie przestaniemy się z nimi zapoznać.
naszej kolejnej sztuczki, możemy to zrobić:
unset($b);
var_dump($c);
Teraz $ c [0] to jedyne odniesienie do naszych danych. I, whoa! Magicznie, to już nie jest "odniesienie". Nie według miary var_dump(), a nie przez miarę mysqli :: bind_param().
Urządzenie PHP manual mówi, że na każdym elemencie danych znajduje się osobna flaga "is_ref". Jednak badanie to wydaje się sugerować, że „is_ref” jest rzeczywiście równoważne „(RefCount> 1)”
Dla zabawy, które można modyfikować ten przykład zabawki następująco:
$a = array("foo");
$b = array(&$a[0]);
$c = array(&$a[0]);
var_dump($a);
var_dump($b);
var_dump($c);
Zauważ, że wszystkie trzy Macierze mają znak odniesienia na swoich elementach, który potwierdza, że "is_ref" jest funkcjonalnie równoważne "(refcount> 1)".
To mnie przerasta, dlaczego mysqli :: bind_param() zadba o to rozróżnienie w pierwszej kolejności (lub może to call_user_func_array() ... tak czy inaczej), ale wydaje się, że to, co "naprawdę" musimy zapewnić jest to, że liczba referencyjna wynosi co najmniej dla każdego członka $ this-> bindArgs w naszym wywołaniu call_user_func_array() (patrz sam początek post/pytanie). Najłatwiejszym sposobem na zrobienie tego (w tym przypadku) jest przekazanie polecenia setArray().
Edit:
Dla dodatkowej zabawy i gry, zmodyfikowałem mój oryginalny program (nie pokazaną tutaj), aby opuścić swój odpowiednik setArray() przechodzi przez wartość, a do stworzenia nieuzasadnioną dodatkową tablicę , bindArgsCopy, zawierające dokładnie to samo co bindArgs. Co oznacza, że tak, obie tablice zawierają odniesienia do "tymczasowych" danych, które zostały zdekalokowane do czasu drugiego połączenia. Jak przewidywano w powyższej analizie, zadziałało to. To pokazuje, że powyższa analiza nie jest artefaktem wewnętrznych działań var_dump() (przynajmniej ulga), a także pokazuje, że liczy się licznik referencji, a nie "tymczasowość" oryginału przechowywanie danych.
So. Wykonuję następujące stwierdzenie: W PHP, dla funkcji call_user_func_array() (i prawdopodobnie więcej), powiedzenie, że element danych jest "referencją", to to samo, co powiedzenie, że liczba referencyjna elementu jest większa lub równa 2 (ignorując wewnętrzne optymalizacje pamięci PHP dla równych wartościach skalarnych)
Administrivia uwaga: Chciałbym dać Mario kredytu strony na odpowiedź, jak był pierwszy zaproponować prawidłową odpowiedź, ale ponieważ on napisał w komentarzu, a nie rzeczywiste odpowiedź, nie mogłem zrobić :-(
Wouldn” • Metoda 'setArray()' również musi pobrać tablicę jako parametr odniesienia? Inaczej tworzysz odniesienia do tymczasowej tablicy. – mario
@mario: Pan, sir, ma absolutną rację, ponieważ to rozwiązuje mój problem. Co nasuwa pytanie ... _Dlaczego_ to rozwiązuje problem? Spodziewam się, że odniesienie do tymczasowej tablicy nadal będzie punktem odniesienia, a jedynie odniesieniem do czegoś, co jest przechowywane tylko w pamięci z powodu samego odniesienia. Przypuszczam, że powinienem czytać na temat zarządzania pamięcią PHP, jeśli uda mi się znaleźć taki dokument. –
Tak, put & $ vals w funkcji setArray – malletjo