# PHP:n tyyppijärjestelmä

> PHP:n tyyppijärjestelmä selitettynä: tietotyypit, tyyppimääritykset versioista PHP 7.1–8.3, strict_types ja löysät vertailut esimerkein sekä tietoturvayhteys.

Lähde: https://phpsos.de/fi/php/typsystem/  
Tilanne: 25.10.2026  
Palveluntarjoaja: PHP SOS, PHP-hätäapu ja palvelimen tietoturva. Pikaapu WhatsAppilla: https://wa.me/491736494914?text=Hei%20PHP%20SOS%2C%20tarvitsemme%20apua%20PHP-palvelimemme%20kanssa.%20Yritys%3A%20%E2%80%A6%20Verkkosivusto%3A%20%E2%80%A6

**Lyhyesti:** PHP on dynaamisesti tyypitetty: arvon tyyppi selviää vasta ajon aikana, ja ilman lisämääräyksiä PHP muuntaa arvoja tarvittaessa automaattisesti. Versiosta PHP 7 lähtien parametreille, paluuarvoille ja ominaisuuksille voi kuitenkin määrittää tyypit, ja `declare(strict_types=1)` tekee tarkistuksesta tiukan. Kun säännöt tuntee ja käyttää tiukkoja vertailuja `===`, vältyy kokonaiselta luokalta logiikka- ja tietoturvavirheitä.

## Dynaamisesti tyypitetty, mutta ei tyypitön

PHP:ssä lauseessa `$preis = 19.90;` ei tarvitse ilmoittaa tyyppiä. PHP tunnistaa, että kyseessä on liukuluku. Jos arvo myöhemmin yhdistetään merkkijonoon, PHP muuntaa sen. Tätä toimintaa kutsutaan nimellä type juggling eli automaattinen tyypinmuunnos. Se tekee aloittamisesta helppoa mutta voi johtaa yllättäviin tuloksiin.

