PHP
PHP ja tietokannat
Kun rakennat verkkopalveluja PHP:llä, on lähes väistämätöntä, että jossain kohtaa joudut tekemisiin tietokantojen kanssa. PHP tarjoaa tähän kaksi pääasiallista työkalua: mysqli ja PDO. Molemmat ovat natiivilaajennuksia, jotka tulevat suoraan PHP:n mukana.
mysqli
eli "MySQL improved" on nimensä mukaisesti paranneltu versio alkuperäisestä PHP:n MySQL-tuesta.
Se on rakennettu nimenomaan MySQL-tietokannan uusimpia versioita silmällä pitäen.
mysqli toimii hyvin silloin, kun tiedetään, että tietokanta on ja pysyy MySQL-pohjaisena.
PDO
eli PHP Database Object taas on hieman geneerisempi abstraktio.
Se ei sitoudu yhteen tietokantaan, vaan tukee useita eri tietokantamoottoreita.
PDO:n on oiva valinta projekteihin, joissa halutaan säilyttää mahdollisuus vaihtaa tietokantaa myöhemmin.
Huom! PHP:n aiemmissa versioissa (ennen PHP 7.0) käytettiin mysql_*-funktioita tietokantayhteyksiin. Tämä mysql_-laajennus on virallisesti poistettu PHP 7.0 -versiossa, eikä se ole enää saatavilla uusissa PHP-asennuksissa.
Tietokantalaajennusten käyttöönotto (php.ini)
Jotta mysqli tai PDO olisivat käytettävissä, niiden täytyy olla asennettu ja aktivoitu palvelimen PHP-konfiguraatiossa.
Varmista PHP:n php.ini-asetustiedostossa tarkistetaan, että seuraavat rivit eivät ole kommentoitu (ilman puolipistettä alussa):
extension=mysqli
extension=pdo_mysql
Tämän jälkeen Apache tai muu palvelin tulee uudelleenkäynnistää, jotta muutokset astuvat voimaan.
Jos et tiedä mitkä laajennukset ovat käytössä, phpinfo()
-funktio on hyödyllinen asian tarkistamiseen.
Entä jos käytän frameworkkia tai CMS-järjestelmää?
Moni kehittäjä voi tässä kohtaa kysyä: "Entä jos käytän WordPressiä, Laraveliä tai Symfonya – pitääkö minun edes miettiä mysqli:tä tai PDO:ta?" Vastaus on: kyllä ja ei.
Esimerkiksi WordPress kapseloi tietokantatoiminnot wpdb
(WordPress DataBase)-luokkaansa.
Se on melko suoraviivainen wrapperi mysqli:n päällä, ja tarjoaa valmiit metodit tietojen hakemiseen ja tallentamiseen.
Laravel ja Symfony vievät ajattelun astetta pidemmälle tarjoamat ORM-järjestelmän. ORM (Object-Relational Mapping) muuttaa tietokannan taulut PHP-luokiksi ja rivit olioiksi. Tämä voi tehdä tietokantakoodista paljon puhtaampaa – mutta samalla se voi piilottaa sen, mitä SQL-tasolla oikeasti tapahtuu.
Tällaiset alustojen tarjoamat abstraktiot ovat hienoja – ja erityisesti suurissa projekteissa ne voivat nopeuttaa kehitystä, tehdä koodista luettavampaa ja helpottaa ylläpitoa. Mutta – ja tämä on tärkeää – niiden käyttö ei vapauta kehittäjää ymmärtämästä, mitä taustalla tapahtuu. Olen monta kertaa törmännyt tilanteisiin, joissa suorituskykyongelma tai virhe johtuu siitä, että wrapperi tai ORM tekee jotain "omalla tavallaan", ja korjaus vaatii ymmärrystä alimmasta kerroksesta – siitä, mitä mysqli tai PDO oikeasti tekee.
Tietoturva: SQL-injektio ja prepared statements
Jos saisin valita yhden aiheen, jonka jokainen web-kehittäjä oppisi kunnolla heti uran alussa, se olisi todennäköisesti SQL-injektiot.
Hyökkäykset, joissa käyttäjän syöte upotetaan suoraan SQL-kyselyyn, ovat yhä yllättävän yleisiä. On hyvä muistaa: kaikki ulkopuolelta tuleva syöte on oletusarvoisesti epäluotettavaa. Tämä pätee niin lomakkeisiin, URL-parametreihin kuin evästeisiinkin.
Tietokantapohjaisen PHP-sovelluksen kohdalla turvallisin tapa suojautua on käyttää nk. prepared statements-kyselyjä.
Ne pitävät syötteen ja itse SQL-kyselyn erillään, jolloin esimerkiksi OR 1=1
tms. hyökkäysyritykset eivät enää toimi.
Prepared statementsit ovat lähes aina paras tapa käsitellä dataa, ja sekä mysqli että PDO tarjoavat niille natiivin tuen.
Tämän lisäksi on tärkeää validoida syötteet järkevästi – ei siksi, että se estäisi injektiot (se ei yksin riitä), vaan siksi, että se estää epäloogisen tai haitallisen datan päätymisen kantaan.
Virheenkäsittely ja debuggaus
Jos kirjoitat tietokantapohjaista PHP-sovellusta, et voi välttää virheitä. Mutta voit hallita niiden seurauksia. Tässä kohtaa astuvat kuvaan try-catch-rakenteet ja virhelokit.
PDO tukee poikkeusten käsittelyä elegantisti – virheet voidaan napata try-lohkossa ja käsitellä vaikka näyttämällä käyttäjälle siisti virheilmoitus ja kirjaamalla tekninen selite lokiin.
mysqli:n kanssa virheenkäsittely on vähän kömpelömpää. Oletusarvoisesti mysqli ilmoittaa virheistä varoituksilla (PHP Warning) eikä poikkeuksilla. Tästä syystä try-catch-lohko ei nappaa virheitä, ellet muuta oletuskäyttäytymistä asettamalla virhemoodiksi MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
Tämän jälkeen mysqli-toiminnot heittävät oikeita poikkeuksia (mysqli_sql_exception), jotka voit käsitellä tavallisella try-catch-finally-rakenteella.
Debuggausvaiheessa kannattaa käyttää myös ini_set('display_errors', 1)
ja error_reporting(E_ALL)
, jotta saadaan kaikki virheet ruudulle.
Tuotantoympäristössä nämä pitää tietenkin ottaa pois ja käyttää lokitusta sen sijaan.
Suorituskyky: mitä kannattaa säätää?
Vaikka tietokantayhteys PHP:ssä on periaatteessa yksinkertainen – yhdistä, kysy, sulje – niin suuremmassa mittakaavassa pienetkin asiat voivat tehdä eron nopean ja tuskallisen hitaasti toimivan sivun välillä. Tässä muutama perusasia, jotka jokaisen PHP-kehittäjän kannattaa pitää mielessä suorituskykyä ajatellen.
Älä käytä pysyviä yhteyksiä (persistent connections). Vaikka PHP tarjoaa mahdollisuuden käyttää pysyviä yhteyksiä (pconnect), niiden käyttö on harvoin järkevää tavallisissa verkkosovelluksissa.
Tee kyselyistä tehokkaita jo ennen kuin sijoitat ne PHP:n sisälle. Yksi yleisimmistä suorituskykyongelmien lähteistä on huonosti rakennettu SQL-kysely. Jos taulussa on miljoonia rivejä ja kysely käyttää LIKE '%sana%' ilman indeksiä – olet pulassa. PHP ei voi pelastaa huonoa tietokantarakennetta tai puutteellista indeksointia.
Vapauta resurssit heti, kun et niitä enää tarvitse. Sulje yhteys (tai vapauta resurssit) heti kun kysely on tehty ja data käsitelty. Tämä vähentää palvelimeen kohdistuvaa kuormitusta. PHP sulkee tietokantayhteyden automaattisesti skriptin lopussa – mutta sitä ei kannata jäädä odottamaan.