Illustration av en grön lysande elkontakt som trycks mot en solid mörk vägg utan uttag, kontakten kan inte nå igenom.
Guider

Samma plugin funkar hos grannen men inte hos dig

Ett plugin fungerar felfritt hos kompisen på ett annat webbhotell, eller på din egen VPS, men hos dig kastar WordPress upp ett kryptiskt "Call to undefined function", eller så slutar en funktion i pluginet helt enkelt fungera utan felmeddelande. Det är sällan din kod, ett trasigt WordPress eller en bugg i pluginet. Oftast har webbhotellet stängt av en inbyggd PHP-funktion på systemnivå, och till skillnad från de flesta andra PHP-inställningar kan du inte slå på den själv.

Vad disable_functions faktiskt gör

De flesta delade webbhotell kör en funktionsblocklista (disable_functions) i sin PHP-konfiguration. Listan stänger av ett urval inbyggda PHP-funktioner som kan köra systemkommandon eller på andra sätt nå operativsystemet under webbservern, typiskt funktioner som exec, shell_exec, system, passthru, proc_open, popen, putenv och symlink. Tanken bakom det är säkerhet. På en server där hundratals kunder delar samma PHP-process vill webbhotellet inte att en enskild kunds kod ska kunna starta processer eller läsa filer utanför sitt eget konto.

Det här är något annat än ett saknat PHP-tillägg (extension). Ett tillägg som saknas, till exempel imagick eller intl, är helt enkelt inte installerat och kan i regel läggas till av värden eller aktiveras i kontrollpanelen. En avstängd funktion i funktionsblocklistan är däremot fullt installerad och finns i PHP, men webbhotellet har uttryckligen förbjudit att den anropas. Skillnaden spelar roll när du felsöker, för lösningen är helt olika i de två fallen.

Därför kan du inte slå på funktionen själv

Här skiljer sig funktionsblocklistan från nästan alla andra PHP-inställningar. php.nets egen dokumentation är rak på sak.

"This directive must be set in php.ini. It cannot be set in httpd.conf."

Direktivet har ändringsklassen PHP_INI_SYSTEM, vilket betyder att det bara kan sättas i den centrala php.ini-filen på servern. Varken en .htaccess-fil, en .user.ini i ditt eget konto eller ett anrop till ini_set() i koden biter på den typen av inställning. Loopias egen kunskapsbas bekräftar samma sak för sina kunder, med orden "Det går inte att redigera php.ini i ditt konto".

Det lämnar dig med tre spakar, inte fler. Antingen har webbhotellet en inställning i kontrollpanelen där du själv kan slå på enstaka funktioner, det beror på webbhotellet och den är ofta gråmarkerad just för att direktivet ligger på systemnivå. Eller så får du be supporten öppna funktionen åt dig. Eller så flyttar du till en VPS där du äger php.ini och bestämmer själv. Vill du läsa hela beskrivningen av direktivet finns den hos php.net.

Högljutt eller tyst? Det avgörs av pluginets kod, inte av dig

PHP-versionen och hur pluginet är kodat avgör hur felet syns. php.net förklarar skillnaden så här.

"As of PHP 8.0.0, disabling a function removes its definition, allowing userland to redefine it. Prior to PHP 8.0.0, disabling a function just prevented the function from being invoked."

På PHP 8.x är en avstängd funktion alltså odefinierad, som om den aldrig funnits. Ett plugin som anropar den utan skydd kraschar hela sidan med Fatal error: Uncaught Error: Call to undefined function ...(), ett felmeddelande som ser ut som en trasig PHP-installation snarare än en policy hos webbhotellet. Har pluginet i stället en koll med function_exists('...') innan anropet, hoppar det tyst över funktionen. Ingen krasch, inget felmeddelande, bara en funktion som av någon anledning aldrig gör något.

Tre exempel visar hur olika det slår i praktiken.

  • EWWW Image Optimizer, ett av de mer använda pluginen för bildoptimering, kräver exec() för att köra sina komprimeringsverktyg lokalt på servern. Är funktionen avstängd säger pluginet det rakt ut, "EWWW Image Optimizer requires exec() or an API key. Your system administrator has disabled the [exec-funktionen]." Enda vägen framåt då är att betala för deras moln-API i stället. Samma plugin som fungerar felfritt hos grannen kräver alltså en extra prenumeration hos dig.
  • WP-CLI, kommandoradsverktyget för WordPress, förutsätter proc_open och proc_close för sina databaskommandon. Är de avstängda meddelar WP-CLI:s egen dokumentation att kommandot avbryts med texten "The PHP functions proc_open() and/or proc_close() are disabled", och hela wp db-familjen av kommandon slutar fungera.
  • WooCommerce gick 2022 i samma fälla i sin egen kärnkod. Bildregenereringsrutinen anropade putenv() utan skydd, vilket kraschade sajter på webbhotell som stängt av funktionen, med Call to undefined function putenv() som resultat. Felet åtgärdades i en senare uppdatering. Även ett av världens mest använda plugin kunde alltså missa att skydda ett funktionsanrop, och vilket plugin som helst kan råka ut för samma sak.

