Variabile, tipuri, string-uri
Lectură estimată: 15 minute
Estimare pentru text, la un ritm de 180 de cuvinte pe minut. Timpul pentru analizarea codului, exerciții și proiecte se adaugă separat și depinde de ritmul tău.
De ce două valori care arată identic pot produce rezultate diferite
Într-o pagină, numărul 12 și textul „12” pot arăta la fel. În program, ele nu sunt însă același tip de valoare. O cantitate primită dintr-un formular pornește de obicei ca text, în timp ce o cantitate calculată intern poate fi un număr întreg. Dacă nu controlezi această diferență, poți accepta date invalide, poți compara greșit valori sau poți transforma un mesaj într-un calcul neașteptat.
În această lecție construim un mod de a gândi datele înainte să le afișăm. Vom separa valoarea brută, valoarea validată, valoarea folosită în calcul și textul prezentat utilizatorului. Exemplele folosesc un produs fictiv și o cantitate, fără reguli fiscale. Scopul este să poți explica fiecare conversie, nu să obții un rezultat numeric prin orice mijloc.
Poți rula exemplele în fișiere PHP din terminal. Pentru fragmentele care produc HTML, folosește serverul local din lecția anterioară și compară sursa răspunsului cu pagina. Rulează separat exemplele care arată intenționat o eroare, astfel încât o excepție să nu oprească toate experimentele următoare.
Variabila, valoarea și tipul
Numele unei variabile PHP începe cu $. Numele este sensibil la litere mari și mici: $pret și $Pret sunt două nume diferite. Valoarea are un tip, iar variabila poate primi ulterior altă valoare dacă programul nu impune un contract mai strict prin proprietăți sau parametri tipizați. Această flexibilitate este utilă, dar face disciplina numelor și a verificărilor importantă.
<?php
declare(strict_types=1);
$cantitate = 3;
$pretBani = 1250;
$activ = true;
$nume = 'Agendă';
$observatie = null;
var_dump($cantitate, $pretBani, $activ, $nume, $observatie);Observă tipul afișat de var_dump, nu doar valoarea. Int reprezintă un număr întreg, float un număr în virgulă mobilă, bool una dintre valorile true și false, iar string o secvență de octeți folosită frecvent pentru text. Null exprimă absența unei valori și nu este un tip scalar în aceeași categorie cu cele patru de mai sus. Array-urile vor primi o lecție separată.
Alege nume care includ semnificația și, când este util, unitatea. $pretBani este mai clar decât $valoare dacă suma se exprimă în bani întregi. $procentReducere nu este același lucru cu $factorReducere. O eroare de unitate poate trece prin toate verificările de tip: atât 25 de lei, cât și 25 de bani sunt numere, dar reprezintă sume diferite.
Absența, zero și șirul gol
Un câmp poate lipsi, poate avea null, poate avea zero sau poate avea șirul gol. Aceste situații nu trebuie tratate automat identic. Pentru un preț, zero poate însemna gratuit. Pentru o observație, șirul gol poate însemna că utilizatorul nu a scris nimic. Pentru un rezultat încă necalculat, null poate exprima absența rezultatului.
Operatorul ?? este util când vrei o alternativă pentru o valoare absentă sau null. Funcția isset verifică existența și faptul că valoarea nu este null. Dacă ai nevoie să distingi o cheie absentă de o cheie prezentă cu null într-un array, poți folosi array_key_exists. Alegerea depinde de contractul datelor, nu de preferința pentru o funcție mai scurtă.
Ai grijă cu empty când zero este o valoare validă. Ea grupează mai multe valori considerate goale, inclusiv șirul „0”. Pentru un câmp obligatoriu numeric, verificarea „nu este gol” nu este o validare completă. Scrie condițiile concrete: este un șir sau un număr în forma acceptată, poate fi convertit fără ambiguități și se află în intervalul permis.
Compararea strictă face intenția vizibilă
Operatorul === compară atât valoarea, cât și tipul. Operatorul == permite reguli de conversie între tipuri. Pentru multe verificări ale datelor și ale rezultatelor funcțiilor, comparația strictă este mai ușor de urmărit. Nu înseamnă că trebuie să memorezi fiecare conversie implicită; înseamnă că trebuie să eviți conversiile accidentale în locuri unde tipul are sens.
<?php
$dinFormular = '3';
$intern = 3;
var_dump($dinFormular === $intern); // false
var_dump((int) $dinFormular === $intern); // true pentru această intrareAl doilea rezultat nu dovedește că orice șir poate fi convertit sigur prin cast. Ai ales aici un șir valid. Dacă ai primit „3cutii”, conversia directă poate produce o parte numerică și poate ascunde restul textului. Validarea trebuie să decidă dacă întreaga intrare respectă formatul, înainte ca o conversie să îi schimbe sensul.
Unele funcții returnează false pentru eșec, dar pot returna și zero ca rezultat valid. În acele cazuri, o condiție de forma if (!$rezultat) poate confunda succesul cu eșecul. Citește contractul funcției și compară strict cu false când aceasta este valoarea documentată pentru eșec. Vom aplica această regulă la validarea numerelor și la citirea rândurilor din baza de date.
Validarea cantității înainte de conversie
Pentru laborator, cantitatea trebuie să fie un număr întreg între 1 și 100. Datele externe pot veni ca text, iar o cerere construită manual poate trimite chiar un array în loc de text. Înainte să folosești trim sau o funcție de validare, verifică forma intrării. O funcție care așteaptă string nu trebuie să primească necontrolat un array din formular.
<?php
declare(strict_types=1);
function cantitateValida(mixed $brut): int {
if (!is_string($brut)) {
throw new InvalidArgumentException('Cantitatea trebuie trimisă ca text');
}
$rezultat = filter_var(trim($brut), FILTER_VALIDATE_INT, [
'options' => ['min_range' => 1, 'max_range' => 100],
]);
if ($rezultat === false) {
throw new InvalidArgumentException('Cantitatea trebuie să fie între 1 și 100');
}
return $rezultat;
}
echo cantitateValida('3'); // 3Funcția are o graniță clară. Primește o valoare brută, respinge forma nepotrivită, validează întregul și returnează un int acceptat. După apel, codul de calcul nu mai trebuie să ghicească dacă „3cutii” înseamnă trei sau dacă o valoare negativă este permisă. Pentru intrări invalide, laboratorul folosește o excepție; într-un formular, vei transforma eroarea într-un mesaj asociat câmpului.
Testele utile sunt „1”, „100”, „0”, „101”, „3.5”, „abc” și un array. Primele două trec, celelalte sunt respinse conform regulii. Nu modifica regula doar pentru că o anumită conversie produce un număr. Dacă aplicația trebuie să accepte cantități fracționare, aceea este o altă cerință și trebuie definită împreună cu unitatea și precizia.
Calcule monetare și formatarea rezultatului
În laborator păstrăm prețul în bani întregi. Pentru trei produse a câte 1250 bani, totalul este 3750 bani. Abia la afișare împărțim la 100 și formatăm rezultatul. Această convenție evită amestecarea șirurilor de prezentare cu valorile de calcul și face exemplele ușor de verificat pe hârtie.
<?php
$pretBani = 1250;
$cantitate = 3;
$totalBani = $pretBani * $cantitate;
$totalAfisat = number_format($totalBani / 100, 2, ',', '.') . ' lei';
echo $totalAfisat; // 37,50 leiNu folosi $totalAfisat în calcule ulterioare. El conține virgulă, eventual separatori și textul monedei. Păstrează $totalBani pentru logică și folosește șirul doar la ieșire. Dacă un utilizator introduce un preț cu virgulă, ai nevoie de o regulă de parsare potrivită formatului acceptat; un cast float nu reprezintă o interpretare universală a formatelor locale.
Numerele întregi au limite, iar calculele foarte mari pot depăși domeniul lor. O aplicație reală trebuie să definească sumele maxime și să verifice rezultatele. Pentru valori financiare complexe, reprezentarea și rotunjirea se aleg la nivelul sistemului. Nu generaliza exemplul cu bani întregi la orice monedă, orice număr de zecimale și orice operație fără să verifici cerințele.
Ghilimele simple, duble și concatenare
Ghilimelele simple păstrează în mare parte textul literal, iar ghilimelele duble permit interpolarea variabilelor și anumite secvențe speciale. Concatenarea se face cu punct. Operatorul plus este pentru operații aritmetice, nu pentru lipirea textelor. Această diferență este importantă dacă ai lucrat anterior în alt limbaj.
<?php
$nume = 'Ana';
echo 'Salut, $nume'; // text literal cu numele variabilei
echo "Salut, {$nume}!"; // Salut, Ana!
echo 'Salut, ' . $nume . '!';Într-un mesaj lung, acoladele din interpolare pot face granița variabilei mai clară. Totuși, interpolarea nu validează și nu escapează pentru HTML. Dacă mesajul va fi introdus în pagină, pregătește ieșirea pentru acel context. Este util să construiești mai întâi un mesaj textual și să îl escapezi la afișare, în loc să amesteci reguli de calcul, text și marcaj într-o expresie greu de citit.
Heredoc poate face un text pe mai multe linii mai ușor de scris, dar nu schimbă aceste responsabilități. Faptul că șablonul arată ca HTML în fișierul PHP nu înseamnă că valorile interpolate sunt automat sigure. Pentru început, păstrează exemplele mici și explicite; confortul sintaxei nu trebuie să ascundă granița dintre date și structură.
Textul românesc și diferența dintre octeți și caractere
Un string PHP este o secvență de octeți. În UTF-8, unele caractere ocupă mai mult de un octet. De aceea strlen nu este o măsură universală a numărului de caractere pe care le vede utilizatorul. Pentru texte cu diacritice, funcții precum mb_strlen, disponibile prin extensia mbstring, pot număra punctele de cod potrivit codificării specificate.
Chiar și numărul punctelor de cod poate diferi de numărul simbolurilor percepute în texte cu combinații sau emoji. Pentru laboratorul nostru cu nume românești obișnuite, mb_strlen cu UTF-8 este o alegere mai potrivită decât strlen pentru limite de text. Pentru cerințe internaționale complexe, definește ce înseamnă „caracter” în produs și folosește instrumentul potrivit.
Aceeași atenție se aplică transformării în litere mari și mici. O funcție orientată doar către un set simplu de caractere poate să nu trateze toate literele românești cum te aștepți. Verifică extensia, codificarea și exemplele concrete. Nu construi un sistem complet de normalizare a numelor doar printr-un str_replace al spațiilor și presupuneri despre alfabet.
Normalizarea nu trebuie să distrugă informația
Trim elimină anumite caractere de la marginile unui șir și este util pentru un email sau un nume introdus cu spații accidentale. Nu trebuie aplicat fără discernământ oricărui câmp. Într-o parolă, spațiile pot face parte din valoarea aleasă. Într-un text formatat, spațiile și liniile pot avea semnificație. Regula de normalizare trebuie să aparțină câmpului, nu întregii cereri.
Nu transforma automat numele persoanelor în litere mici pentru stocare dacă ai nevoie să păstrezi forma introdusă. Poți construi o valoare separată pentru căutare sau comparare, dar păstrează scopurile distincte. În același mod, un slug este o reprezentare pentru un identificator lizibil în URL și nu este același lucru cu numele original.
Declarațiile de tip și limitele lui strict_types
O funcție care primește int și returnează int exprimă un contract util. Declare strict_types=1 influențează verificarea anumitor tipuri scalare la apelurile făcute din fișierul respectiv. Nu face ca toate variabilele să aibă tip fix și nu verifică domeniul comercial. Numărul -1 este tot int, chiar dacă este o cantitate interzisă.
Separă deci trei întrebări: valoarea are forma așteptată, are tipul intern potrivit și respectă regula aplicației? Validarea cantității rezolvă aceste întrebări înainte de calcul. Declarația de tip ajută funcția de calcul să își exprime așteptările, dar nu înlocuiește verificarea intervalului. Această separare va face funcțiile din lecția următoare mai ușor de reutilizat și de testat.
Studiu de caz: prețul se schimbă după afișare
Un program formatează 1234,50 lei pentru interfață și salvează acel șir în aceeași variabilă folosită pentru calcul. Mai târziu încearcă să adauge costul de livrare. Rezultatul este greșit sau apare o eroare deoarece variabila nu mai reprezintă suma numerică inițială. Problema a fost introdusă la granița dintre calcul și prezentare, nu în formula livrării.
Repararea începe prin nume și responsabilități separate: totalBani pentru calcul și totalAfisat pentru text. Verifică totalul numeric înainte de formatare și șirul după formatare. Apoi verifică faptul că operațiile ulterioare folosesc tot valoarea numerică. Dacă păstrezi aceste roluri clare, schimbarea separatorului zecimal sau a monedei afișate nu modifică aritmetica.
Exercițiu cu rezultate de control
Construiește un script care primește intern un nume, un preț în bani și o cantitate brută ca text. Validează cantitatea prin funcția de mai sus, calculează totalul și construiește un mesaj. Pentru numele „Agendă <A5>”, prețul 1250 și cantitatea „3”, rezultatul numeric trebuie să fie 3750, iar textul afișat să conțină 37,50 lei. Marcajul din nume trebuie să apară literal în HTML.
Schimbă apoi cantitatea în „3cutii”, „0” și un array. Niciuna nu trebuie să ajungă la calcul ca o cantitate acceptată. Păstrează separat un caz cu preț zero: dacă regula produsului permite gratuitatea, rezultatul este zero, nu o eroare produsă de empty. Scrie pentru fiecare caz valoarea brută, valoarea validată sau eroarea și rezultatul afișat.
O rezolvare bună poate explica de ce normalizarea, validarea, conversia și escaparea sunt pași diferiți. Dacă ai o singură funcție numită „curata” care pretinde că rezolvă totul, verifică ce face efectiv în fiecare context. În lecția despre formulare vom folosi aceste distincții pe date trimise prin HTTP, unde utilizatorul poate ocoli complet interfața ta.
Laborator suplimentar: trei etape pentru aceeași cantitate
Păstrează funcția cantitateValida din lecție într-un fișier separat, fără apelul demonstrativ de la final. În scriptul de lucru, include acel fișier și construiește un rezumat pentru un produs. Scopul este să urmărești separat intrarea brută, cantitatea acceptată și mesajul afișat. Dacă ai o eroare de validare, nu construi un total folosind o valoare implicită aleasă pentru a evita oprirea programului.
Pentru primul caz, produsul costă 1250 bani, iar cantitatea primită este textul „3”. După validare ai întregul 3, după calcul ai întregul 3750, iar pentru afișare ai textul „37,50 lei”. Aceste rezultate seamănă între ele, dar fiecare aparține unei etape diferite. Schimbarea separatorului zecimal din afișare nu trebuie să afecteze primele două etape.
<?php
declare(strict_types=1);
require __DIR__ . '/cantitate.php';
$nume = 'Agendă <A5>';
$pretBani = 1250;
$cantitateBruta = '3';
try {
$cantitate = cantitateValida($cantitateBruta);
$totalBani = $pretBani * $cantitate;
$mesaj = $nume . ': ' . number_format($totalBani / 100, 2, ',', '.') . ' lei';
echo htmlspecialchars($mesaj, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
} catch (InvalidArgumentException $e) {
echo htmlspecialchars($e->getMessage(), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}Acest script folosește un preț intern fix și mic. Dacă prețul ar proveni dintr-o sursă externă, ar avea nevoie de propriul contract și de verificarea domeniului înainte de înmulțire. Nu interpreta validarea cantității ca pe o validare a întregii comenzi. Fiecare intrare trebuie evaluată potrivit rolului ei, iar faptul că o valoare este int nu dovedește că aparține domeniului permis.
De ce testul cu zero trebuie scris de două ori
În scenariul nostru, cantitatea zero este interzisă, dar prețul zero poate reprezenta un produs gratuit. Prin urmare, scriem două teste distincte. Primul trimite cantitatea „0” și așteaptă eroare. Al doilea folosește prețul 0 și cantitatea „3” și așteaptă totalul 0. Dacă aceeași verificare empty respinge ambele situații, implementarea a confundat două reguli diferite.
Această observație este utilă dincolo de bani. Vârsta zero poate fi validă pentru un copil, în timp ce numărul de participanți zero poate împiedica începerea unui atelier. Un text „0” poate fi un cod acceptat, iar șirul gol poate indica lipsa codului. Valoarea nu are sens comercial izolată de câmpul în care apare. Numele și regulile câmpurilor trebuie să păstreze acest context.
Experimentează cu spații și cu formatul întregului
Funcția normalizează marginile prin trim, deci „ 3 ” este acceptat ca 3. Nu elimină spații din interior și nu ghicește ce înseamnă „3 cutii”. Filtrul ales are propriile reguli pentru reprezentarea unui întreg. Dacă produsul trebuie să accepte un format diferit, precum zerouri inițiale în anumite coduri, nu reutiliza automat validatorul de cantități pentru acel câmp.
Încearcă intrările „1”, „100”, „101”, „3.5”, „3cutii”, șirul gol și un array. Pentru fiecare, notează mai întâi predicția, apoi rezultatul. Când predicția diferă de rezultat, verifică dacă problema este în cerință, în înțelegerea filtrului sau în cod. Nu schimba testul doar pentru a transforma rezultatul curent într-un succes. Un test este util tocmai când îți contrazice presupunerea.
Observă diferența dintre afișare și diagnostic
Echo poate afișa true ca 1 și poate face unele valori să pară identice. Var_dump arată și tipul, deci este mai potrivit pentru experimentele din terminal. Nu lăsa însă diagnosticarea completă a intrărilor într-o pagină publică: un array primit de la utilizator poate conține informații pe care nu vrei să le expui. Pentru laborator folosește date fictive și scoate diagnosticul din versiunea finală a paginii.
Compară strlen și mb_strlen pentru textul „țară”, specificând UTF-8 în a doua funcție. Prima numără octeții, a doua numără punctele de cod. Dacă editorul salvează în altă codificare decât cea presupusă, rezultatul nu mai verifică experimentul dorit. Notează codificarea fișierului și cerința extensiei mbstring, ca altcineva să poată reproduce observația în propriul mediu.
Corectura unei soluții aparent funcționale
O soluție greșită transformă imediat orice intrare în int, calculează totalul, apoi afișează rezultatul. Pentru „3” pare corectă. Pentru „3cutii” poate produce tot trei și ascunde faptul că formatul nu era acceptat. Corectura nu constă în adăugarea unui mesaj după calcul, ci în mutarea validării înaintea conversiei folosite de logica internă. Datele respinse nu trebuie să ajungă la calcul.
Predarea laboratorului include un caz valid, două limite, două forme invalide și cazul produsului gratuit. Pentru fiecare caz valid, arată tipul cantității și totalul înainte de formatare. Pentru fiecare caz invalid, arată că nu a fost construit un total. Astfel demonstrezi un flux de date controlat, nu doar o propoziție care arată bine pentru un singur exemplu favorabil.
Documentație oficială pentru aprofundare
Referințe verificate pentru această ediție. Exemplele sunt exerciții originale de laborator.
Ți-a plăcut lecția? Continuă cursul complet
Ai citit o lecție gratuită. Celelalte lecții, aplicațiile practice și materialele se deblochează odată cu cursul.
99 lei
Plată unică, acces pe viață la toate cele 7 lecții.
Vezi ce conține cursul →