Ordine evaluare condiţii in PHP

Mereu m-a nedumerit logica sucită în care php-ul insistă să evalueze expresiile de genul ăsta:

<?php

$curier = "fan";

$descr = ($curier == "fan" ? "Fan Courier" : $curier == "urgent" ? "Urgent Curier" : "");
echo "<p>Evaluare 1: $descr</p>";

$descr = ($curier == "fan" ? "Fan Courier" : ($curier == "urgent" ? "Urgent Curier" : ""));
echo "<p>Evaluare 2: $descr</p>";

?>

Rezultat:

Evaluare 1: Urgent Curier
Evaluare 2: Fan Courier

Obişnuit cu modul în care evaluează C/C++, mă uitam ca prostul la rezultatul bizar care îmi era afişat pe ecran :slight_smile:

Ciudat, într-adevăr. În JS evaluează bine, în Fan Courier, dar în PHP o ia rău pe câmpii.

Pe de altă parte, cred că e modul PHP-ului de a te forța să nu scrii abominații d’alea :troll:

4 Likes

Uneori prefer să scriu cod mai compact :slight_smile:

Eu evit să folosesc astfel de condiții. De fapt, le folosesc doar dacă e vorba de o singură verificare, ceva simplu. Nu folosind o condiție într-alta. Devine ambiguă, greu de analizat etc.

Dacă un programator ar crea un script folosind numai sintaxe dintr-astea, următorul programator care ar intra în script ar înjura.

Iar referitor la cod, a doua evaluare mi se pare suficient de clară.

Prima evaluare, mi-e puțin rușine, dar vă spun… Singura valoare la care n-aș fi crezut că se poate ajunge este Urgent Curier.

Mi se pare lipsită de orice logică.

1 Like

Pare ca aici s-ar putea gasi explicatia
http://php.net/manual/ro/language.operators.precedence.php

Dupa cum spune si @anon80386878 si eu evit sa folosesc operatorul ternar. La a doua expresie, paranteza influenteaza evaluarea expresiei.

Din ce imi amintesc si gruparea folosind paranteze poate influenta rezultatul


Mie nu mi se pare deloc greu de citit un astfel de cod, ba dimpotrivă, mai ales dacă e grupat şi indentat corespunzător. Pot să fie şi 100 de condiţii înlănţuite, un cârnat de if-uri ar fi infinit mai greu de urmărit.

$descr = ($curier == "fan" ? "Fan Courier" :
         ($curier == "urgent" ? "Urgent Curier" :
         ($curier == "tnt" ? "TNT" :
         ($curier == "fedex" ? "Fedex" :
         ($curier == "dhl" ? "DHL" :
         "")))));

E complicat de lucrat cu un astfel de cod.
Mai practic in acest caz e un array unde accesezi direct cheia pe care o cauti si obtii valoarea.

Operatorul ternar e folosit indeosebi la initializari, nu la lookups.

6 Likes

Cred ca poti scrie ce este mai sus cu un switch case.

Și eu aș alege tot un array:

$curieri = [
  'fan' => 'Fan Curier',
  'urgent' => 'Urgent Curier',
  'tnt' => 'TNT Curier',
];
$descr = $curieri[ $curier ] ?? '';

Mi se pare mai elegant, mai ușor de citit, mai curat iar efortul necesar pentru a parsa mental codul este mult mai mic.

Eventual ai putea folosi un switch, dar ajungi să ai un cârnat de cod care va arăta și mai urât decât ternary-ul tău (dar tot va fi mai ușor de citit).

9 Likes

Hehe, evident că exemplul meu este unul extrem, ca să arăt că nu e aşa dificil de “parsat” ochiometric o astfel de construcţie, dacă e scrisă cum trebuie. Pentru lookups e clar că map-ul e mult mai potrivit.

Ideea e că uneori ai “bigger fish to fry” şi vrei să scrii codul neimportant cât mai compact (eventual pe o singură linie), ca să nu-ţi stea în cale în înţelegerea lucrurilor cu adevărat importante. Cel puţin aşa am remarcat eu în practică, dacă fac “unfold” la cod doar ca să fiu pedant, codul meu începe să arate mult mai complex decăt este în realitate.

