skip to content

In PHP, how do the E_* constants combine into an error_reporting level, and what does E_ALL & ~E_DEPRECATED report?

level: juniorimportance: should knowfreq 45%

answer

  1. each level is one bit
  2. | adds a level, & ~ removes it
  3. E_ALL is the default since 8.0
  4. E_STRICT removed as a level in 8.4
  5. trigger_error() raises only E_USER_*

basics

~20 s

Each E_* constant is a single bit, so levels combine with | and are removed with & ~. E_ALL & ~E_DEPRECATED, the php.ini-production value, reports every level except engine deprecations; E_ALL, the default since PHP 8.0, reports everything.

solid answer

~40 s

Error levels are bit flags: `E_ERROR` is 1, `E_WARNING` 2, `E_PARSE` 4, `E_NOTICE` 8, and so on up to `E_USER_DEPRECATED` at 16384. A reporting level is a bitmask built with `|` to include levels and `& ~` to exclude them. `E_ALL & ~E_DEPRECATED`, the value php.ini-production ships, reports everything except `E_DEPRECATED` — the engine's deprecation notices — while still reporting `E_USER_DEPRECATED` from libraries. Since PHP 8.0 the built-in default is `E_ALL`; before that it left out notices and deprecations. The `error_reporting()` function reads the level, or sets a new one and returns the old. Your own code raises `E_USER_*` levels with `trigger_error($message, $level)`: `E_USER_NOTICE` by default, `E_USER_WARNING`, `E_USER_DEPRECATED`, and `E_USER_ERROR`, which PHP 8.4 deprecated in favour of throwing an exception. PHP 8.4 also removed `E_STRICT` as a level.

code

php · 9 lines
php
<?php
$old = error_reporting(E_ALL & ~E_DEPRECATED);   // set, keep the previous level

var_dump((bool) (error_reporting() & E_DEPRECATED));      // bool(false)
var_dump((bool) (error_reporting() & E_USER_DEPRECATED)); // bool(true)

trigger_error('Booking::legacyTotal() is deprecated, use total()', E_USER_DEPRECATED);

error_reporting($old);

go deeper

for a junior

Recall that E_* levels are bits combined with | and removed with & ~, and that E_ALL is the default since PHP 8.0.

for a middle

Explain what E_ALL & ~E_DEPRECATED keeps and drops, how error_reporting() reads and sets the level, and which levels trigger_error() accepts.

for a senior

Choose reporting levels per environment so deprecations are seen before upgrades, and replace E_USER_ERROR and E_STRICT leftovers in legacy code.

for a principal

Decide how deprecations flow from libraries to teams — reported, logged and tracked — so PHP upgrades are planned rather than discovered.

