joi, 25 octombrie 2012

Conversia tipurilor şi utilizarea operatorilor de atribuire (=) şi egalitate (==, ===) în PHP

        Utilizarea corectă a operatorilor uzuali este una dintre cele mai de bază cerinţe în învăţarea oricărui limbaj de programare. Cele mai multe limbaje de programare actuale (ex. Java, C++ etc.) impun reguli stricte de declararare a tipului unei variabile înainte de iniţializarea şi folosirea sa. Limbajul PHP nu face parte din această categorie – se spune că este loosely typed - iar conversiile implicite de tipuri pot fi puţin derutante pentru cei neobişnuiţi cu astfel de operaţiuni. Cum o bună stăpânire a acestor concepte este indispensabilă pentru înţelegerea limbajului, am socotit nimerit să prezint, în cele ce urmează, câteva consideraţii elementare despre conversia tipurilor în PHP şi utilizarea operatorilor de atribuire (=), egalitate lejeră (==), şi respectiv, de egalitate strictă sau identitate (===), lăsând deoparte, totuşi, operatorii de cast explicit, asupra cărora, poate, voi reveni.

Operatorul de atribuire (asignare) (=) are forma:
$var = expresie;
        Se evaluează expresia din dreapta operatorului, atât ca tip, cât şi ca valoare, iar rezultatul este atribuit variabilei din stânga. Spre deosebire alţi operatori, dacă variabila din stânga nu există, operatorul de atribuire o creează fără a emite niciun avertisment. Returnează valoarea atribuită variabilei $var, şi asociază de la dreapta la stânga, ceea ce înseamnă că putem scrie atribuiri multiple, ca în exemplul de mai jos:
<?php
$a = 2 + 3; //$a primeşte valoarea întreagă 5
echo $a."<br/>";
$b = $a = 7; //$a  primeşte valoarea  7, apoi aceasta valoare 
//este atribuită şi lui $b
echo $a.' '.$b."<br/>"; 
$b = 5 + $a = 3; //$a primeşte valoarea întreagă 3, 
//iar $b primeşte valoarea întreagă  8
echo $a.' '.$b."<br/>"; 
?>
        Trebuie reţinut, aşadar, că operatorul de asignare nu realizează o comparaţie şi, ca orice alt operator, returnează un rezultat. Această informaţie banală este adesea ignorată de programatorii începători, care se bazează pe avertismentele de eroare emise de sistem. Să considerăm următorul exemplu:
<?php
$a = 'ceva';
if ($a = 'altceva'){
    echo 'Ce s-o fi întâmplat?
} else {
    echo '$a este ceva';
}
?>
        Rezultatul va fi că sistemul va afişa textul Ce s-o fi întâmplat?. Motivul este următorul: în antetul instrucţiunii if nu s-a realizat, de fapt, nicio comparaţie, ci o atribuire, de fapt o suprascriere a variabilei $a, care conţine acum 'altceva', indiferent de valoarea ei anterioară. Conform regulilor de conversie a tipurilor, rezultatul returnat de operatorul de atribuire (care este tot 'altceva') este evaluat la true, ceea ce conduce la afişarea textului menţionat.
        Să considerăm acum un alt exemplu:
<?php
$a = 1;
while ($a = 1) {
    echo '$a = '.$a."<br/>";
    $a++;
}
?>
        În acest caz, deoarece, în ciuda incrementării variabilei $a în interiorul buclei while, condiţia de ieşire din ciclu nu va fi îndeplinită niciodată, fiindce în antetul instrucţiunii while nu se face o comparaţie, ci o atribuire care va returna întotdeauna 1, adică true.

        Operatorii de egalitate lejeră (==) şi, respectiv, de egalitate strictă (===) returnează, ambii, un rezultat logic, adică true sau false. Deosebirea este că operatorul == ţine cont doar de conţinutul variabilelor comparate, ingnorând tipul lor – adică, de fapt, face conversia tipurilor -, operatorul === compară atât conţinutul variabilelor, cât şi tipul lor.
        Operatorul de egalitate lejeră funcţionează ţinând cont de următoarele reguli:
  • Din punct de vedere logic, orice număr întreg sau real, cu excepţia lui 0, orice string nevid, cu excepţia stringului "0", orice array nevid (care are măcar o componentă, indiferent ce fel de componentă, şi indiferent de valoare) sunt true. În schimb, sunt false: numărul 0, stringul "0" şi cel vid (""), array-urile vide (array()) şi null-urile.
  • Privite ca numere, true e echivalent cu 1, iar false cu 0.
  • Privite ca stringuri, true e echivalent cu "1", iar false cu "0".
  • Văzut ca număr, orice string este egal cu numărul care se poate forma cu cel mai mare număr din primele sale caractere, iar dacă acest număr este zero, atunci stringul este 0.De exemplu, "1001 dalmaţieni" este echivalent cu 1001, "-2e3p07MN" este echivalent cu -2*103 = -2000, iar "abracadabra" este echivalent cu 0. Atenţie, deci, la aceste echivalenţe, care nu sunt tranzitive – convertit în număr, "abracadabra" este 0, şi convertit în boolean, este false, pe câtă vreme, convertit direct în boolean, este true – şi, în mod evident, nici reflexive – privit ca boolean, "abracadabra" e true, dar true, văzut ca string, e "1".
        Aceste reguli sunt evidenţiate în tabelul de mai jos, unde sunt notate cu asterisc (*) comparaţiile care returnează true.


