What’s New and Migration

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against PHP 8.5, checked on 2026-10-05. PHP 8.5 is the current release (latest patch 8.5.11, 2026-09-24). PHP 8.6 is upcoming: it was at RC2 on that date, with general availability scheduled for 2026-11-19, so 8.6 items below are marked as upcoming and may still change. Every other page in this section marks the version that introduced a feature; this page puts the versions side by side.

Release by release

Each release has a migration guide in the PHP Manual listing new features, backward-incompatible changes and deprecations. Read the guides of every version between the one you run and the one you move to.

Release GA Headline changes

7.0

2015-12-03

New engine (about twice as fast as 5.6, much less memory); scalar and return type declarations; ?? and <=>; anonymous classes; Throwable with Error for most fatal errors; CSPRNG random_bytes()/random_int(). Removed: mysql_*, ereg, ASP and script tags

7.1

2016-12-01

Nullable types (?int), void, iterable; [$a, $b] = ... destructuring with keys; class constant visibility; multi-catch; negative string offsets

7.2

2017-11-30

object type; parameter type widening; Argon2 password hashing; sodium in core; mcrypt removed

7.3

2018-12-06

Flexible heredoc/nowdoc; trailing commas in calls; JSON_THROW_ON_ERROR; array_key_first()/array_key_last(); hrtime(); is_countable()

7.4

2019-11-28

Typed properties; arrow functions fn; ??=; spread in arrays; numeric literal separator 1_000; covariant returns; preloading; FFI; WeakReference; __serialize()/__unserialize()

8.0

2020-11-26

JIT; named arguments; attributes; constructor property promotion; union types, mixed, static return; match; nullsafe ?->; throw as an expression; saner string-to-number comparison; TypeError/ValueError from internal functions; str_contains(); WeakMap; Stringable

8.1

2021-11-25

Enumerations; readonly properties; fibers; first-class callable syntax f(...); pure intersection types; never; new in initialisers; 0o octal prefix; array_is_list(); resources to objects (finfo, IMAP, …​)

8.2

2022-12-08

Readonly classes; DNF types; null, false and true as standalone types; constants in traits; #[\SensitiveParameter]; the Random extension (Randomizer); dynamic properties deprecated

8.3

2023-11-23

Typed class constants; #[\Override]; dynamic class constant fetch C::{$name}; re-initialising readonly properties in __clone(); json_validate(); Randomizer::getBytesFromString(); str_increment()

8.4

2024-11-21

Property hooks; asymmetric visibility public private(set); lazy objects; new Foo()->bar() without parentheses; #[\Deprecated]; array_find(), array_any(), array_all(); HTML5 DOM parser (Dom\HTMLDocument); PDO driver subclasses; bcmath\Number; implicitly nullable parameters deprecated

8.5

2025-11-20

Pipe operator |>; clone() with properties; #[\NoDiscard] and (void); closures and first-class callables in constant expressions; array_first()/array_last(); the URI extension; backtraces for fatal errors; OPcache always built in; static asymmetric visibility; attributes on constants

8.6 (upcoming)

2026-11-19 (scheduled)

Partial function application f(?, 2); clamp(); Time\Duration; the SortDirection enum; readonly property defaults; #[\Override] on class constants; stream errors and polling APIs; secure session defaults (use_strict_mode, cookie_httponly, SameSite=Lax)

The 8.6 migration guide is published in the manual when 8.6 is released; until then, the UPGRADING file in the PHP-8.6 branch of php-src is the authoritative list.

Support timeline

Each release gets two years of active support (bug and security fixes), then two years of security fixes only. Since a 2024 policy change, both periods end on 31 December, which also extended 8.1 to the end of 2025. After that a branch is end of life: it receives no fixes at all, including for actively exploited vulnerabilities.

Branch Released Active support until Security support until State on 2026-10-05

7.0 to 7.4

2015-12-03 to 2019-11-28

 — 

2019-01-10 to 2022-11-28

End of life

8.0

2020-11-26

2022-11-26

2023-11-26

End of life

8.1

2021-11-25

2023-11-25

2025-12-31

End of life

8.2

2022-12-08

2024-12-31

2026-12-31

Security fixes only (8.2.34)

8.3

2023-11-23

2025-12-31

2027-12-31

Security fixes only (8.3.35)

8.4

2024-11-21

2026-12-31

2028-12-31

Active (8.4.26)

8.5

2025-11-20

2027-12-31

2029-12-31

Active (8.5.11)

8.6 (upcoming)

2026-11-19 (scheduled)

2028-12-31 (by policy)

2030-12-31 (by policy)

Release candidate

