Trumpai: PHP yra dinamiškai tipizuota: reikšmės tipas paaiškėja tik vykdymo metu, o be papildomų nurodymų PHP prireikus reikšmes konvertuoja automatiškai. Tačiau nuo PHP 7 parametrams, grąžinamoms reikšmėms ir savybėms galima priskirti tipus, o declare(strict_types=1) patikrą padaro griežtą. Kas žino taisykles ir naudoja griežtus palyginimus su ===, išvengia ištisos loginių ir saugumo klaidų klasės.
Dinamiškai tipizuota, bet ne be tipų
PHP rašant $preis = 19.90; tipo nurodyti nereikia. PHP pati atpažįsta, kad tai slankiojo kablelio skaičius. Jei reikšmė vėliau sujungiama su eilute, PHP ją konvertuoja. Toks elgesys vadinamas Type Juggling, t. y. automatiniu tipų konvertavimu. Jis palengvina pradžią, bet gali duoti netikėtų rezultatų.
Sprendimų priėmėjams tai svarbu, nes daug senesnių PHP programų parašyta be tipų nurodymų. Tokį kodą sunkiau tikrinti ir modernizuoti. Naujesni projektai tipus naudoja nuosekliai, todėl klaidos išryškėja anksčiau, o statinė analizė su įrankiais, tokiais kaip PHPStan ar Psalm, tik tada tampa tikrai veiksminga.
PHP tipų apžvalga
PHP vadovas nurodo devynis vidinius tipus, kuriuos gali turėti bet kuri išraiška. Paprastai jie skirstomi į tris grupes:
| Grupė | Tipai | Pavyzdys |
|---|---|---|
| Skaliariniai tipai | bool, int, float, string |
true, 42, 3.5, "Text" |
| Sudėtiniai tipai | array, object, callable |
[1, 2], new DateTime() |
| Ypatingi tipai | null, resource |
null, atidarytas failas |
Tipų patikrai vadovas rekomenduoja is_*() funkcijas, pavyzdžiui, is_int(). Tyrinėjimui tinka get_debug_type(), kuri nuo PHP 8.0.0 grąžina įprastus tipų pavadinimus, o senesnė gettype() grąžina, pavyzdžiui, "integer" ir "double".
Be automatinio konvertavimo yra ir aiškus: su tipo keitimu (cast), pavyzdžiui, (int) $wert, arba su settype() kodas pats nustato, kokio tipo turi būti reikšmė. Tai įprasta formų įvesčiai, nes viskas, kas ateina per $_GET ar $_POST, iš pradžių yra eilutė arba masyvas. Tačiau cast netikrina, ar reikšmė buvo prasminga: iš "abc" tyliai tampa 0. Todėl įvestims dažniausiai geresnis pasirinkimas yra tikra patikra su filter_var().
Klasės ir sąsajos PHP taip pat yra tipai. Funkcija gali reikalauti, kad parametras būtų tam tikros klasės objektas arba įgyvendintų tam tikrą sąsają. Kaip kartu veikia klasės, sąsajos ir enum, paaiškinta puslapyje Objektinis programavimas PHP.
Tipų deklaracijos: nuo PHP 7.1 iki 8.3
Tipų deklaracijos nustato, kokias reikšmes funkcija priima ir grąžina. Tipų sistema augo per daugelį versijų. Pasak vadovo pakeitimų žurnalo, be kita ko, atsirado šios galimybės:
| Versija | Naujiena |
|---|---|
| 7.1.0 | nullable tipai (?int), void, iterable |
| 7.2.0 | object |
| 7.4.0 | tipizuotos klasės savybės |
| 8.0.0 | sąjungos tipai (keli leidžiami tipai viename nurodyme), mixed, grąžinamas tipas static |
| 8.1.0 | sankirtos tipai (A&B), grąžinamas tipas never |
| 8.2.0 | DNF tipai, literalo tipas true, null ir false kaip savarankiški tipai |
| 8.3.0 | tipizuotos klasių, sąsajų, trait ir enum konstantos |
DNF reiškia disjunktyviąją normalinę formą ir leidžia deriniais, pavyzdžiui, (A&B)|null. Kas planuoja PHP versijas, iš šios lentelės mato ir tai, kokių kalbos priemonių reikia programai.
strict_types: konvertavimas arba TypeError
Be papildomo nurodymo PHP veikia konvertavimo režimu. Funkcija su int parametrais tuomet priima ir 1.5 ir paverčia jį 1; nuo PHP 8.1 toks nuostolingas konvertavimas pažymėtas kaip pasenęs. Su declare(strict_types=1) failo pradžioje PHP vietoj to išmeta TypeError.
<?php
declare(strict_types=1);
function summe(int $a, int $b): int
{
return $a + $b;
}
echo summe(1, 2); // 3
echo summe(1.5, 2.5); // TypeError, vietoj tylaus 3
Svarbūs du niuansai. Pirma, strict_types galioja iškvietimams iš failo, kuriame jis nurodytas, o ne ten apibrėžtoms funkcijoms. Jei failas be strict_types iškviečia tą pačią funkciją, konvertuojama. Antra, sveikasis skaičius ten, kur tikimasi slankiojo kablelio skaičiaus, leidžiamas ir griežtame režime.
Laisvi ir griežti palyginimai
Operatorius == lygina po tipų konvertavimo, === lygina reikšmę ir tipą be konvertavimo. Vadovas daugeliu atvejų rekomenduoja === ir !==, nes konvertavimo taisyklės ne visada akivaizdžios.
<?php
var_dump(0 == "a"); // PHP 7: true, PHP 8: false
var_dump("1" == "01"); // true, abi yra skaitinės eilutės
var_dump("1" === "01");// false, tas pats tipas, bet kita eilutė
var_dump(1 == true); // true
var_dump(1 === true); // false
Su PHP 8.0 palyginimai tarp skaičių ir neskaitinių eilučių tapo prasmingesni. Senesnių programų elgsena po atnaujinimo gali pasikeisti, pavyzdžiui, switch blokuose. Taip pat su PHP 8.0 įvesta match išraiška visada lygina griežtai. Kaip laisvi palyginimai sukelia saugumo spragas, pavyzdžiui, tikrinant žetonus ar slaptažodžius, aprašo straipsnis Type Juggling: laisvi palyginimai kaip saugumo rizika.
Tipai, atnaujinimai ir PHP2Shell
Griežta tipų sistema daro programos kodą patikimesnį, bet neapsaugo nuo klaidų paties interpretatoriaus viduje. PHP2Shell spragos CVE-2026-109618 ir CVE-2026-109619 slypi PHP atminties ir užklausų valdyme. Ištaisytos PHP 8.5.12, 8.4.27, 8.3.36 ir 8.2.35; po to visi PHP procesai turi būti paleisti iš naujo.
Tipų sistema vis dėlto vaidina vaidmenį, būtent atnaujinant. Programos, įstrigusios ties PHP 8.1 ar senesne versija, iš PHP projekto oficialių atnaujinimų nebegauna. Gerai tipizuotą kodą gerokai lengviau perkelti į palaikomą versiją, nes įrankiai nesuderinamumus randa anksti. Senesniems projektams padeda mūsų perkėlimas į palaikomą PHP versiją.
Įjunkite strict_types palaipsniui naujuose ir perdarytuose failuose. Taip tipų saugumas auga, o senas kodas vienu metu nesugenda.
Kaip PHP SOS padeda konkrečiai?
- Tikriname, kokios minimalios PHP versijos jūsų programai reikia, ir lyginame su ištaisytomis versijomis.
- Paleidžiame kodo statinę analizę ir pažymime vietas su laisvais palyginimais saugumui svarbiose patikrose.
- Atnaujinimus planuojame taip, kad pasikeitusios palyginimo taisyklės būtų išbandytos iš anksto.
- Įdiegiame PHP atnaujinimą, paleidžiame iš naujo visus darbinius procesus (worker) ir dokumentuojame būseną.
Dažni klausimai
Ar PHP yra stipriai, ar silpnai tipizuota?
PHP yra dinamiškai tipizuota: kintamojo tipas paaiškėja tik vykdymo metu. Be strict_types PHP prireikus reikšmes konvertuoja. Su tipų deklaracijomis ir strict_types kodą tačiau galima padaryti labai griežtą.
Ką daro declare(strict_types=1)?
Jis išjungia automatinį skaliarinių reikšmių konvertavimą iškvietimams iš to failo. Jei reikšmė neatitinka deklaruoto tipo, PHP išmeta TypeError. Vienintelė išimtis yra sveikasis skaičius ten, kur tikimasi slankiojo kablelio skaičiaus.
Kodėl 0 == "a" PHP 8 yra neteisinga, o PHP 7 teisinga?
Iki PHP 7 neskaitinė eilutė lyginant su skaičiumi buvo paverčiama skaičiumi, iš „a“ tapdavo 0. Nuo PHP 8.0 šiuo atveju PHP skaitiniu būdu nebelygina, rezultatas yra false.
Ar tipai saugo nuo pačios PHP saugumo spragų?
Ne. Tipai apsaugo nuo klaidų jūsų programos kode. Interpretatoriaus spragas, tokias kaip PHP2Shell, uždaro tik PHP atnaujinimas. Tačiau šiuolaikinėms tipų galimybėms reikia naujų PHP versijų, todėl jos yra gera proga atnaujinti.