Päättäjälle tällä on merkitystä, koska monet vanhemmat PHP-sovellukset on kirjoitettu ilman tyyppimerkintöjä. Sellaista koodia on vaikeampi tarkastaa ja modernisoida. Uudemmat hankkeet käyttävät tyyppejä johdonmukaisesti, jolloin virheet tulevat näkyviin aiemmin ja [staattinen analyysi](https://phpsos.de/fi/php/statische-analyse/) työkaluilla kuten PHPStan tai Psalm pääsee kunnolla oikeuksiinsa.

## PHP:n tyypit pähkinänkuoressa

PHP-käsikirja mainitsee yhdeksän sisäistä tyyppiä, jotka millä tahansa lausekkeella voi olla. Ne jaetaan yleensä kolmeen ryhmään:

| Ryhmä | Tyypit | Esimerkki |
|---|---|---|
| Skalaarityypit | `bool`, `int`, `float`, `string` | `true`, `42`, `3.5`, `"Text"` |
| Yhdistetyt tyypit | `array`, `object`, `callable` | `[1, 2]`, `new DateTime()` |
| Erikoistyypit | `null`, `resource` | `null`, avattu tiedosto |

Tyyppitarkistuksiin käsikirja suosittelee `is_*()`-funktioita, kuten `is_int()`. Tutkimiseen sopii `get_debug_type()`, joka palauttaa yleiset tyyppinimet versiosta PHP 8.0.0 lähtien, kun taas vanhempi `gettype()` palauttaa esimerkiksi `"integer"` ja `"double"`.

Automaattisen muunnoksen rinnalla on nimenomainen muunnos: castilla kuten `(int) $wert` tai funktiolla `settype()` koodi päättää itse, minkä tyyppinen arvon pitää olla. Se on tavallista lomakkeiden syötteissä, sillä kaikki, mikä tulee kautta `$_GET` tai `$_POST`, on aluksi merkkijono tai taulukko. Cast ei kuitenkaan tarkista, oliko arvo järkevä: merkkijonosta `"abc"` tulee hiljaa `0`. Syötteille aito tarkistus funktiolla `filter_var()` on siksi yleensä parempi valinta.

Luokat ja rajapinnat ovat PHP:ssä myös tyyppejä. Funktio voi siis vaatia, että parametri on tietyn luokan olio tai täyttää tietyn rajapinnan. Miten luokat, rajapinnat ja enumit toimivat yhdessä, selitetään sivulla [Olio-ohjelmointi PHP:ssä](https://phpsos.de/fi/php/objektorientierung/).

## Tyyppimääritykset: versioista PHP 7.1 versioon 8.3

Tyyppimääritykset kertovat, mitä arvoja funktio ottaa vastaan ja palauttaa. Tyyppijärjestelmä on kasvanut monen version aikana. Käsikirjan muutoslokin mukaan mukaan tulivat muun muassa seuraavat mahdollisuudet:

| Versio | Uutta |
|---|---|
| 7.1.0 | nullable-tyypit (`?int`), `void`, `iterable` |
| 7.2.0 | `object` |
| 7.4.0 | tyypitetyt luokan ominaisuudet |
| 8.0.0 | unioni-tyypit (useita sallittuja tyyppejä yhdessä määrityksessä), `mixed`, paluutyyppi `static` |
| 8.1.0 | leikkaustyypit (`A&B`), paluutyyppi `never` |
| 8.2.0 | DNF-tyypit, literaalityyppi `true`, `null` ja `false` omina tyyppeinään |
| 8.3.0 | tyypitetyt luokka-, rajapinta-, trait- ja enum-vakiot |

DNF tarkoittaa disjunktiivista normaalimuotoa, ja se sallii yhdistelmiä kuten `(A&B)|null`. PHP-versioita suunnitteleva näkee tästä taulukosta myös, mitä kielen ominaisuuksia sovellus edellyttää.

## strict_types: muunnos vai TypeError

Ilman lisämääräystä PHP toimii muuntavassa tilassa. Funktio, jolla on `int`-parametreja, ottaa silloin vastaan myös arvon `1.5` ja tekee siitä `1`; versiosta PHP 8.1 lähtien tämä tietoa menettävä muunnos on merkitty vanhentuneeksi. Kun tiedoston alussa on `declare(strict_types=1)`, PHP heittää sen sijaan `TypeError`-virheen.

```php
<?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, statt stillschweigend 3
```

Kaksi hienoisuutta on tärkeitä. Ensinnäkin `strict_types` koskee kutsuja siitä tiedostosta, jossa se on, ei siellä määriteltyjä funktioita. Jos tiedosto ilman `strict_types`-määrystä kutsuu samaa funktiota, arvot muunnetaan. Toiseksi kokonaisluku on sallittu myös tiukassa tilassa paikassa, jossa odotetaan liukulukua.

## Löysät ja tiukat vertailut

Operaattori `==` vertaa tyypinmuunnoksen jälkeen, `===` vertaa arvoa ja tyyppiä ilman muunnosta. Käsikirja suosittelee useimmissa tapauksissa operaattoreita `===` ja `!==`, koska muunnossäännöt eivät aina ole ilmeisiä.

```php
<?php
var_dump(0 == "a");    // PHP 7: true,  PHP 8: false
var_dump("1" == "01"); // true, molemmat ovat numeerisia merkkijonoja
var_dump("1" === "01");// false, gleicher Typ, aber andere Zeichenkette
var_dump(1 == true);   // true
var_dump(1 === true);  // false
```

Versiossa PHP 8.0 lukujen ja ei-numeeristen merkkijonojen vertailut muuttuivat järkevämmiksi. Vanhemmat sovellukset voivat päivityksen jälkeen toimia eri tavalla, esimerkiksi `switch`-lohkoissa. Myös versiossa PHP 8.0 käyttöön tullut `match`-lauseke vertaa aina tiukasti. Miten löysät vertailut johtavat tietoturva-aukkoihin, esimerkiksi tunnisteiden tai salasanojen tarkistuksessa, kuvaa artikkeli [Type juggling: löysät vertailut tietoturvariskinä](https://phpsos.de/fi/blog/type-juggling-php/).

## Tyypit, päivitykset ja PHP2Shell

Tiukka tyyppijärjestelmä tekee sovelluskoodista kestävämpää, mutta se ei suojaa virheiltä itse tulkissa. PHP2Shell-haavoittuvuudet CVE-2026-109618 ja CVE-2026-109619 sijaitsevat PHP:n muistin- ja pyynnönhallinnassa. Ne on korjattu versioissa PHP 8.5.12, 8.4.27, 8.3.36 ja 8.2.35; sen jälkeen kaikkien PHP-prosessien on käynnistyttävä uudelleen.

Tyyppijärjestelmällä on silti rooli, nimittäin päivityksessä. Sovellukset, jotka ovat yhä versiossa PHP 8.1 tai vanhemmassa, eivät saa PHP-projektilta enää virallisia päivityksiä. Hyvin tyypitetty koodi on selvästi helpompi nostaa tuettuun versioon, koska työkalut löytävät yhteensopimattomuudet ajoissa. Vanhemmissa hankkeissa apuna on [siirtymämme tuettuun PHP-versioon](https://phpsos.de/fi/leistungen/php-migration-legacy/).

> **Vinkki:**
> Ottakaa `strict_types` käyttöön vaiheittain uusissa ja uudistetuissa tiedostoissa. Näin tyyppiturvallisuus kasvaa ilman, että vanha koodi hajoaa kerralla.

## Miten PHP SOS auttaa konkreettisesti?

- Selvitämme, mitä PHP-versiota sovelluksenne vähintään tarvitsee, ja vertaamme sitä korjattuihin versioihin.
- Ajamme koodille staattisen analyysin ja merkitsemme kohdat, joissa tietoturvan kannalta olennaisissa tarkistuksissa on löysiä vertailuja.
- Suunnittelemme päivitykset niin, että muuttuneet vertailusäännöt testataan etukäteen.
- Asennamme PHP-päivityksen, käynnistämme kaikki worker-prosessit uudelleen ja dokumentoimme tilanteen.


## Usein kysytyt kysymykset

**Onko PHP vahvasti vai heikosti tyypitetty?**  
PHP on dynaamisesti tyypitetty: muuttujan tyyppi selviää vasta ajon aikana. Ilman strict_types-asetusta PHP muuntaa arvoja tarpeen mukaan. Tyyppimäärityksillä ja strict_types-asetuksella koodista voi kuitenkin tehdä hyvin tiukan.

**Mitä declare(strict_types=1) tekee?**  
Se kytkee kyseisen tiedoston kutsuissa pois skalaariarvojen automaattisen muuntamisen. Jos arvo ei sovi ilmoitettuun tyyppiin, PHP heittää TypeErrorin. Ainoa poikkeus on kokonaisluku paikassa, jossa odotetaan liukulukua.

**Miksi 0 == "a" on PHP 8:ssa epätosi mutta PHP 7:ssä tosi?**  
PHP 7:ään asti ei-numeerinen merkkijono muunnettiin vertailussa luvun kanssa luvuksi, ja "a" muuttui nollaksi. Versiosta PHP 8.0 lähtien PHP ei tässä tapauksessa vertaa enää numeerisesti, ja tulos on false.

**Suojaavatko tyypit PHP:n omilta tietoturva-aukoilta?**  
Eivät. Tyypit ehkäisevät virheitä sovelluskoodissanne. Tulkin aukot, kuten PHP2Shell, suljetaan vain PHP-päivityksellä. Modernit tyyppiominaisuudet edellyttävät kuitenkin ajantasaisia PHP-versioita, ja siksi ne ovat hyvä syy päivittää.

## Lähteet

- [PHP-käsikirja: tyyppimääritykset](https://www.php.net/manual/de/language.types.declarations.php)
- [PHP-käsikirja: johdanto tyyppeihin](https://www.php.net/manual/de/language.types.intro.php)
- [PHP-käsikirja: vertailuoperaattorit](https://www.php.net/manual/de/language.operators.comparison.php)
- [PHP 8.0 -julkaisu](https://www.php.net/releases/8.0/en.php)
