← Debugging Guides

阅读中文版本

PHP Fatal error — Undefined Function and Class Not Found

Diagnose PHP fatal errors by linting the file, reproducing with full error reporting, checking extensions and autoloading, then fixing namespaces and composer class maps.

beginnerdebuggingphpfatal-error

Symptoms

  • PHP Fatal error: Uncaught Error: Call to undefined function curl_init()
  • PHP Fatal error: Uncaught Error: Class "App\Services\Mailer" not found
  • The page returns HTTP 500 with no details in the browser
  • The same code works on one server but not another

Possible causes

  • A required PHP extension such as curl or mbstring is not installed or enabled
  • The namespace or class name does not match the autoloader mapping
  • Composer's autoloader was not regenerated after adding a class
  • A fatal TypeError or value error is hidden by display_errors being off

Diagnosis steps

Lint the file for syntax errors

php -l app/Services/Mailer.php

Interpretation

A parse error is reported with the exact line. Syntax errors prevent the file from loading at all and can surface as a class-not-found error elsewhere.

Reproduce with full error reporting

php -d display_errors=1 -d error_reporting=E_ALL -d log_errors=0 app/index.php

Interpretation

This prints the uncaught error, file, and line instead of a blank 500. Never leave display_errors enabled in production.

Check the extension or class

php -m | grep -i curl && php -r "var_dump(class_exists('App\\Services\\Mailer'));"

Interpretation

If the extension is missing from php -m, install and enable it. A false class_exists means the autoloader cannot map the namespace to a file.

Inspect the configured error log

php -i | grep -E 'error_log|display_errors|log_errors' && tail -n 50 /var/log/php/error.log

Interpretation

The log records the actual fatal error even when the response is empty. Its path must be writable by the PHP-FPM user.

Root cause

A PHP fatal error stops execution immediately and cannot be caught by userland code unless it is a throwable Error. Undefined functions usually mean a missing extension, while class-not-found errors mean the namespace, file path, or Composer class map does not match. With display_errors disabled, the response is only a 500 and the cause lives in the error log.

Fix

Install or enable the missing extension, correct the namespace and file path to follow PSR-4, and run composer dump-autoload -o after adding classes. For type errors, fix the value at the boundary rather than casting blindly. Configure log_errors=On with a writable error_log and display_errors=Off in production, and fail fast at startup if required extensions are absent.

Risk note

Enabling display_errors in production leaks paths, SQL, and credentials to users. Suppressing errors with @ hides fatal causes and breaks logging. Editing vendor autoload files is overwritten by the next composer install.

Verify the fix

Run php -l on changed files, request the failing route, and confirm a normal response instead of 500. Check the error log has no new fatal entries, then run the test suite in an environment matching production extensions.

A fatal error is the end of the request

PHP Fatal error: Uncaught Error: Call to undefined function curl_init()

PHP stops at that line. If display_errors is off, the browser only sees a 500 and the detail is written to the error log, if logging is configured.

Reproduce with the error visible

php -l app/Services/Mailer.php
php -d display_errors=1 -d error_reporting=E_ALL -d log_errors=0 app/index.php
php -m | grep -i curl
php -r "var_dump(class_exists('App\\Services\\Mailer'));"

php -l catches syntax errors. The second command prints the actual uncaught error. php -m confirms whether the extension exists for this SAPI; a web server may load a different php.ini than the CLI.

Fix the two common shapes

For a missing function, install the extension:

sudo apt-get install php8.3-curl
sudo systemctl reload php8.3-fpm

For a missing class, align the namespace with the PSR-4 root and regenerate the autoloader:

composer dump-autoload -o

Check that the file is at app/Services/Mailer.php with namespace App\Services;, and that composer.json maps App\\ to app/. Case matters on Linux even if it worked on a case-insensitive filesystem.

Verify

Request the failing route and confirm a normal response. Tail the error log for new fatal entries, then run the test suite in a container with the production php.ini. Keep display_errors=Off and log_errors=On in production; the log is where the next fatal error will be found.