Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Pułapki w PHP
Forum PHP.pl > Forum > PHP
michalg
Witam,

Ostatnio w pracy natknęliśmy się na pewien problem, który z pozoru może być niezrozumiały na pierwszy rzut oka. Choć zachowanie to jest prawidłowe i zupełnie uzasadnione. Chodzi o problem foreacha "psującego" tablice (oczywiście problem wystąpił w dużo bardziej złożonym kodzie, ten jest tylko przykładem):

  1. <?php
  2. $tablica = array('1', '2', '3');
  3.    
  4.    foreach($tablica as &$element) {}
  5.  
  6.    foreach($tablica as $element) {}
  7.    
  8.    print_r($tablica);
  9. ?>


Rezultat 1, 2, 2 może wydawać się nieprawidłowy, lecz wszystko działa prawidłowo. Nie napiszę tu, dlaczego tak jest, żeby osoby, które nigdy się z tym nie zetknęły mogły same dojść do tego, co się stało winksmiley.jpg

Znacie jeszcze jakieś takie ciekawe smaczki w PHP?
Maciekbjw
Ameryki nie odkryliście smile.gif

Hmm, co do smaczków winksmiley.jpg

Ciekawym jest, że w operatorze ternariuszu nie można używać echo -> http://www.kacka.pl/index.php?showtopic=24
michalg
Cytat(Maciekbjw @ 26.02.2009, 19:00:36 ) *
Ameryki nie odkryliście smile.gif


W to nie wątpię winksmiley.jpg

Cytat(Maciekbjw @ 26.02.2009, 19:00:36 ) *
Hmm, co do smaczków winksmiley.jpg

Ciekawym jest, że w operatorze ternariuszu nie można używać echo -> http://www.kacka.pl/index.php?showtopic=24


Ciekawe, choć uzasadnienie jest trochę mylące. Powodem raczej nie jest to, że ternariusz musi coś zwracać, tylko to, że echo nie jest funkcją tylko specjalną kontstrukcją języka.

Często, można się też przejechać na porównaniach i niejawnym rzutowaniu typów, np mogłoby się wydawać, że coś takiego będzie porównywać dwa stringi:
  1. <?php
  2. if ("1.00" == "1.0") echo "ROWNE\n";
  3.    if ("001" == "01") echo "ROWNE\n";
  4. ?>


Oczywiście w dokumentacji jest opisane, że w tych wypadkach rzutowanie jest na typy numeryczne.

Inna pułapka, niekoniecznie związana z PHP, ale z arytmetyką zmiennoprzecinkową, to to, że przykładowo dwa nie zawsze jest równe dwa:

  1. <?php
  2. $a = 1;
  3.  
  4.    while ($a < 3) {
  5.    $a += 0.1;
  6.  
  7.    echo $a."\n";    
  8.        if ($a == 2.0) {
  9.        echo "a == 2\n";
  10.        }
  11.  
  12.        if (round($a, 5) == 2.0) {
  13.        echo "round(a, 5) == 2\n";
  14.        }
  15.  
  16.    }
  17. ?>
kodereq
Cytat
Rezultat 1, 2, 2 może wydawać się nieprawidłowy, lecz wszystko działa prawidłowo.


Mógłbym jednak poprosić o wyjaśnienie przykładu ?
mike
Cytat(michalg @ 26.02.2009, 19:30:39 ) *
Powodem raczej nie jest to, że ternariusz musi coś zwracać, tylko to, że echo nie jest funkcją tylko specjalną kontstrukcją języka.
Nie. Uzasadnienie, które podważasz jest poprawne. Twoje jest niepoprawne.
print() podobnie jak echo() jest konstrunkcją językową w PHP ale zwraca wartość. Dlatego można go w omawianej sytuacji użyć.
bazyliszek83
Cytat(kodereq @ 27.02.2009, 10:15:12 ) *


Mógłbym jednak poprosić o wyjaśnienie przykładu ?


