Hopp til innhold

PHP

språket det meste av det gamle nettet kjører på

Hva det er

PHP er et skriptspråk som kjøres på webserveren: en forespørsel kommer inn, PHP-koden kjører, og det som når nettleseren, er HTML-en den produserte, aldri selve koden. Bokstavene står i dag for «PHP: Hypertext Preprocessor», et rekursivt akronym, men de begynte som «Personal Home Page». En PHP-fil kan være vanlig HTML med kode plassert mellom taggene <?php og ?>, eller ren kode som et rammeverk sender hver forespørsel gjennom. Språket kjører også skript fra kommandolinjen, men utformingen heller mot å lage nettsider.

Historien begynner i 1994, da Rasmus Lerdorf skrev et sett CGI-binærfiler i C for å telle besøkene på CV-en sin på nettet og kalte dem Personal Home Page Tools. Han ga ut kildekoden i juni 1995, og verktøyene vokste til PHP/FI. I 1997 begynte Andi Gutmans og Zeev Suraski å skrive om parseren, og resultatet kom ut som PHP 3.0 i juni 1998 under det rekursive navnet; på det meste var det installert på omtrent 10 % av webserverne på internett. Den neste motoren deres, Zend Engine, oppkalt etter dem begge, bar PHP 4.0 i mai 2000, og PHP 5 fulgte i juli 2004.

På en server i dag kjøres PHP som regel av PHP-FPM, FastCGI Process Manager, som følger med språket. En webserver foran sender hver PHP-forespørsel videre til en pulje (pool) med arbeidsprosesser. Hver pulje kan kjøre som sin egen bruker og gruppe, med sitt eget miljø og egne verdier i php.ini, og prosessbehandleren holder enten et fast antall arbeidsprosesser, starter dem ved behov eller lar antallet variere mellom to grenser. Språket utvikles i utgivelsesgrener (release branches), og de fire siste kom med ett års mellomrom. Hver gren får to års aktiv støtte, med rettinger av både feil og sikkerhetshull, deretter to år til med bare kritiske sikkerhetsrettinger, og så når den end of life, slutten av levetiden. PHP 8.2, utgitt 8. desember 2022, får sikkerhetsrettinger til 31. desember 2026; PHP 8.5, utgitt 20. november 2025, får dem til 31. desember 2029.

PHP konverterer mellom typer når en operasjon krever det, og det er i den løse sammenligningsoperatoren == det viser seg tydeligst. Er begge sider numeriske strenger, eller den ene et tall og den andre en numerisk streng, blir sammenligningen numerisk. Før PHP 8.0.0 ble enhver streng som ble sammenlignet med et tall, først gjort om til et tall, så 0 == "a" var sann. Den strenge operatoren === sammenligner typen i tillegg til verdien og konverterer ingenting, og manualen anbefaler selv å foretrekke den i de fleste tilfeller.

<?php
var_dump(0 == "a");      // PHP 7: true, PHP 8: false
var_dump("1" == "01");   // true i begge: to numeriske strenger
var_dump("10" == "1e1"); // true i begge: 10 er lik 1e1
var_dump(100 == "1e2");  // true i begge
var_dump(0 === "a");     // false i begge: === konverterer aldri

Hvorfor det er viktig

PHP betyr mest på grunn av det som allerede er skrevet i det. WordPress og Joomla, publiseringssystemene svært mange organisasjoner bruker til nettsteder de redigerer selv, er PHP-applikasjoner: WordPress anbefaler PHP 8.3 eller nyere, og Joomla 6 krever minst PHP 8.3.0. WordPress kjører fortsatt på PHP 7.4, men siden med systemkravene advarer om at slike versjoner har nådd slutten av levetiden. En virksomhet kan være avhengig av PHP i årevis uten at noen der har skrevet en eneste linje i språket, og derfor blir versjonen under glemt. Et nettsted fortsetter å levere sider etter at grenen har nådd end of life; det slutter bare å få rettinger, og ingenting på skjermen sier fra.

Oppgraderinger endrer også oppførsel, i det stille. PHP 8.0 endret hvordan strenger sammenlignes med tall, så en betingelse som var sann på PHP 7, kan være usann på PHP 8 uten noen feilmelding. PHP 8.4 hevet standardkostnaden for bcrypt i password_hash() fra 10 til 12, noe som gjør hver ny passordhash dyrere å regne ut på den samme maskinvaren. Begge endringene er dokumentert, og hver av dem når produksjon bare gjennom en oppgradering som noen må planlegge, teste og eie.

