skip to content

In PHP, what does an E_DEPRECATED notice tell you, and how should a team act on deprecations before an upgrade?

level: middleimportance: should knowfreq 42%

answer

  1. still works today, will not later
  2. E_DEPRECATED is 8192
  3. production ini hides E_DEPRECATED
  4. deprecated in a minor, removed in a major
  5. fail the test run on deprecations

basics

~10 s

E_DEPRECATED means the code still works but uses something PHP plans to remove or change, usually at the next major version. Surface the notices in development and CI, fix them first, then upgrade.

solid answer

~40 s

`E_DEPRECATED` is a **non-fatal** diagnostic: the code runs exactly as before, but PHP is warning that the feature is scheduled to go. Historically deprecations land in a minor and removals in the next major: `create_function()` and `each()` were deprecated in 7.2 and removed in 8.0. The trap is that the shipped `php.ini-production` sets `error_reporting = E_ALL & ~E_DEPRECATED`, so production logs are silent about them. A team should run development and CI with `E_ALL`, make the test suite fail or report on deprecations, and fix them **on the current version**, where the fix is safe, before switching to the version that turns the warning into a hard error. `E_USER_DEPRECATED` is the same signal raised by libraries through `trigger_error()` or the 8.4 `#[\Deprecated]` attribute.

code

php · 10 lines
php
<?php
declare(strict_types=1);

// Deprecated since 8.4: implicitly nullable parameter (reported at compile time).
function findInvoice(Customer $customer = null) {}

// Fix that works on the current version and the next one.
function findInvoiceFixed(?Customer $customer = null) {}

final class Customer {}

go deeper

for a junior

Recall that a deprecation notice is a warning about the future, not a failure, and that the fix should be made before upgrading.

for a middle

Explain the deprecate-then-remove lifecycle with an example such as each(), and why php.ini-production hides E_DEPRECATED.

for a senior

Describe how you make deprecations visible in CI and staging, including compile-time ones, and fix them on the current version before the switch.

for a principal

Treat the deprecation count as upgrade debt: track it per application, keep it near zero continuously, and budget the work instead of discovering it on upgrade day.

## What the notice means PHP's error levels include **`E_DEPRECATED`** (value 8192), described in the manual as run-time deprecation notices you enable "to receive warnings about code that will not work in future versions". Three properties matter: - It is **not an error**. Execution continues and the deprecated code keeps its old behaviour. - It is a **promise about the future**: the feature is expected to be removed or to change behaviour, most often at the next major version. - It carries a message naming what to change, for example that a parameter is implicitly nullable or that a dynamic property is being created. Its sibling **`E_USER_DEPRECATED`** (16384) is raised by PHP code rather than the engine: a library calling `trigger_error('...', E_USER_DEPRECATED)`, or, since 8.4, a function marked with the `#[\Deprecated]` attribute. The meaning is the same: your code uses something its author plans to remove. Some deprecations are **soft**: documented in the manual with no diagnostic at runtime. PHP 8.5 soft-deprecated `__sleep()` and `__wakeup()`; 8.3 soft-deprecated incrementing non-numeric strings before 8.5 made it a hard deprecation. A soft deprecation will not show up in logs at all, which is one more reason to read the migration guide. ## The lifecycle: deprecated, then removed The usual path for a feature is: 1. **Deprecated in a minor release.** The notice appears; nothing breaks. 2. **Kept through the rest of that major line.** Code keeps working, noisily. 3. **Removed or changed in a later major**, where the same code becomes an `Error`, a parse error or different behaviour. | Feature | Deprecated | Removed or hardened | |---|---|---| | `create_function()` | 7.2 | removed in 8.0 | | `each()` | 7.2 | removed in 8.0 | | dynamic properties | 8.2 | still a deprecation in 8.5 | | `"${var}"` interpolation | 8.2 | still a deprecation in 8.5 | | implicitly nullable `Foo $x = null` | 8.4 | still a deprecation in 8.5 | | incrementing non-numeric strings | soft in 8.3 | hard deprecation in 8.5 | The last three rows are the current backlog: code that triggers them runs on 8.5 today and is what a future major is expected to break. ## Why production logs are silent The `php.ini-production` file shipped with PHP 8.5 sets `error_reporting = E_ALL & ~E_DEPRECATED`; the development file and the built-in default use `E_ALL`. A server built from the production template therefore **never logs a deprecation**. A team that relies on production logs to tell it what will break on the next version sees nothing and learns about the problem on upgrade day. The configuration of error levels is its own topic; the point here is where the signal disappears. A second, quieter gap: some deprecations are raised when a file is **compiled** (an implicitly nullable parameter, `"${var}"`), not when a line runs. With an opcode cache the file is compiled once, so the notice can appear only on the first request after a deploy. ## Acting on deprecations before an upgrade A practical order of work: 1. **Make them visible.** Run development, CI and a staging environment with `E_ALL`, and send deprecations to the log rather than the page. 2. **Make them count.** Configure the test runner to report or fail on deprecations, and add static analysis with deprecation rules, which also finds code paths the tests never execute. 3. **Fix on the current version.** Every deprecation has a replacement that works on the version you run today: `?Foo $x = null` instead of `Foo $x = null`, a declared property instead of a dynamic one, `"{$var}"` instead of `"${var}"`. 4. **Run the next version in CI before production.** Deprecations that only the new release emits appear there first. 5. **Read the migration guide** for each minor you cross, because soft deprecations and behaviour changes do not all produce notices. The principle is to **pay the cost while the old behaviour still works**: a deprecation fixed early is a small, reversible change, while the same issue found after an upgrade is an outage.

  • What is the difference between E_DEPRECATED and E_USER_DEPRECATED?
    `E_DEPRECATED` is raised by the engine or a bundled extension about PHP itself, such as an implicitly nullable parameter. `E_USER_DEPRECATED` is raised from PHP code: a library calling `trigger_error()` with that level, or a function marked `#[\Deprecated]` since 8.4. Both mean "works now, planned to go"; one is about the language, the other about a package's API.
  • Why can a compile-time deprecation be missing from the logs under load?
    Deprecations such as an implicitly nullable parameter are raised while the file is compiled. With an opcode cache, compilation happens once and later requests reuse the cached opcodes, so the notice may appear only for the first request after a deploy or cache reset. Running the test suite or a linter in CI catches these reliably.

saying these in an interview costs you the question

  • An E_DEPRECATED notice means the feature has already stopped working.
  • Deprecated features are removed in the very next minor release.
  • If production logs show no deprecations, the code is ready for the next version.
  • Suppressing deprecations with the @ operator is a valid way to prepare an upgrade.
  • Deprecations can only be fixed after switching to the new PHP version.