W3docs

error_reporting()

In this article, we will discuss error reporting functions in PHP. Error reporting is an essential aspect of PHP development as it helps developers identify and

Introduction

This chapter covers how PHP reports errors: the error_reporting() function that controls which errors PHP raises, the display_errors and log_errors directives that control where those errors go, and the supporting functions you use to handle and log them. By the end you will know how to configure error reporting differently for development and production, and why getting this wrong is one of the most common security and debugging mistakes in PHP.

Why error reporting matters

PHP runs as a forgiving, dynamically typed language: many mistakes that would stop another language from compiling instead surface at runtime as warnings, notices, or deprecation messages. If you suppress these, broken behaviour can pass silently into production. If you show them to end users, you can leak file paths, SQL, and stack traces to attackers.

Good error reporting strikes the right balance for each environment:

  • In development — show everything, loudly, so you catch bugs as you write them.
  • In production — show nothing to the user, but log everything to a file for later analysis.

Error levels and constants

PHP errors are graded by level. Each level is a predefined constant, and you combine them with bitwise operators. The most common levels:

ConstantMeaning
E_ERRORFatal run-time error; script execution stops.
E_WARNINGRun-time warning; script continues.
E_NOTICENotice (e.g. using an undefined variable).
E_DEPRECATEDUse of a feature that will be removed in a future PHP version.
E_USER_ERROR / E_USER_WARNING / E_USER_NOTICELevels you raise yourself with trigger_error().
E_ALLAll errors, warnings, and notices.

Because these are bit flags, you combine them with | (include), & (mask), and ~ (negate):

// All errors EXCEPT notices and deprecation messages
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);

// Only fatal errors and warnings
error_reporting(E_ERROR | E_WARNING);

Version note: Since PHP 8.0 the default error_reporting level is E_ALL. E_STRICT was deprecated in 8.0 and removed in 8.4, and its notices are now folded into E_ALL, so you no longer need to add it separately.

Reporting vs. displaying: two different settings

A frequent source of confusion is that two independent switches decide whether you actually see an error:

  1. error_reporting() — decides which levels PHP generates.
  2. display_errors — decides whether generated errors are printed to the output.

You must turn on both to see an error on screen. The example below generates an E_ALL-level notice and prints it because both switches are on:

<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');

echo $undefined;   // Warning: Undefined variable $undefined

If display_errors were off, the same notice would still be generated (and could be logged) but nothing would appear in the page output.

The core error-reporting functions

error_reporting()

Sets which error levels are reported at runtime and returns the previous level. Call it with no argument to read the current setting.

<?php
$old = error_reporting(E_ALL & ~E_NOTICE);
echo "Now reporting all errors except notices.\n";
echo "Previous level was: " . $old . "\n";

ini_set()

Overrides a php.ini directive for the duration of the current script. It is the runtime equivalent of editing php.ini, and it is how you flip display_errors, display_startup_errors, and log_errors from inside code.

<?php
// Development setup: show everything on screen
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

Note: display_errors cannot catch fatal parse errors in the same file, because the whole file fails to compile before ini_set() runs. For those, set the directive in php.ini itself.

set_error_handler()

Registers a callback that runs whenever a (non-fatal) error is raised, letting you replace PHP's default behaviour — for example to convert warnings into exceptions or to format them as JSON for an API. The callback receives the error level, message, file, and line.

<?php
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool {
    echo "[$errno] $errstr in " . basename($errfile) . " on line $errline\n";
    return true; // true = we handled it; PHP's internal handler is skipped
});

echo $undefined; // routed to our handler instead of the default message

See set_error_handler() for the full signature and how to restore the previous handler.

error_log() and trigger_error()

  • error_log() sends a message to PHP's configured log, a specific file, or an email address — never to the page output. This is the production-safe way to record problems.
  • trigger_error() raises an error you define (E_USER_* levels), which then flows through the same error_reporting/handler pipeline as built-in errors.
<?php
// Append a message to a specific log file (message type 3)
error_log("Payment gateway timed out", 3, "/var/log/php_errors.log");

// Raise a user-level warning that your handler / log can pick up
trigger_error("Cache miss for product 42", E_USER_WARNING);

Learn more in error_log() and trigger_error().

Rather than scatter settings around, put them at the top of your bootstrap file.

<?php
// --- Development ---
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
<?php
// --- Production ---
error_reporting(E_ALL);          // still GENERATE everything...
ini_set('display_errors', '0');  // ...but never show it to users...
ini_set('log_errors', '1');      // ...log it instead.
ini_set('error_log', '/var/log/php_errors.log');

The production block keeps full visibility through logs while exposing nothing to visitors — the configuration you almost always want on a live server.

Best practices

  • Never run production with display_errors on. Leaked paths and stack traces are an information-disclosure risk.
  • Report at E_ALL everywhere. Reporting and displaying are separate; suppressing levels just hides bugs.
  • Avoid the @ error-suppression operator. It hides errors at the call site and makes debugging far harder; handle the condition explicitly instead.
  • Catch what you can as exceptions. For recoverable failures, prefer try/catch over error handlers. See PHP Exceptions.
  • Use a logging library in larger apps. Tools like Monolog give you log levels, rotation, and multiple destinations on top of error_log().

Conclusion

Error reporting in PHP comes down to three layers: choosing the levels with error_reporting(), choosing the destination with display_errors / log_errors, and optionally customizing handling with set_error_handler() and trigger_error(). Configure them deliberately — verbose on screen during development, silent-but-logged in production — and you get fast debugging without exposing your application to users or attackers.

Related reading: PHP Error handling · error_get_last() · try…catch.

Practice

Practice
What can be said about error reporting in PHP?
What can be said about error reporting in PHP?
Was this page helpful?