## Levels are bits PHP classifies every non-exception diagnostic by an **error level**, and each level is a power of two, so a set of levels fits in one integer: | Constant | Value | Raised for | |---|---|---| | `E_ERROR` | 1 | Fatal run-time errors, such as exhausting memory | | `E_WARNING` | 2 | Run-time warnings; the script continues | | `E_PARSE` | 4 | Parse errors | | `E_NOTICE` | 8 | Notices about probable bugs | | `E_CORE_ERROR` | 16 | Fatal errors during PHP's startup | | `E_CORE_WARNING` | 32 | Warnings during PHP's startup | | `E_COMPILE_ERROR` | 64 | Fatal compile-time errors | | `E_COMPILE_WARNING` | 128 | Compile-time warnings | | `E_USER_ERROR` | 256 | Raised by `trigger_error()` (deprecated in 8.4) | | `E_USER_WARNING` | 512 | Raised by `trigger_error()` | | `E_USER_NOTICE` | 1024 | Raised by `trigger_error()`, its default | | `E_RECOVERABLE_ERROR` | 4096 | Legacy "almost fatal" errors | | `E_DEPRECATED` | 8192 | The engine warning about features that will go away | | `E_USER_DEPRECATED` | 16384 | Deprecations raised by your code or libraries | | `E_ALL` | 30719 | Every level above | `E_ALL` was 32767 before PHP 8.4, when it still included `E_STRICT`. That change is the best argument for writing **constants, never raw numbers**, in configuration and code. ## Combining levels Bitwise operators build a mask: - `E_ERROR | E_WARNING` — only fatal errors and warnings; - `E_ALL & ~E_NOTICE` — everything except notices; - `E_ALL & ~E_DEPRECATED` — everything except engine deprecations; `E_USER_DEPRECATED` is a different bit, so it is still reported; - `E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED)` — no deprecations at all. `~` flips every bit of its operand, and `&` keeps only the bits set in both sides, so `& ~X` clears the bits of `X`. ## Setting and reading the level - In php.ini, `error_reporting` takes the same expression syntax: `error_reporting = E_ALL & ~E_DEPRECATED`. - At run time, `error_reporting(?int $error_level = null): int` returns the current level when called with no argument (or `null`), and sets a new one when given an integer, returning the old value. - The level filters both outputs, displaying and logging; it is not a display switch. Defaults worth knowing: 1. Built-in default since PHP 8.0: `E_ALL`. Before 8.0 it excluded `E_NOTICE` and `E_DEPRECATED`. 2. php.ini-development: `E_ALL`. 3. php.ini-production: `E_ALL & ~E_DEPRECATED`. Keeping deprecations visible in development or staging is how you find code that will break on the next PHP upgrade. ## Raising your own diagnostics with trigger_error() `trigger_error(string $message, int $error_level = E_USER_NOTICE): true` raises a user-level diagnostic that goes through the same reporting and handlers as the engine's. It accepts only `E_USER_ERROR`, `E_USER_WARNING`, `E_USER_NOTICE` and `E_USER_DEPRECATED`; any other level throws a `ValueError`. The most common modern use is **library deprecations**: `trigger_error('Booking::legacyTotal() is deprecated, use total()', E_USER_DEPRECATED)` warns callers without breaking them. Passing `E_USER_ERROR` has been deprecated since PHP 8.4 — throw an exception or call `exit()` instead. ## What was removed `E_STRICT` used to carry coding-standard advice. PHP 8.4 removed it as an error level, since the engine no longer raised it, and deprecated the constant. Configurations such as `E_ALL | E_STRICT` are leftovers from older releases. ## Testing a level in code Because the level is a bitmask, "is this level reported?" is a bitwise AND: ```php if (error_reporting() & E_USER_DEPRECATED) { // deprecation notices from libraries are currently reported } ``` The result is non-zero when the bit is set. This is the same test an error handler performs with `error_reporting() & $errno` to decide whether to act on a diagnostic, and it is why comparing the level with `===` against one constant is wrong: `E_ALL & ~E_DEPRECATED` is not equal to `E_ALL`, yet it still reports warnings. Treat the level as a set of flags, and ask about one flag at a time.

  • What does trigger_error() do when given E_WARNING instead of E_USER_WARNING?
    It throws a `ValueError`. `trigger_error()` only accepts the user levels `E_USER_ERROR`, `E_USER_WARNING`, `E_USER_NOTICE` and `E_USER_DEPRECATED`; engine levels such as `E_WARNING` are reserved for PHP itself.
  • Why write E_ALL in configuration rather than its numeric value?
    Because the value changes between releases. `E_ALL` was 32767 until PHP 8.4 removed the `E_STRICT` level, and it is 30719 now. A hard-coded number silently means something different after an upgrade; the constant always means every level the running version has.

saying these in an interview costs you the question

  • E_ALL & ~E_DEPRECATED also hides E_USER_DEPRECATED notices
  • Subtracting a constant, as in E_ALL - E_NOTICE, is the safe way to drop a level
  • PHP's default error_reporting still excludes notices
  • trigger_error() can raise E_WARNING or E_ERROR directly
  • E_ALL | E_STRICT is still needed to see every message