# Olio-ohjelmointi PHP:ssä

> Olio-ohjelmointi PHP:ssä: luokat, rajapinnat, traitit ja enumit, kielen uudet ominaisuudet versioista PHP 8.0–8.4 sekä yhteys deserialisointiin ja tietoturvaan.

Lähde: https://phpsos.de/fi/php/objektorientierung/  
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:** Olio-ohjelmointi (OOP) kokoaa tiedot ja niihin kuuluvat funktiot luokkiin. PHP tarjoaa sitä varten luokat, abstraktit luokat, rajapinnat, traitit ja versiosta PHP 8.1 lähtien enumit. Versioissa PHP 8.0–8.4 kieleen tuli paljon uusia ominaisuuksia, esimerkiksi konstruktorin promootio, `readonly`, property hookit ja epäsymmetrinen näkyvyys. Tietoturvan kannalta tärkeää: hyvin kapseloitu koodi on helpompi tarkastaa, ja taikametodit tekevät turvattomasta deserialisoinnista vaarallisen. Tulkin aukkoja, kuten PHP2Shelliä, olio-ohjelmointi ei kuitenkaan torju.

## Mitä olio-ohjelmointi tarkoittaa PHP:ssä

Irrallisten funktioiden ja globaalien muuttujien sijaan luokka kuvaa asiakokonaisuuden: tilauksen, asiakkaan, laskun. Se määrittää, mitkä tiedot siihen kuuluvat (ominaisuudet) ja mitä sillä voi tehdä (metodit). Olio on tämän luokan konkreettinen kappale.

Päättäjälle tällä on käytännön arvoa: selkeillä luokilla rakennettu olio-orientoitunut koodi on helpompi testata, tarkastaa ja luovuttaa palveluntarjoajalle. Vanhat PHP-sovellukset, jotka koostuvat monista erillisistä komentosarjoista ja globaaleista muuttujista, ovat sen sijaan vaikeita ylläpitää ja alttiita virheille päivityksissä.

Keskeinen ajatus on kapselointi: luokka antaa ulospäin käyttöön vain ne metodit, joita muut todella tarvitsevat, ja suojaa sisäistä tilaansa. Tilauksen voi silloin merkitä maksetuksi esimerkiksi vain metodilla, joka tarkistaa ensin, onko se ylipäätään sallittua. Tällaisten sääntöjen kokoaminen yhteen paikkaan on hyöty myös tietoturvalle.

## Rakennuspalikat pähkinänkuoressa

| Palikka | Tarkoitus | Lyhyt esimerkki |
|---|---|---|
| Luokka | olioiden rakennekuvaus ominaisuuksineen ja metodeineen | `class Rechnung { ... }` |
| abstrakti luokka | yhteinen perusta, ei suoraan instantioitavissa | `abstract class Zahlart { ... }` |
| Rajapinta | sopimus: mitä metodeja luokan on tarjottava | `interface Versandfaehig { ... }` |
| Trait | uudelleenkäytettävät metodit luokille, joilla ei ole yhteistä kantaluokkaa | `trait MitZeitstempel { ... }` |
| Enum | kiinteä joukko sallittuja arvoja omana tyyppinään, versiosta PHP 8.1 | `enum Status { case Offen; ... }` |

