← Terug naar kennisbank HOSTING

Fatal error: Uncaught Error: Class not found — uitgelegd en opgelost

PHP probeerde een class te instantiëren (new SomeClass()) maar kan de class-definitie nergens vinden. Resultaat: een fatale fout die je site op die specifieke pageload helemaal afbreekt. De oorzaak ligt bijna altijd in één van vijf scenario's: een ontbrekende Composer-autoload, plugins die in de verkeerde volgorde laden, een namespace-mismatch, file-permissions die het bestand verbergen, of een plugin die wordt gebruikt door een ander plugin maar zelf is gedeactiveerd.

Een typische foutmelding ziet er zo uit:

Fatal error: Uncaught Error: Class "WC_Product" not found
in /home/site/wp-content/plugins/custom-extension/includes/api.php:42
Stack trace:
#0 /home/site/wp-content/plugins/custom-extension/custom-extension.php(15): require_once()
#1 /home/site/wp-includes/class-wp-hook.php(308): include_once()
#2 ...

Drie dingen die nuttig zijn om te lezen:

  • Welke class wordt gezocht: hier WC_Product — een WooCommerce-class. De plugin probeert WooCommerce te gebruiken.
  • Waar de aanroep gebeurt: plugins/custom-extension/includes/api.php regel 42. Dat is de plek die de class zoekt, niet noodzakelijk de plek waar de class hoort te staan.
  • De stack trace: de keten van include's die ertoe leidde. Vaak helpt dit te zien welk hook of welke plugin het probleem triggert.

De vijf hoofdoorzaken

1. Composer autoload niet geladen

Moderne PHP-projecten gebruiken Composer voor dependency-management. Classes worden niet handmatig require'd; ze worden automatisch geladen via Composer's autoloader (vendor/autoload.php).

Symptomen:

  • De fout verschijnt direct na een Composer-update
  • De class staat duidelijk in vendor/leverancier/pakket/src/SomeClass.php
  • De class is netjes gedefinieerd met namespace

Fix: regenereer de autoload-mapping:

# In de project-root (waar composer.json staat):
composer dump-autoload -o

# -o = --optimize: genereert een classmap (snellere lookup dan PSR-4)
# Voor productie altijd met -o.

Werkt nog niet? Controleer dat require_once __DIR__ . '/vendor/autoload.php'; daadwerkelijk wordt uitgevoerd aan het begin van je entry-point. Bij WordPress is dat optioneel — sommige plugins hebben hun eigen autoload-mechanisme zonder Composer.

2. Plugin-volgorde: parent-plugin laadt na child

WordPress laadt plugins alfabetisch op de naam van hun hoofd-bestand. Een plugin my-extension/my-extension.php wordt later geladen dan woocommerce/woocommerce.php. Dat is meestal goed — behalve als je plugin in de admin-area al WooCommerce-classes nodig heeft vóórdat WooCommerce z'n classes heeft geregistreerd.

