Komunikat o błędzie „Ścisłe standardy: tylko zmienne należy przekazywać przez odniesienie”

81
$el = array_shift($instance->find(..))

Powyższy kod w jakiś sposób zgłasza ostrzeżenie o surowych standardach, ale to nie będzie:

function get_arr(){
    return array(1, 2);
}
$el = array_shift(get_arr());

Więc kiedy i tak zgłosi ostrzeżenie?

user198729
źródło
1
Co zwraca $ instance-> find (..)?
Silver Light
2
Oto rozwiązanie: stackoverflow.com/questions/9848295/ ...
ajaristi
Myślę, że przykłady (logika) lub może być niewłaściwy sposób okrągłe w pytaniu, od 2 przykładzie ( get_arr()funkcja) nie wytwarzają ścisłe zawiadomienie norm (testowane PHP 5.2 oraz PHP 5.5).
MrWhite

Odpowiedzi:

93

Rozważ następujący kod:

error_reporting(E_STRICT);
class test {
    function test_arr(&$a) {
        var_dump($a);
    }
    function get_arr() {
        return array(1, 2);
    }
}

$t = new test;
$t->test_arr($t->get_arr());

Spowoduje to wygenerowanie następującego wyniku:

Strict Standards: Only variables should be passed by reference in `test.php` on line 14
array(2) {
  [0]=>
  int(1)
  [1]=>
  int(2)
}

Powód? test::get_arr()Metoda nie jest zmienna i w trybie ścisłym spowoduje to wygenerowanie ostrzeżenia. To zachowanie jest wyjątkowo nieintuicyjne, ponieważ get_arr()metoda zwraca wartość tablicy.

Aby obejść ten błąd w trybie ścisłym, zmień podpis metody, aby nie używała odwołania:

function test_arr($a) {
    var_dump($a);
}

Ponieważ nie możesz zmienić podpisu array_shift, możesz również użyć zmiennej pośredniej:

$inter = get_arr();
$el = array_shift($inter);
leepowers
źródło
7
@ user198729: Szukałem wyjaśnienia lub poprawki i stwierdziłem, że możesz użyć current () dla pierwszego elementu. Niestety metoda end () nie działa na ostatni element, ponieważ „przesuwa wewnętrzny wskaźnik do ostatniego elementu”. current (array_reverse (somefunction ())) działa (tak, to głupie)
MSpreij
1
Użycie currentzakłada, że ​​wskaźnik tablicy znajduje się na pierwszym elemencie. W większości przypadków może to być uzasadnione założenie, ale należy na nie uważać.
cmbuckley
1
@leepowers Oczywiście wystąpiłby ten sam problem, co array_shift()w przypadku, gdy oczekuje się odniesienia do modyfikacji :-)
cmbuckley
1
@ user198729 Możesz uniknąć $intermediatewartości, używając dodatkowej pary nawiasów. $el = array_shift( ( get_arr() ) );. Zobacz stackoverflow.com/questions/9848295/…
Chloe,
1
@Chloe To jest najbardziej genialne rozwiązanie, jakie widziałem, aby uprościć kod! Dziękuję Ci!
hargobind
7

$instance->find() zwraca odniesienie do zmiennej.

Otrzymujesz raport, gdy próbujesz użyć tego odwołania jako argumentu funkcji, bez wcześniejszego zapisywania go w zmiennej.

Pomaga to zapobiegać wyciekom pamięci i prawdopodobnie stanie się błędem w następnych wersjach PHP.

Twój drugi blok kodu zwróciłby błąd, gdyby napisał (zwróć uwagę na &podpis funkcji):

function &get_arr(){
    return array(1, 2);
}
$el = array_shift(get_arr());

Tak więc szybką (i niezbyt przyjemną) poprawką byłoby:

$el = array_shift($tmp = $instance->find(..));

Zasadniczo najpierw należy przypisać zmienną tymczasową i wysłać ją jako argument.