Driver du en butik ställer WooCommerce egna serverkrav utöver funktionsblocklistan. Vi går igenom dem i vår genomgång av webbhotell för WooCommerce.

open_basedir, filspärren som får plugin att tyst sluta fungera

Ett systerdirektiv skapar ett liknande men annat symptom. open_basedir begränsar vilka mappar på servern som PHP överhuvudtaget får läsa eller skriva i, oavsett vilka funktioner som är påslagna. php.net beskriver det så här.

"When the file is outside the specified directory-tree, PHP will refuse to access it. All symbolic links are resolved, so it's not possible to avoid this restriction with a symlink."

Konsekvensen för ett plugin blir samma sorts tystnad som ovan. Vill pluginet läsa eller skriva utanför kontots hemkatalog, till exempel en delad /tmp-mapp, en systemsökväg eller ett symlänkat bibliotek, nekas det. Oftast utan krasch, bara en Warning i loggen och ett funktionsanrop som tyst returnerar false.

Här skiljer sig open_basedir från funktionsblocklistan på en viktig punkt. Direktivet har ändringsklassen PHP_INI_ALL, vilket betyder att du själv kan skärpa inställningen ytterligare, till exempel i en egen .user.ini, men aldrig luckra upp den eller ta dig förbi den från ditt konto. Det är alltså inte systemnivå-låst på samma sätt som disable_functions, bara satt av värden som ett golv du inte kan gräva dig under. Hela direktivet finns beskrivet hos php.net.

"PHP 8.3 stöds" på prissidan bevisar ingenting

Webbhotellens prissidor radar upp vilka PHP-versioner du kan välja mellan, 8.1, 8.2, 8.3 och så vidare. Det säger ingenting om vilka PHP-funktioner som faktiskt är påslagna i den versionen hos just det kontot. Två konton på samma PHP-version, hos samma värd, kan ha helt olika funktionsblocklistor beroende på paket och konfiguration. Vill du veta vad som gäller hos dig är rätt verktyg phpinfo(), inte versionssiffran. Leta upp raderna disable_functions och open_basedir där. Vår genomgång av PHP hos svenska webbhotell går igenom versioner och tillägg mer i detalj.

En annan dold PHP-inställning pekar åt motsatt håll. max_input_vars styr hur många formulärfält en sida får skicka in samtidigt, och har ändringsklassen PHP_INI_PERDIR. Till skillnad från disable_functions kan du normalt höja den själv via en egen .user.ini-fil. Vi har skrivit om exakt det symptomet, en WordPress-meny där produktvarianter tyst slutar sparas, i artikeln om när menyn eller produktvarianterna inte sparas. Samma familj av dolda gränser ligger bakom ett plugin-symptom, men handlingsutrymmet är rakt motsatt. I det ena fallet kan du fixa det själv, i det andra kan du inte.

disable_functions och open_basedir är dessutom inte de enda dolda gränserna som kan få WordPress att bete sig märkligt. Resurskvoter för processor, minne och antal filer (inoder) är en besläktad men annan sorts dold gräns, en som slår till under belastning snarare än vid ett enskilt funktionsanrop. Den varianten går vi igenom i dolda resursgränser i WordPress.

Vad du kan göra

Börja alltid med att läsa av verkligheten innan du felsöker vidare i pluginet. Skapa en testsida med phpinfo() och leta upp raderna disable_functions och open_basedir. Det tar två minuter och sparar dig timmar av att skylla på fel sak.

  • Fråga supporten exakt vilka funktioner som är avstängda, och om kontrollpanelen har någon inställning för att slå på enstaka funktioner per konto. Den är ofta gråmarkerad eller dold, men det är värt att fråga.
  • Välj i första hand plugin med en reservlösning när en funktion saknas, till exempel ett moln-API eller en lösning i ren PHP, i stället för att bara bero på ett systemanrop.
  • Behöver du genuint en funktion som värden inte vill öppna är en VPS, där du själv äger php.ini, den egentliga lösningen.

Det finns ingen offentlig lista över vilka funktioner som är avstängda hos vilket webbhotell, och det är knappast en slump. Ingen av de stora svenska värdarna publicerar sin funktionsblocklista, vilket i sig är ett svar på frågan varför pluginet inte fungerar hos just dig. Du får läsa av ditt eget konto med phpinfo(), varje gång, snarare än att lita på vad som stod i en forumtråd om en helt annan värd.