Näkyvyydet (`public`, `protected`, `private`) säätelevät, kuka saa käyttää ominaisuuksia ja metodeja. Luokat järjestetään nimiavaruuksiin ja ladataan automaattisella latauksella; miten se toimii, näyttää sivu [Nimiavaruudet ja automaattinen lataus](https://phpsos.de/fi/php/namespaces-autoloading/).

## Moderni PHP: uudistukset versioista 8.0–8.4

Seuraavat kielen ominaisuudet on dokumentoitu PHP-käsikirjassa tai julkaisutiedotteissa:

| Versio | Uudistus |
|---|---|
| 8.0 | konstruktorin promootio, nullsafe-operaattori `?->`, `::class` olioissa |
| 8.1 | `readonly`-ominaisuudet, enumit, `new` alustuslausekkeissa, `final` luokkavakioille |
| 8.2 | `readonly`-luokat; dynaamiset ominaisuudet katsotaan vanhentuneiksi |
| 8.3 | attribuutti `#[\Override]`, tyypitetyt luokkavakiot |
| 8.4 | property hookit, epäsymmetrinen näkyvyys, `new` ilman ympäröiviä sulkeita |

Esimerkki, joka yhdistää useita näistä:

```php
<?php
declare(strict_types=1);

enum Status: string
{
    case Offen = 'offen';
    case Bezahlt = 'bezahlt';
}

final class Bestellung
{
    public function __construct(
        public readonly int $id,
        public private(set) Status $status = Status::Offen,  // versiosta PHP 8.4
    ) {}

    public function alsBezahltMarkieren(): void
    {
        $this->status = Status::Bezahlt;
    }
}

$b = new Bestellung(42);
$b->alsBezahltMarkieren();
echo $b->status->value;              // bezahlt
$s = Status::tryFrom('storniert');   // null virheen sijaan
```

`readonly` estää tunnuksen muuttamisen luomisen jälkeen. `private(set)` sallii lukemisen ulkopuolelta mutta kirjoittamisen vain luokan sisällä. Tällaiset koodin takuut tekevät epätodennäköisemmiksi virheet, jotka muuten huomataan vasta käytössä. Taustalla olevat tyypit selittää sivu [PHP:n tyyppijärjestelmä](https://phpsos.de/fi/php/typsystem/).

### Päivitysansat olio-orientoituneessa koodissa

Vanhoille sovelluksille kaksi muutosta on erityisen olennaisia. Versiosta PHP 8.2.0 lähtien dynaamisten ominaisuuksien luominen on vanhentunutta, eli ominaisuuksien, joita ei ole luokassa ilmoitettu, ellei luokalla ole attribuuttia `#[\AllowDynamicProperties]`. Vanhempi koodi käyttää tätä usein tiedostamattaan ja täyttää lokit sitten huomautuksilla. Lisäksi tyypitetyt ominaisuudet on käsikirjan mukaan alustettava ennen ensimmäistä lukemista, muuten PHP heittää `Error`-virheen.

Hyödyllinen on versiossa PHP 8.3 tullut attribuutti `#[\Override]`: se merkitsee metodin kantaluokan metodin ylikirjoitukseksi. Jos sellaista metodia ei enää ole, esimerkiksi kirjastossa tehdyn uudelleennimeämisen jälkeen, PHP ilmoittaa virheestä sen sijaan, että metodi jäisi hiljaa tyhjäkäynnille. Tällaiset tarkistukset tekevät päivityksistä ennakoitavampia; jumiin jääneissä vanhoissa hankkeissa auttaa [siirtymämme tuettuun PHP-versioon](https://phpsos.de/fi/leistungen/php-migration-legacy/).

## Oliot ja muisti

PHP:ssä oliot välitetään tunnisteiden (handle) avulla. Kun olio annetaan funktiolle, se työskentelee samalla oliolla, ei kopiolla. Se on tehokasta mutta johtaa helposti kehäviittauksiin, esimerkiksi kun tilaus tuntee rivinsä ja jokainen rivi taas tilauksensa. Tällaisia kehiä viitelaskenta yksin ei siivoa; sitä varten PHP:ssä on oma roskienkerääjä. Yksityiskohdat ovat sivulla [Muistinhallinta PHP:ssä](https://phpsos.de/fi/php/speicherverwaltung/).

## OOP ja tietoturva: taikametodit ja deserialisointi

PHP tuntee taikametodeja, joita kutsutaan automaattisesti, kuten `__construct`, `__destruct`, `__toString`, `__get` tai `__wakeup`. Se on käytännöllistä mutta sillä on varjopuolensa: kun sovellus muuntaa ulkopuolista dataa funktiolla `unserialize()` takaisin olioiksi, hyökkääjä voi määrittää, mitä luokkia syntyy, ja käynnistää siten olemassa olevaa koodia taikametodeissa. Miten tämä estetään, kuvaa opas [Turvaton deserialisointi PHP:ssä](https://phpsos.de/fi/ratgeber/deserialisierung-php/). Lyhyesti: tiedonvaihtoon kannattaa käyttää funktioita `json_encode()` ja `json_decode()`, ja `unserialize()`-funktiota vain omalle, luotettavalle datalle, mieluiten asetuksella `allowed_classes`.

PHP2Shellin kohdalla hyvä olio-orientoitunut koodi sen sijaan ei auta. CVE-2026-109618 ja CVE-2026-109619 sijaitsevat tulkissa, ja mod_php:ssä PHP-tiimin mukaan on esitetty hyökkäys, joka toimii ennen kuin sovelluskoodia ajetaan. Pakollista on päivittää versioon PHP 8.5.12, 8.4.27, 8.3.36 tai 8.2.35 ja käynnistää kaikki PHP-prosessit uudelleen.

## Miten PHP SOS auttaa konkreettisesti?

- Arvioimme sovelluksenne rakenteen ja osoitamme, mitkä osat vaikeuttavat päivitystä tuettuun PHP-versioon.
- Etsimme kohdennetusti riskialttiita malleja, kuten `unserialize()` ulkopuolisella datalla ja vaaralliset taikametodit.
- Tarkistamme omat kehitystyönne [tietoturvatarkastuksen ja tunkeutumistestin](https://phpsos.de/fi/leistungen/sicherheitsaudit-penetrationstest/) puitteissa.
- Asennamme PHP-päivityksen, käynnistämme kaikki worker-prosessit uudelleen ja testaamme sovelluksen sen jälkeen.


## Usein kysytyt kysymykset

**Onko PHP olio-ohjelmointikieli?**  
PHP tukee sekä proseduraalista että olio-orientoitunutta koodia. Kehykset, CMS-ytimet ja verkkokauppajärjestelmät on nykyisin kuitenkin pääosin rakennettu olio-orientoituneiksi, ja niissä on luokkia, rajapintoja ja nimiavaruuksia.

**Mikä on ero rajapinnan ja abstraktin luokan välillä?**  
Rajapinta kuvaa vain, mitä metodeja luokan on tarjottava. Abstrakti luokka voi sisältää jo koodia ja ominaisuuksia, mutta sitä ei voi suoraan instantioida. Luokalla voi olla useita rajapintoja mutta vain yksi kantaluokka.

**Mistä lähtien PHP:ssä on ollut enumeja?**  
Luettelotyypit (enum) tulivat versiossa PHP 8.1. Ne korvaavat vakioiden kokoelmat omalla tyypillä, joka sallii vain määritellyt tapaukset. Backed Enum -tyypeillä on lisäksi int- tai string-arvo.

**Miksi taikametodit ovat tietoturvan kannalta olennaisia?**  
PHP kutsuu metodeja kuten __destruct tai __wakeup automaattisesti, esimerkiksi tietoja deserialisoitaessa. Jos ulkopuolista dataa päätyy tarkistamatta funktioon unserialize(), se voi käynnistää odottamattomia kulkuja olemassa olevissa luokissa.

## Lähteet

- [PHP-käsikirja: luokat ja oliot, perusteet](https://www.php.net/manual/de/language.oop5.basic.php)
- [PHP-käsikirja: ominaisuudet](https://www.php.net/manual/en/language.oop5.properties.php)
- [PHP-käsikirja: näkyvyys](https://www.php.net/manual/en/language.oop5.visibility.php)
- [PHP 8.1 -julkaisu](https://www.php.net/releases/8.1/en.php)