bool
true
bool
false
int
1
int
0
int
-1
string
"1"
string
"0"
string
"-1"
NULL array()

string
"string"
string
""
true *
*
* *
*

*
false
*
*

*
* *
*
1 *
*

*





0
*
*







-1 *


*






"1" *
*

*





"0"
*



*




"-1" *





*



NULL
*





* *
*
array()
*





* *

"string" *








*
""
*





*

*

         Din tabelul de mai sus rezultă, cred, destul de clar, diferenţele dintre operatorul de egalitate lejeră şi cel de egalitate strictă, astfel încât nu voi da niciun exemplu cu privire la modul în care înlocuirea unui operator cu celălalt poate compromite logica unui program. Voi evidenţia, totuşi, modul în care se pot realiza câteva conversii simple, cât şi comportarea unor functii uzuale, precum isset(), is_type() (unde type e unul dintre cele 8 tipuri: integer, float, boolean, string, array, object, resursă şi null), şi settype(). Pentru determinarea tipului unei variabile se poate folosi, de asemenea, şi funcţia gettype(), dar ea este considerată învechită (deprecate) şi, probabil, va fi eliminată în viitoarele versiuni de PHP.
<?php
$ceva;
if (isset($ceva)) { 
    echo "Variabila $ceva este setată.<br/>";
} else {
    echo "Variabila \$ceva nu este setată.<br/>";
}
$ceva = NULL;
if (isset($ceva)) { 
    echo "Variabila $ceva este setată.<br/>";
} else {
    echo "Variabila $ceva nu este setată.<br/>";
}
$ceva = '1001 de nopţi';
if (is_string($ceva)) { 
    echo "Variabila $ceva este un string.<br/>";
} else {
    echo "Variabila $ceva nu este un string.<br/>";
}
$altceva = 2.5;
if (is_numeric($altceva)) { 
    echo "Variabila $altceva este un număr.<br/>";
} else {
    echo "Variabila $altceva nu este un număr.<br/>";
}
if (is_int($altceva)) { 
    echo "Mai precis, un număr întreg.<br/>";
} 
if (is_float($altceva)) { 
    echo "Mai precis, un număr real.<br/>";
} 
$ceva = $ceva + $ceva;
if (is_numeric($ceva)) { 
    echo "Variabila $ceva este un număr.<br/>";
} else {
    echo "Variabila $ceva nu este un număr.<br/>";
}
$ceva = $ceva.$ceva;
if (is_string($ceva)) { 
    echo "Variabila $ceva este un string.<br/>";
} else {
    echo "Variabila $ceva nu este un string.<br/>";
}
settype($ceva, 'bool');
if (is_bool($ceva)) { 
    echo "Variabila \$ceva este acum booleană şi are valoarea $ceva.<br/>";
} else {
    echo "Variabila $ceva nu booleană.<br/>";
}
$altceva = $altceva - $ceva;
echo "Variabila \$altceva are acum valoarea $altceva.<br/>";
if ($altceva) 
    $ceva = "101 dalmaţieni";
echo "Acum, \$ceva este $ceva<br/>";
$ceva += (int)$altceva;
echo "Acum, \$ceva este $ceva<br/>";
echo "În timp ce \$altceva a rămas $altceva.";
?>
Ieşirea programului este următoarea:
Variabila $ceva nu este setată.
Variabila nu este setată.
Variabila 1001 de nopţi este un string.
Variabila 2.5 este un număr.
Mai precis, un număr real.
Variabila 2002 este un număr.
Variabila 20022002 este un string.
Variabila $ceva este acum booleană şi are valoarea 1.
Variabila $altceva are acum valoarea 1.5.
Acum, $ceva este 101 dalmaţieni
Acum, $ceva este 102
În timp ce $altceva a rămas 1.5.

Niciun comentariu:

Trimiteți un comentariu