gantt title PHP support windows (active support, then security fixes only) dateFormat YYYY-MM-DD axisFormat %Y todayMarker off section 7.4 active :done, 2019-11-28, 2021-11-28 security :done, 2021-11-28, 2022-11-28 section 8.0 active :done, 2020-11-26, 2022-11-26 security :done, 2022-11-26, 2023-11-26 section 8.1 active :done, 2021-11-25, 2023-11-25 security :done, 2023-11-25, 2025-12-31 section 8.2 active :done, 2022-12-08, 2024-12-31 security :crit, 2024-12-31, 2026-12-31 section 8.3 active :done, 2023-11-23, 2025-12-31 security :crit, 2025-12-31, 2027-12-31 section 8.4 active :active, 2024-11-21, 2026-12-31 security :crit, 2026-12-31, 2028-12-31 section 8.5 active :active, 2025-11-20, 2027-12-31 security :crit, 2027-12-31, 2029-12-31 section 8.6 (upcoming) active :2026-11-19, 2028-12-31 security :2028-12-31, 2030-12-31

Libraries and frameworks set their own minimums. Laravel 13 requires PHP 8.3 to 8.5, and the next major version is expected to require 8.4. Composer’s "php" constraint in composer.json and config.platform.php make the requirement explicit (see Composer and Packages).

Deprecations and removals

PHP removes features in two steps. A minor release deprecates a feature: it still works, but emits an E_DEPRECATED notice (a soft deprecation is documented only, with no notice). The next major release (8.0, then 9.0) removes it. The table lists the deprecations that most often appear in real code.

Version Deprecated Removed

7.0

PHP 4-style constructors; static calls to non-static methods

mysql_*, ereg*, ASP tags <%, <script language="php">, call_user_method()

7.1

mcrypt; the e modifier of mb_ereg_replace()

 — 

7.2

each(), create_function(), __autoload(), the (unset) cast, assert() with a string, $php_errormsg, parse_str() without a result argument

mcrypt (moved to PECL)

7.3

Case-insensitive constants; non-string needles in strpos() and friends; fgetss()

 — 

7.4

Unparenthesised nested ternaries; $str{0} offsets; the (real) cast; money_format(); get_magic_quotes_gpc(); allow_url_include; implode() with reversed arguments

ext/wddx, ext/interbase, ext/recode (unbundled)

8.0

Optional parameters before required ones; libxml_disable_entity_loader()

Everything deprecated in 7.x: each(), create_function(), money_format(), (unset), __autoload(), $str{0}, PHP 4 constructors, fgetss(), string assert(), get_magic_quotes_gpc(); unquoted array keys ($a[key])

8.1

Passing null to non-nullable internal parameters; Serializable without __serialize(); strftime()/gmstrftime(); FILTER_SANITIZE_STRING; mhash*(); autovivification on false; implicit lossy float-to-int conversion; date_sunrise()/date_sunset()

 — 

8.2

Dynamic properties; "${var}" interpolation; utf8_encode()/utf8_decode(); partially supported callables ("self::method"); mbstring QPrint, Base64, Uuencode and HTML-ENTITIES

 — 

8.3

assert_options(); get_class() without arguments; ++ on empty or non-alphanumeric strings and -- on empty or non-numeric strings; MT_RAND_PHP

 — 

8.4

Implicitly nullable parameters (string $s = null); trigger_error() with E_USER_ERROR; _ as a class name; lcg_value(); mysqli_ping(); session sid_length/sid_bits_per_character; E_STRICT

ext/imap, ext/oci8, ext/pdo_oci, ext/pspell (unbundled, now in PECL)

8.5

The backtick operator; non-canonical casts (integer), (boolean), (double), (binary); case ended with ;; null as an array offset; ++ on non-numeric strings; curl_close(), imagedestroy(), finfo_close(), xml_parser_free() (no-ops); __sleep()/__wakeup() (soft); register_argc_argv for web SAPIs; SplObjectStorage::attach()/detach()/contains()

 — 

8.6 (upcoming)

return inside finally; let and is as class names, readonly as a function name; values returned from __construct(); spl_object_hash(); is_long(), is_integer(), is_double(), doubleval(); metaphone(); strcoll()

 — 

Contrary to some upgrade guides, list() is not deprecated: it still works without a notice in 8.5 and in 8.6.0RC2, although [] (7.1) is the usual syntax.

This script runs on PHP 8.5, but every marked line produces a deprecation. Deprecations found at compile time are reported before the script starts; the rest go through the error handler as the lines run:

<?php
error_reporting(E_ALL);
set_error_handler(function (int $level, string $message, string $file, int $line): bool {
    echo "line $line: $message\n";
    return true;
});

class Member
{
    public function rename(string $name = null): void {}    // implicitly nullable parameter (8.4)
}