Vår anbefaling er å behandle PHP-versjonen som en avhengighet med en utløpsdato skrevet ved siden av. En eksisterende PHP-applikasjon som virker, er verdt å beholde: flytt den til en gren som har støtte, planlegg neste oppgradering i god tid før sikkerhetsstøtten tar slutt, og gi den sin egen FPM-pulje og sin egen systembruker. Ved et nytt prosjekt bør dere velge PHP når jobben er et nettsted kunden skal redigere i WordPress eller Joomla, eller når teamet som skal drifte det etter overleveringen, kan PHP godt, og ellers velge ut fra hva det teamet kan. Språket er sjelden risikoen; en versjon ingen eier, er det.

Slik ser det ut i praksis

Weblaget på siden om teknologiene vi bruker gir PHP en bestemt plass. Sider som kan være filer, bygges statisk, fordi en side som er en fil, ikke kan falle over ende, og dette nettstedet bygges av Hugo; Django og PHP brukes der det er en applikasjon å levere. Den samme siden sier hvordan valget tas: etter problemet, etter hva kundens team kan drifte etter at vi har gått, og etter hvordan tingen svikter, ikke hvordan den tar seg ut i en demo. Den regelen gjelder PHP direkte: når det riktige svaret er stakken kunden allerede har, lover siden å si det.

Fra 2015 til 2022, i rollen som teamleder for GIS-programvareutvikling, besto arbeidet i å føre mer enn tretti GIS-prosjekter gjennom utvikling, produksjonssetting og driftsstøtte etter at de var satt i drift. De romlige dataene lå i PostgreSQL med PostGIS, og webkartene var bygd på Mapbox GL og Leaflet, noen ganger mot API-ene til ArcGIS eller HERE, med applikasjonslaget i JavaScript, Python, PHP og SQL. PHP var ett av fire språk i det laget, og hovedpoenget på siden gjelder hvert av dem: ansvaret strakk seg forbi leveransen, problemer ble ikke sendt videre, og du designer annerledes når du vet at det er du som blir ringt opp hvis noe ryker.

Hvor det går galt

  • En løs sammenligning vokter en hemmelighet. Et token eller en hash som sjekkes med ==, sammenlignes etter PHPs tallregler hver gang begge sider ser ut som tall, så to ulike strenger som "10" og "1e1" regnes som like. Bruk password_verify() for passord og hash_equals() for andre hemmeligheter: kjøretiden avslører ingenting om innholdet i hemmeligheten, og funksjonen forventer brukerens streng som andre argument. Bruk === overalt ellers.
  • SQL settes sammen av data fra forespørselen. PDO tar selv hånd om siteringen av parameterne i forberedte spørringer (prepared statements), og manualen sier at en applikasjon som bare bruker dem, er beskyttet mot SQL-injeksjon, med ett uttalt unntak: de delene av spørringen som fortsatt bygges av input som ikke er escapet. En plassholder står for en hel verdi, så alt som ikke er en verdi, f.eks. kolonnen en liste sorteres etter, havner fortsatt i strengen. Sjekk de delene mot en fast liste i koden.
  • Passordhasher lagres for smalt. PASSWORD_DEFAULT er bcrypt i dag, og manualen advarer om at den er laget for å endres etter hvert som sterkere algoritmer kommer til. Den anbefaler en kolonne som kan vokse ut over 60 byte, med 255 som et godt valg. Bcrypt kutter dessuten passordet ved 72 byte. Lagre hele hashen, og kall password_needs_rehash() etter en vellykket innlogging, så eldre hasher oppgraderes etter hvert som folk logger inn.
  • Grenen passerer datoen for end of life. Applikasjonen fortsetter å virke, så ingen planlegger oppgraderingen, og hoppet som til slutt kommer, spenner over flere grener på én gang. Noter datoen da sikkerhetsstøtten tar slutt for grenen som er i bruk, oppgrader én gren om gangen, og kjør testsuiten mot neste gren før den nåværende går ut.
  • FPM-puljen er delt og løst satt opp. Én pulje som betjener flere applikasjoner som én bruker, lar en feil i den ene nå filene til alle. security.limit_extensions står som standard på .php .phar, og manualen råder til å begrense den til .php, så en feilkonfigurert webserver aldri sender andre filer til PHP. Gi hver applikasjon sin egen pulje og bruker, og sett pm.max_children til det minnet på serveren kan bære.

Les mer

Ordliste