pronto.ee

Tomorrow will be cancelled due to lack of interest

PHP8: tüübimaagia

PHP

PHP8 tõi sisse hulgaliselt täiendusi tüüpidega majandamisesse ning PHP8.1 lisas sinna omakorda peale täiendava kihi. Selles postituses keskendume me siiski vaid PHP8 muudatustel.

PHP on n.ö. nõrkade tüüpidega (weak typing) keel, mis praktikast tähendab seda, et erinevaid tüüpe on võimalik võrrelda ja nende väärtuseid teineteisele omistada; selleks vajalik tüüpide kantimine (type coercion) toimub automaatselt. Teinekord paraku soovimatute kõrvalnähtudega. PHP8 üritab jõudumööda neid soovimatuid kõrvaltoimed kas siis vältida või vähemalt pakub võimaluse neid kontrolli all hoida.

Tüüpide võrdlemine

Klassikaline erinevus PHP7 ja PHP8 vahel ilmneb järgnevas võrdluses:

if ('something' == 0) {
    echo 'true';
} else {
    echo 'false';
}

Nii 'something' kui 0 võivad olla muutujad; PHP7 puhul on vastuseks true, PHP8 puhul false. Asi on selles, et kui võrdluses on int ja string, siis proovitakse stringist teha int. Kuna tegemist on mittenumbrilise tekstiga, siis PHP7 pani selle väärtuseks 0 sellal kui PHP8 jaoks väärtus puudub ja sellest ka erinev tulemus. Samasuguse tulemuse annab näites järgnev lõik:

if ('42something' == 42) {
    echo 'true';
} else {
    echo 'false';
}

Selline tüüpide kantimine on teinekord väga mugav, näiteks XML failide töötlemisel, kuid sisaldab endas riske ja tänu sellele võib juhtuda, et kood mis toimis PHP7-s ei tööta enam PHP8-s. Märkuseks nii paljud, et:

if ('42' == 42) {
    echo 'true';
} else {
    echo 'false;
}

… on true vaatamata PHP versioonile, kuna ’42’ on string mida on võimalik üheselt intiks kantida.

Muudatus tüüpide kantimises torkab silma ka siis kui kasutada < ja > võrdluseid. Kui varem kanditi string intiks ja seejärel võrreldi, siis nüüd kui stringi ei ole võimalik konvertida on number alati väiksem kui string. Need nüansid laienevad muudelegi võrdlustele, näiteks in_array käitub nüüd pisut erinevalt.

Ma siiski tooks enne järgmise näite juurde minekut eraldi välja erisuse kui kasutada switchi.

switch(0) {
    case 'a' : print "case A\n"; break;
    case 0   : print "case 0\n"; break;
}
 
switch('a') {
    case 'a' : print "case A\n"; break;
    case 0   : print "case 0\n"; break;
}
 
switch(0) {
    case 0   : print "case 0\n"; break;
    case 'a' : print "case A\n"; break;
}
 
switch('a') {
    case 0   : print "case 0\n"; break;
    case 'a' : print "case A\n"; break;
}

Annab PHP7 puhul tulemuseks

case A
case A
case 0
case 0

ja PHP 8 puhul

case 0
case A
case 0
case A

Mis on eelnevat arvestades igati loogiline ja enamasti ka soovitud tulemus. Ehk siis nõrkade tüüpidega manipuleerides võib juhtuda, et kood käitub pisut teisi.

Unionid

PHP7 tõi keelde funktsioonide pöördumistüübi ning PHP7.1 nullitavad tüübid (nullable types). Et vältida soovimatut tüüpide kantimist ning aidata IDE-si soovituste andmisel on funktsioonidel võimalik anda tüüp nii sisendi kui väljundi jaoks. Nullitavad tüübid (algavad ? märgiga) näitavad, et tüüp võib (näiteks kui vastus ei leita) olla ka null. Näide:

public function doStuff(int $i): ?string
{
    // stuff
}

Aga mis siis kui sisend (või väljund) võib olla rohkem kui ühte tüüpi? Iseenesest pole midagi valesti selles kui muutujaid kanditakse enne või pärast funktsiooni välja kutsumist kuid see on tülikas, lisab ballasti ja ei aita loetavusele kaasa. Sestap tõi PHP8 sisse unionid. Näide:

public function add(int|float $a, int|float $b): int|float
{
    return $a + $b;
}

Märkuseks niipalju, et null ei tegelikult tüüp, vaid tüübi puudumine ja sestap ei ole string|null endiselt korrektne konstruktsioon ja selle asemel tuleks kasutada ?string tüüpi.

mixed tüüp

Teinekord on vaja lihtsalt konstruktsiooni kus lubatud on kõik tüübid KAASA ARVATUD null. Selleks puhuks tõi PHP8 sisse pseudotüübi nimega mixed. Näiteks:

public function doStuff(mixed $input): bool
{
    // stuff
}

Muutujad mille tüüp pole veel selge tüüp on samuti mixed. Kuna mixed sisaldab kõiki tüüpe ei oma int|mixed ja ?mixed mingit tähendust ning annavad seetõttu vea. Samal põhjusel ei eksisteeri is_mixed funktsiooni ning samuti ei ole võimalik teha $b = (mixed)$a.

Üks asi veel: alatest PHP8-st ei ole enam võimalik deklareerida klassi nimega mixed. Kuni sinnamaani oli see teoreetiliselt võimalik.

Post a comment