1 Like

Şi cu if şi cu switch/case, codul devine incredibil de verbose, în loc să uşurezi înţelegerea codului, îl faci incomprehensibil :slight_smile:

if($curier == "fan")
	$descr = "Fan Courier"
elseif($curier == "urgent")
	$descr = "Urgent Curier"
elseif($curier == "tnt")
	$descr = "TNT";
elseif($curier == "fedex")
	$descr = "Fedex";
elseif($curier == "dhl")
	$descr = "DHL";
else
	$descr = "";
switch($curier)
{
	case "fan":
		$descr = "Fan Courier";
		break;
	case "urgent":
		$descr = "Urgent Courier";
		break;
	case "tnt":
		$descr = "TNT";
		break;
	case "fedex":
		$descr = "Fedex";
		break;
	case "dhl":
		$descr = "DHL";
		break;
	default:
		$descr = "";
}

In mod normal “Evaluare 1” ar fi trebuit sa dea eroare, “Evaluare 2” este modul de lucru corect.

Grija mare.
In php switch face verificare de tip (==) si nu de tip (===) ca intr-un if.
Ai putea sa ai ceva de genul

switch($curier)
{
	case "fan":
		$descr = "Fan Courier";
		break;
	default:
		$descr = "";
}

Daca din intamplare $curier = true;
atunci $descr = "Fan Courier";

Dacă chiar există şansa de a primi altceva decât string şi vrei ca în cazul ăsta să intre pe branch-ul default, faci casting:

switch((string)$curier)
{
	case "fan":
		$descr = "Fan Courier";
		break;
	default:
		$descr = "";
}

Exista un mod de a evalua diverse conditii mai readable. Nu recomand folosirea decat in cazuri in care orice alta metoda de a scrie cod usor de parcurs a dat gres.

$someVariable = '';
$anotherVariable = ['abc', 'def', 'g'];

switch (true) {
    case (!  is_string($someVariable) || ! is_array($anotherVariable)):
        echo ('type check failed!');
        break;
    case (0 == strlen($someVariable) && 0 == count($anotherVariable)):
        echo('we are dealing with empty values!');
        break;
    // etc 
}
1 Like

deci, de la un bug s-a ajuns la o discutie despre cum sa scrii switch-uri…

1 Like

Pe de alta parte discutia este utila. Cel putin pt mine.

te contrazic, nu era bug, era o folosire incorecta a operatorului ternar

switchurile sunt una din abordarile ce se pot folosi in cazul descris initial, de ce ti se pare ca nu e util sa le descriem ca alternativa?

Să zicem că este un unexpected behaviour, în special dacă vii din lumile altor limbaje. Îmi scapă motivele pentru care implementatorii PHP-ul s-au gândit că ar fi o idee genială să facă evaluarea operatorilor ternari într-un mod complet contraintuitiv. E un feature cam bizar :slight_smile:

Acum mulţi ani (probabil pe vremea lui php 3 sau 4) am senzaţia că m-am lovit de un alt feature bizar, tot legat de operatorul ternar. Era ceva de genul:

function test()
{
    return true ? 1 : 2;
}

Iar test(), in loc sa returneze “1” sau “2”, in funcţie de condiţie, îmi returna rezultatul condiţiei în sine (!!!), “true” în cazul asta. De atunci m-am obişnuit să incadrez mereu între paranteze tipul astă de expresie.

function test()
{
    return (true ? 1 : 2);
}
1 Like

ok, sa zicem ca nu-i bug.

dar de ce-ar fi ok sa scriu switch-ul ala?

cu if/elseif ce are? cum e mai greu de citit?

if($cond){

}elseif($cond){

}elseif($cond){

} ....
else{
}

in rest imi pastrez parerea. omul se mira de un comportament la care nu se astepta si voi sariti sa-i dati niste solutii de care nu are nevoie.