$member = new Member();
$member->nickname = 'ada';                                   // dynamic property (8.2)

$name = 'Ada';
echo "Hello ${name}\n";                                      // ${} interpolation (8.2)

$count = (integer) '3';                                      // non-canonical cast name (8.5)
$version = 'a9';
$version++;                                                  // increment of a non-numeric string (8.5)
var_dump(strftime('%Y'));                                    // strftime (8.1)
Deprecated: Non-canonical cast (integer) is deprecated, use the (int) cast instead in legacy.php on line 19
Deprecated: Member::rename(): Implicitly marking parameter $name as nullable is deprecated, the explicit nullable type must be used instead in legacy.php on line 10
Deprecated: Using ${var} in strings is deprecated, use {$var} instead in legacy.php on line 17
line 14: Creation of dynamic property Member::$nickname is deprecated
Hello Ada
line 21: Increment on non-numeric string is deprecated, use str_increment() instead
line 22: Function strftime() is deprecated since 8.1, use IntlDateFormatter::format() instead
string(4) "2026"

The fixes are mechanical: ?string $name = null, a declared property (or #[\AllowDynamicProperties] as a stopgap), {$name}, (int), str_increment($version) and IntlDateFormatter.

PHP 9

No release date is set for PHP 9.0, but several RFCs have already been accepted for it. Undefined variables will throw an Error instead of a warning. "${var}" interpolation will be removed. Autovivification on false ($x = false; $x[] = 1;, which silently turns $x into an array) will throw. In general, every feature deprecated during the 8.x series is a candidate for removal in 9.0, so code that runs on 8.5 without deprecation notices is the best preparation.

How to upgrade

  1. Read the migration guides for every version you skip, especially the backward-incompatible changes.

  2. Make deprecations visible. Run the test suite and staging with error_reporting=E_ALL and log E_DEPRECATED; PHPUnit’s failOnDeprecation="true" and displayDetailsOnTestsThatTriggerDeprecations="true" turn them into test failures (see Testing with PHPUnit and Pest).

  3. Check compatibility statically. PHPStan reports calls to deprecated functions with its deprecation-rules extension, and the PHPCompatibility sniffs for PHP_CodeSniffer report syntax and functions that do not exist or are removed in a target version.

  4. Rewrite automatically. Rector’s PHP sets apply each version’s upgrades (constructor promotion, match, readonly, explicit nullable types, …​). Review the diff like any other change; see Static Analysis and Refactoring.

  5. Upgrade dependencies with Composer, raising the "php" constraint last.

  6. Run the new version in CI alongside the old one before switching production.

<?php
// rector.php -- upgrade a code base to PHP 8.5 syntax
use Rector\Config\RectorConfig;

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
    ->withPhpSets(php85: true);    // or withPhpSets() to follow composer.json's "php" constraint
vendor/bin/rector process --dry-run      # show the changes as a diff
vendor/bin/rector process                # apply them
vendor/bin/phpcs -p src --standard=PHPCompatibility --runtime-set testVersion 8.5-

Run with Rector 2.6.7 on a PHP 7-era class, the dry run reports:

 class Member
 {
-    private $name;
-    public function __construct(string $name = null) { $this->name = $name; }
+    public function __construct(private readonly ?string $name = null)
+    {
+    }
     public function label(int $kind): string
     {
-        switch ($kind) { case 1: return 'staff'; default: return 'reader'; }
+        return match ($kind) {
+            1 => 'staff',
+            default => 'reader',
+        };
     }
 }

Applied rules:
 * ClassPropertyAssignToConstructorPromotionRector
 * ChangeSwitchToMatchRector
 * ReadOnlyPropertyRector

Where older books and tutorials are outdated

The PHP books this section draws on were written for PHP 5.2 to 7.3 (see the bibliography on the PHP Reference home page). Each page in this section has its own "outdated" section; this table collects every book statement about the language that is no longer true, with the version where it changed.

What the book says or does Where Changed in Current approach

mysql_connect() and the mysql_* functions

Wiley ch. 8, Beak ch. 12 (quiz)

removed in 7.0

mysqli or PDO with prepared statements

ereg(), eregi(), split() POSIX regular expressions

Kromann, Nixon

removed in 7.0

preg_* (Regular Expressions)

PHP 4-style constructors (a method named like the class)

Beak ch. 5

deprecated 7.0, ignored since 8.0

__construct() (Classes and Objects)

mcrypt for encryption

Beak ch. 1, 6 and 8

deprecated 7.1, removed 7.2

sodium, or OpenSSL with AES-GCM (Cryptography)

while (list($k, $v) = each($a))

Beak ch. 4, Nixon ch. 6

removed in 8.0

foreach (Arrays)

create_function()

Beak ch. 2, Kromann ch. 4

removed in 8.0

closures and arrow functions (Closures)

__autoload()

Kromann ch. 6

removed in 8.0

Composer’s PSR-4 autoloader (Namespaces and Autoloading)

money_format()

Beak ch. 3, Kromann ch. 12

removed in 8.0

NumberFormatter::formatCurrency() (Strings)

The (unset) cast

Beak ch. 1

removed in 8.0

unset() or assigning null

fgetss()

Kromann ch. 9-10

removed in 8.0

escape on output (Files and Streams)

get_magic_quotes_gpc() checks

Nixon ch. 10, 27

removed in 8.0

nothing: magic quotes were removed in 5.4; use prepared statements

Bareword array keys $_POST[email]

Kromann ch. 14

fatal Error since 8.0

quoted keys $_POST['email']

"12 o'clock" == 12 is true

Beak ch. 1

false since 8.0

===, and the 8.0 comparison rules (Type Juggling)

Arithmetic on non-numeric strings gives a warning and 0

Powers ch. 4

TypeError since 8.0

validate and cast input first

Internal functions return false/null with a warning on bad arguments

Beak ch. 11

TypeError/ValueError since 8.0

catch the exceptions (Errors and Exceptions)

PDO throws exceptions by default (said of PHP 7, whose default was silent)

Kromann ch. 28

true only since 8.0

rely on ERRMODE_EXCEPTION on 8.x

Checking connect_error after every mysqli call

Powers ch. 13, Kromann ch. 27

mysqli throws since 8.1

rely on mysqli_sql_exception

strftime() with setlocale()

Beak ch. 3, Powers ch. 16, Kromann ch. 12

deprecated 8.1

IntlDateFormatter (Date and Time)

FILTER_SANITIZE_STRING as an XSS defence

Beak ch. 6 and 12, Kromann ch. 13

deprecated 8.1

escape for the output context with htmlspecialchars()

Serializable

Beak ch. 5

deprecated 8.1

__serialize()/__unserialize() (Magic Methods)

setAccessible(true) before reading private members

Beak ch. 5

no-op since 8.1

omit it (Reflection)

Resources (finfo, GD images, cURL handles) closed with *_close()

the books' cURL, GD and fileinfo examples

objects since 8.0-8.1, close functions deprecated 8.5

let the object go out of scope

Assigning undeclared (dynamic) properties on objects

all books (written before 8.2)

deprecated 8.2

declare properties

json_last_error() after every call

Beak ch. 8

JSON_THROW_ON_ERROR since 7.3

the flag, and json_validate() (8.3) (JSON, XML and Serialization)

func_get_args() for variable argument lists

Beak ch. 2, Kromann

...$args since 5.6

variadic parameters (Functions)

SplObjectStorage::attach()/detach()/contains()

Beak ch. 10

deprecated 8.5

offsetSet()/offsetUnset()/offsetExists(), or WeakMap (SPL)

__sleep()/__wakeup()

Beak ch. 5

soft-deprecated 8.5

__serialize()/__unserialize()

The backtick operator and exec() with concatenated strings

Kromann ch. 9, Nixon ch. 7

backticks deprecated 8.5

proc_open() with an argument array (Command-Line PHP)

OPcache as an extension to install and load

Beak ch. 1

always built in since 8.5

configure it, nothing to install (OPcache and JIT)

References to avoid copying arrays; PHP 5 zval diagrams

Beak ch. 1

copy-on-write and the PHP 7 zval

pass by value (References and Memory)

md5()/sha1() password hashes, md5(uniqid(rand())) tokens

Wiley ch. 8 and 15, Kromann ch. 14

password_hash() since 5.5, random_bytes() since 7.0

password_hash(), bin2hex(random_bytes(32))

PEAR packages; no Composer

Kromann ch. 7, Wiley, all other books

Composer since 2012; PECL to PIE (2025)

Composer and PIE (Composer and Packages)

PSR-0 and underscore pseudo-namespaces (Zend_Db_Table)

Wiley

PSR-0 deprecated 2014

namespaces and PSR-4

PSR-2 coding style

Kromann ch. 21

deprecated 2019

PER Coding Style 3.0 (PSR Standards)

display_errors on to debug

Powers ch. 1-5

 — 

log errors in production; fatal errors include backtraces since 8.5 (Debugging)

Beyond these corrections, none of the books covers the language added since PHP 7.4: typed properties, arrow functions, attributes, named arguments, match, enums, readonly properties and classes, fibers, first-class callable syntax, property hooks, asymmetric visibility, the pipe operator and the rest of the table at the top of this page.