Wszystko wyjaśnione jest w manualu:
Cytat
Warning

Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().
ucho
Proszę bardzo: http://pl.php.net/foreach . Osobiście uważam że ten kto wymyślił takie a nie inne zachowanie php powinien oberwać. O http://pl.php.net/manual/pl/types.comparisons.php nie wspominając smile.gif
wookieb
Ja bym jednak polecał troszkę jaśniejsze wyjaśnienie które zobrazuje przypadek.
http://i-code-today.blogspot.com/2009/02/p...references.html

Cytat
Osobiście uważam że ten kto wymyślił takie a nie inne zachowanie php powinien oberwać

Też tak uważałem ale czasem coś takiego może się nawet przydać.

Co do porównywania typów to mam co do tego dobre uczucia. Nie musisz się bawić z konwersja przy porównywaniach. Robi to za ciebie silnik. Jeżeli ktoś zna zasady porównywania to nie sprawi mu to żadnego kłopotu.

Kolejny przykład.
http://forum.php.pl/index.php?showtopic=112957
michalg
Witam,

Cytat(mike @ 27.02.2009, 10:30:52 ) *
Nie. Uzasadnienie, które podważasz jest poprawne. Twoje jest niepoprawne.


Skoro to uzasadnienie (że ternariusz musi coś zwracać) jest poprawne, to dlaczego poniższy fragment kodu działa?
  1. <?php
  2. $a = (true) ? flush() : flush();
  3.    
  4.    var_export($a);
  5. ?>


Funkcja flush nie zwraca żadnej wartości. W efekcie ternariusz również nie zwraca wartości. A jednak działa - dlaczego?

Cytat(mike @ 27.02.2009, 10:30:52 ) *
print() podobnie jak echo() jest konstrunkcją językową w PHP ale zwraca wartość. Dlatego można go w omawianej sytuacji użyć.


Racja, print() również jest konstrukcją językową. Ale to, że zwraca wartość nie ma tutaj znaczenia - ważne jest to, że zachowuje się jak funkcja. Równie dobrze print() mogłby nie zwracać wartości, ale póki miałby charakter funkcji, to mógłby być używany m.in. w ternariuszu.
erix
Cytat
to dlaczego poniższy fragment kodu działa?

Nie działa, gdyż $a ma wartość null. winksmiley.jpg

Analogicznie:
  1. <?php
  2. var_dump($jakasNieistniejacaZmienna);
  3. ?>

też zwraca null.
michalg
Cytat(erix @ 2.03.2009, 17:01:55 ) *
Nie działa, gdyż $a ma wartość null. winksmiley.jpg


Działa w takim sensie, że nie powoduje błędu - tak, jak dzieje się w przypadku użyciu echo. Bo dyskusja dotyczy właśnie tego, dlaczego w ternariuszu nie działa echo.
erix
Cytat
echo() is not actually a function (it is a language construct)

Przy flush" title="Zobacz w manualu PHP" target="_manual takiej wzmianki nie znalazłem. Wszystko jest w porządku.
michalg
Cytat(erix @ 2.03.2009, 17:10:28 ) *
Przy flush" title="Zobacz w manualu PHP" target="_manual takiej wzmianki nie znalazłem. Wszystko jest w porządku.


No i właśnie do tego dążę. Że echo nie działa w ternariuszu nie dlatego, że ternariusz musi zwracać wartość (jak widać na moim przykładzie, nie musi), tylko że echo jest specjalną konstrukcją, której nie można używać jak funkcji.
ucho
Apropo flush() - nawet jak nie użyliście echo ani nic takiego to flush spowoduje wysłanie nagłówków http - raz przez to strasznie się głowiłem dlaczego mam ciągle "headers already sent" tongue.gif
I jedna "pułapka" - zachowanie php które jest kompletnie logiczne, tylko nikt o tym nie wie/nie pamięta. Otóż jeśli nasz użytkownik otworzy np. 10 podstron naszego serwisu to wszystkie, z wyjątkiem aktualnie aktywnego żądania, będą wisieć na session_start(). PHP dba w ten sposób o spójność danych w $_SESSION ale może to zrobić kuku, kiedy jedna z podstron ładuje się długo. Należy używać session_write_close(), kiedy wiemy, że już nic w sesji zmieniać nie będziemy.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.