Sagi
źródło
Powinno teraz działać (sprawdziłem). Aby zwrócić referencję, musisz zadeklarować ją w podpisie metody, a nie w instrukcji powrotu (moja wina).
Sagi
Nie, nie mogę zmienić podpisu. Zmienna pośrednia @ pygorex1 może rozwiązać ten problem, ale wygląda na zbędną, prawda?
user198729
Wiem, że nie możesz zmienić podpisu, tylko wyjaśniłem, jak to się dzieje. Państwo mają do wykorzystania tymczasowego (= zmienna pośrednia), ale można to zrobić w tym samym wierszu. Spójrz na mój drugi fragment kodu.
Sagi
4
Wypróbowałem twój drugi fragment, nie działa, działa tylko w osobnym wierszu
user198729
3
W rzeczy samej. Przypisanie zwraca przypisaną wartość . array_shift($tmp = $instance->find(..))przypisuje wartość $instance->find(..)do, $tmpa następnie przekazuje wartość przypisania do array_shift()- co nie jest tym samym, co przekazanie $tmpsamego siebie, więc nie jest lepsze niż pierwotna sytuacja bez przypisania.
phils
6

Przyczyną błędu jest użycie wewnętrznej funkcji programowania struktur danych PHP, array_shift () [php.net/end].

Funkcja przyjmuje tablicę jako parametr. Chociaż znak ampersand jest wskazany w prototypie array_shift()w podręczniku ”, w rozszerzonej definicji tej funkcji nie ma żadnej dokumentacji ostrzegawczej ani wyraźnego wyjaśnienia, że ​​parametr jest w rzeczywistości przekazywany przez odniesienie.

Być może jest to / zrozumiane /. Nie rozumiałem jednak, więc trudno było mi wykryć przyczynę błędu.

Powiel kod:

function get_arr()
{
    return array(1, 2);
}
$array = get_arr();
$el = array_shift($array);
Biju B Adoor
źródło
3

Ten kod:

$monthly_index = array_shift(unpack('H*', date('m/Y')));

Należy zmienić na:

$date_time = date('m/Y');
$unpack = unpack('H*', $date_time);
array_shift($unpack);
user6031348
źródło
0

Cóż, w takich oczywistych przypadkach zawsze możesz powiedzieć PHP, aby blokował komunikaty, używając znaku „@” na początku funkcji.

$monthly_index = @array_shift(unpack('H*', date('m/Y')));

Tłumienie wszystkich błędów w ten sposób może nie być jedną z najlepszych praktyk programistycznych , ale w niektórych przypadkach (takich jak ten) jest to przydatne i jest akceptowalne.

W rezultacie jestem pewien, że "administrator systemu" twojego przyjaciela będzie zadowolony z mniej zanieczyszczonego error.log.

Julio Marchi
źródło
Nie wiem, kto zlekceważył tę odpowiedź, ale przedstawione rozwiązanie DZIAŁA i JEST standardową techniką PHP. Naprawdę rozczarowujące ... Następnym razem mogę już nie odpowiadać na pytanie ... :(
Julio Marchi
5
Przypuszczam, że to dlatego, że zniesienie komunikatu o błędzie nie rozwiązuje problemu z kodem. Co zrobisz, gdy ten typ błędu zmieni się z E_STRICT na E_ERROR w przyszłej wersji PHP, a Twój kod nie będzie teraz działał i nie generuje żadnych błędów / danych wyjściowych?
Łukasz
@TinoDidriksen, rozumiem i zgadzam się z powodami odradzania niektórych „złych nawyków”, szczególnie w przypadku nowych pokoleń. Jednak zasób istnieje do wykorzystania, gdy (i jeśli) jest bezpieczny w użyciu i można go zastosować w proponowanym kontekście. Gdyby eliminator błędów „@” miał zostać usunięty, zostałby usunięty z samego języka. To samo co „eval” (może być złe, ale ma swoje cele). To, co sprzeciwiam się, nie dotyczy wykorzystania niektórych zasobów, ale uogólnienia porady. W szczególności w proponowanym przypadku użycie go nie byłoby szkodliwe, nawet do celów debugowania.
Julio Marchi