2013-08-29 11 views
6

Mam następujący kod w produkcji, który wydaje się być przyczyną nieskończonej pętli.Opis priorytetu operatorów w php

$z=1; 
while (!$apns = $this->getApns($streamContext) && $z < 11) 
{ 
    myerror_log("unable to conncect to apple. sleep for 2 seconds and try again"); 
    $z++; 
    sleep(2); 
} 

W jaki sposób stosowane są reguły pierwszeństwa, które powodują takie zachowanie?

http://php.net/manual/en/language.operators.precedence.php

widzę tę notatkę w docs:

Chociaż = ma niższy priorytet niż większość innych operatorów, PHP nadal pozwalają wyrażenia podobne do poniższego: if ($ a = foo()), w przypadku którego zwracana jest wartość foo() do $ a.

Który sprawia, że ​​myślę, że = należy najpierw ocenić. a później ! następnie & &, który nie spowoduje nieskończonej pętli.

+0

Czy chodziło Ci używać '==' (porównanie) zamiast ' = '(przypisanie) w pętli while? – vimist

+1

nie, to polecenie naprawdę oznacza po prostu, że'! 'po lewej stronie' = 'jest poprawne.' && 'nadal jest częścią przypisywanej wartości – Dave

+2

także: NIE. BARDZO Nie koduj tak, to jest ohydne – Dave

Odpowiedz

2

Kod ocenia tak:

while (!($apns = ($this->getApns($streamContext) && ($z < 11)))) 

dlatego widać nieskończoną pętlę (jak najszybciej $z >= 11, $apns jest fałszywa, więc warunek jest zawsze prawdziwe). Powodem tego pierwszeństwa jest to, że reguły specjalne mają zastosowanie tylko do ! na po lewej stronie przypisanego przypisania (o niższym priorytecie niż =). Nie ma wpływu na operator boolowski po prawej, który zachowuje się tak, jak w każdym rozsądnym języku.

Twój styl jest zły. Spróbuj tego, co jest o wiele bardziej czytelny i różni się tylko w końcowej wartości $z (i jeśli to ważne można dostosować oświadczenie break.

for($z = 1; $z < 11; ++ $z) { 
    // note extra brackets to make it clear that we intend to do assignment not comparison 
    if(($apns = $this->getApns($streamContext))) { 
     break; 
    } 
    myerror_log("unable to conncect to apple. sleep for 2 seconds and try again"); 
    sleep(2); 
} 
+0

wydaje mi się, że $ pensy nigdy nie miałyby nic przypisanego poza wartością boolowską. Dobrze? Reszta kodu, który łączy się z jabłkiem i wysyła wiadomość push, nigdy nie zadziała. – digidigo

+0

'$ apns' otrzyma wszystko, co' getApns' zwróci. To jest wspólny wzorzec; skutecznie czeka, aż '$ apns' stanie się prawdą (tzn. nie-zerową), a następnie będzie kontynuowane z kodem.Zwróć uwagę, że ponieważ porównanie jest stosowane po przypisaniu, nie ma wpływu na wartość, która jest w '$ apns'. Ale jeśli chcesz uczynić to jaśniejszym, dzieląc zadanie od stanu, to jest również dobre. – Dave

+1

W przeciwieństwie do mojego wcześniejszego komentarza (teraz edytowanego), '&&' i '||' * konwertują wartości na 'true' lub' false', więc twój początkowy kod zawsze zawierałby 'true' lub' false 'in' $ apns', co oznacza, że ​​nigdy nie zadziała. Kod, który wysłałem, zadziała. – Dave

2

Twój kod jest wyraźnym przykładem, dlaczego to dobry zwyczaj, aby zawsze umieścić wszystkie warunki podane w nawiasach (i to samo odnosi się do bloku kodu. Nawet oneliners powinny być otoczone { i }). Zamiast więc podatne na błędy:

while (!$apns = $this->getApns($streamContext) && $z < 11) 

zrobić

while (!($apns = $this->getApns($streamContext)) && ($z < 11)) 

a będziesz bezpieczny.