Vaker: een plugin gebruikt een class van een andere plugin op een hook die te vroeg afgaat (zoals plugins_loaded met priority 5, terwijl WooCommerce z'n classes registreert op plugins_loaded priority 10).

Fix: omhul de gebruiks-code met een class_exists()-check, en hook later:

// In je plugin
add_action('plugins_loaded', 'mijn_init', 20); // Priority 20: na WooCommerce

function mijn_init() {
    if ( ! class_exists('WC_Product') ) {
        return; // WooCommerce niet actief, niets doen
    }
    // Veilig om WC_Product nu te gebruiken
}

Of expliciet in de plugin-header een dependency declareren met Requires Plugins: (WordPress 6.5+). Dan toont WordPress een waarschuwing als de afhankelijkheid niet actief is.

3. Namespace-mismatch met PSR-4

PSR-4 is de standaard die zegt: namespace + class-naam moet exact matchen met directory-pad + bestandsnaam. App\Service\EmailSender moet in src/Service/EmailSender.php staan (gegeven dat src/ de PSR-4-root is).

Veelgemaakte fouten:

  • Bestandsnaam in lowercase: emailsender.php in plaats van EmailSender.php (Linux is hoofdletter-gevoelig, macOS/Windows niet — vandaar dat het lokaal werkt en op productie niet)
  • Namespace heet App\Services maar PSR-4-mapping zegt App\Service (typo)
  • Class staat onder namespace SomeNamespace maar wordt aangeroepen zonder de use-statement

Fix:

# Check exact welke files PSR-4 verwacht
composer dump-autoload --classmap-authoritative

# Toont waarschuwingen als bestand-namen niet matchen met namespace

Hernoem bestanden zodat ze exact de class-naam (incl. hoofdletters) hebben. En gebruik use SomeNamespace\SomeClass; bovenaan elk bestand dat de class gebruikt.

4. File permissions: PHP kan het bestand niet lezen

De class staat er, maar de PHP-user (typisch www-data op Linux) heeft geen leesrechten. Of de directory waarin het bestand staat heeft 700 in plaats van 755.

Diagnose:

# Check rechten op het bestand
ls -la wp-content/plugins/some-plugin/includes/SomeClass.php
# -rw------- 1 user user  (alleen owner kan lezen!)

# Check rechten op de directory tree
namei -m wp-content/plugins/some-plugin/includes/SomeClass.php

# Voor PHP moeten alle directories minimaal 755 zijn en files 644
find wp-content -type f -exec chmod 644 {} \;
find wp-content -type d -exec chmod 755 {} \;

Krijg je dit op shared hosting? De hoster heeft soms een chmod-tool in cPanel (File Manager → rechtsklik → Permissions). Of mail support: "Sommige plugin-bestanden hebben verkeerde permissions, kunnen jullie alle wp-content-files op 644 zetten?"

5. Plugin gedeactiveerd, andere plugin gebruikt z'n class

Klassiek scenario: je deactiveerde "Plugin A" maar "Plugin B" gebruikt nog steeds A\SomeClass. Plugin B crasht, en omdat WordPress nog probeert plugins te laden, kun je niet meer in admin om Plugin B uit te zetten.

Cirkel van ellende. Fix:

Optie A: Plugin B uitzetten via SSH/FTP

cd wp-content/plugins/
mv plugin-b plugin-b.disabled

# Of via WP-CLI
wp plugin deactivate plugin-b

Optie B: Alle plugins uitzetten via SQL

-- In phpMyAdmin of via mysql CLI
UPDATE wp_options
   SET option_value = 'a:0:{}'
 WHERE option_name = 'active_plugins';

Daarna kun je weer in admin en activeer je één voor één om de werkende set terug te krijgen. Plugin B opent z'n probleem opnieuw zodra je 'm activeert — tijd voor een vervanger of bug-rapport bij de developer.

Diagnose-stappen in volgorde

  1. Lees de exacte class-naam in de error. WC_Product? WooCommerce. ACF\Field? Advanced Custom Fields. Stripe\Charge? Stripe-pakket via Composer.
  2. Check of de "owner" actief / aanwezig is: WooCommerce geïnstalleerd en geactiveerd? Composer-package in vendor/ aanwezig?
  3. Check de stack trace voor wie de class probeert te gebruiken. Welke plugin / welk thema / welk script?
  4. Recent veranderd? Plugin-update, composer-update, deploy — vaak is dat de trigger.
  5. Werkt het lokaal en niet op productie? Vrijwel altijd file-permissions of case-sensitivity (Linux vs macOS/Windows).

WordPress-specifiek: meest voorkomende cases

Class "WC_Product" not found (of WC_Order, WC_Customer)

WooCommerce niet actief, niet geïnstalleerd, of een plugin probeert WooCommerce-classes te gebruiken op een hook die te vroeg afgaat. Quick check:

// In de plugin-code vóór WC_Product gebruik:
if ( ! class_exists('WooCommerce') ) {
    return; // Doe niets
}

Class "ACF" not found of get_field not defined

Advanced Custom Fields plugin niet actief. Vaak na een staging-deploy waarbij ACF wel op staging stond maar niet op productie geïnstalleerd is.

Class "Twig\Environment" not found (of Symfony\, Laravel\, Stripe\)

Composer-autoload niet geladen of vendor/ ontbreekt. Run composer install op de server.

Class "WP_Customize_Manager" not found

WordPress core class. Plugin gebruikt 'm op een verkeerde hook (te vroeg, voordat WordPress core volledig is geladen). De fix zit in de plugin-code, niet in jouw config — rapport bij de developer.

Veelgestelde vragen

Mijn site werkte gisteren prima, vandaag deze fout - wat veranderde?

Drie meest waarschijnlijke triggers: (1) een geautomatiseerde update van een plugin/theme/Composer-pakket, (2) je hoster heeft PHP-versie bijgewerkt en oude plugin werkt niet meer met nieuwe PHP, (3) iemand heeft via FTP/SFTP een file gewijzigd. Check je hosting-logs voor recent activity en de Updates-pagina voor recente plugin-updates.

Werkt op staging, niet op productie - waarom?

9 van de 10 keer case-sensitivity. macOS en Windows zijn case-insensitive op file-system niveau (file Foo.php is hetzelfde als foo.php), Linux is dat niet. Een class App\Service\EmailSender kan op staging vinden naar emailsender.php en op productie crashen op zoeken-naar EmailSender.php. Hernoem files exact zoals de class.

Composer dump-autoload werkt niet op shared hosting?

Veel shared hosters geven geen SSH-toegang. Drie opties: (1) lokaal dump-autoload draaien en vendor/composer/ opnieuw uploaden, (2) hoster mailen om Composer-toegang te krijgen, (3) overstappen naar hosting waar SSH wel kan. Voor moderne PHP-development (Laravel, Symfony, custom Composer) is SSH zo'n basisvereiste dat het de moeite waard is om hosting te kiezen die dat heeft.

Wat is het verschil tussen Class not found en function not found?

Class not found: PHP zoekt een class-definitie en kan 'm niet vinden — meestal autoload-issue. Function not found (Call to undefined function): PHP zoekt een vrijstaande functie en kan 'm niet vinden — meestal een ontbrekend require of plugin niet geladen. Ze gedragen zich vergelijkbaar maar de fix is anders: classes worden ge-autoloadt, functies meestal niet.

Kan ik de fout in WordPress 'opvangen' zonder de site op te blazen?

Ja, met WP_DEBUG_LOG = true en WP_DEBUG_DISPLAY = false in wp-config.php. Dan wordt de fout naar wp-content/debug.log geschreven en blijft de site werken (zoveel mogelijk). Bezoekers zien een witte pagina of een vriendelijke fout-pagina als je die hebt ingericht. Goede praktijk voor productie.