A breakdown of PHP versions

… and how they might make you sad.

PHP 5.4.0

Released: March 1, 2012
EOL: September 14, 2014
End of Support: September 14, 2015

Latest version: 5.4.45 (released September 3, 2015)

Major changes:

  • Removal of register_globals
  • Removal of magic quotes
  • Default character set changed to UTF-8
  • Removal of safe_mode
PHP 5.4.x

Register globals

User input automatically becomes variables in your script.

No need for $_GET, $_POST, or $_REQUEST.

Where to set:

.htaccess:

php_flag register_globals Off

or

php_flag register_globals On

php.ini:

register_globals = on

or

register_globals = off
PHP 5.4.x

For example:

echo $user_id;

if($is_admin){
    echo "You are in, administrator.
}

Output:

$ curl http://quitesecure.com/script.php?user_id=1337&is_admin=true

1337

You are in, administrator.
PHP 5.4.x

Pre-5.4.0 Upgrade Code with Register Globals:

echo $user_id;

After upgrade:

$user_id = $_REQUEST['user_id'];
echo $id;

… or …

$user_id = (isset($_GET['user_id'])) ? 
            intval($_GET['user_id']) : false;

if ($user_id && $user_id > 0) {
    echo $user_id;
} else {
    echo "Invalid User ID";
}
PHP 5.4.x

Magic Quotes

Optional setting. It caused many problems.

// Example of magic quotes in action
$username = "O'Reilly";
$query = "SELECT * FROM users WHERE username = '$username'";

echo $query;

// Output with magic quotes enabled:
// SELECT * FROM users WHERE username = 'O\'Reilly'

// Output with magic quotes disabled:
// SELECT * FROM users WHERE username = 'O'Reilly'
PHP 5.4.x

Magic quotes - After

$name = $_POST['name'];
$query = "INSERT INTO users (name) VALUES ('" . 
          mysqli_real_escape_string($conn, $name) . "')";

… or …

// Using MySQLi Object-Oriented with prepared statements
$name = $_POST['name'];
$filteredName = preg_replace(
                  '/[^a-zA-Z0-9\s\.,-]/', '', 
                    $name);

$stmt = $mysqli->prepare("INSERT INTO users 
                                      (name) 
                               VALUES (?);");

$stmt->bind_param("s", $filteredName);
$stmt->execute();

$stmt->close();
$mysqli->close();
PHP 5.4.x

Default character set changed.

Default charset for some functions switched from ISO-8859-1 to UTF-8;

Functions will return an empty string if the content is not valid UTF-8.

You can still pass a specific encoding as the second parameter.

Affected Functions:

  • htmlspecialchars()
  • htmlentities()
  • html_entity_decode()
PHP 5.4.x

Example:

htmlspecialchars( $some_data )

… becomes:

htmlspecialchars( $some_data, ENT_COMPAT | ENT_XHTML, 'ISO-8559-1' )

… or, to test 5.4.x behaviour in 5.3.x:

htmlspecialchars( $some_data, ENT_COMPAT | ENT_XHTML, 'UTF-8' )
PHP 5.4.x

Other

  • safe_mode() removed.
  • PHP no longer checks the TZ environment variable; you can use the php.ini setting date.timezone or the date_default_timezone_set function to set the default timezone.
  • Parameter names cannot shadow superglobals - in other words, function something($_GET) { is no longer valid.
PHP 5.4.x

PHP 5.5.0

Released: June 20, 2013
EOL: July 10, 2015

Latest version: 5.5.38 (released July 21, 2016)

Major changes:

  • Generators and coroutines
  • finally keyword for exception handling
  • password_hash() and password_verify() functions
  • OpCache extension bundled
PHP 5.5.x

PHP 5.6.0

Released: August 28, 2014
EOL: December 31, 2018

Latest version: 5.6.40 (released January 10, 2019)

Major changes:

  • Constant scalar expressions
  • Variadic functions using …
  • Argument unpacking using …
  • Exponentiation using **
  • use function and use const
PHP 5.6.x

Encypted client streams now default to peer verification.

No changes required if SSL certificates:

  • signed by a trusted authority
  • non-expired
  • match hostnames.

Best fix is to fix SSL certificates.

Possible fix is disabling.

PHP 5.6.x

PHP 7.0.0

Released: December 3, 2015
EOL: January 10, 2019

Latest version: 7.0.33 (released January 10, 2019)

Major changes:

  • Improved performance (up to 2x faster than PHP 5.6)
  • Scalar type declarations
  • Return type declarations
  • Null coalescing operator (??)
  • Spaceship operator (<=>)
  • Anonymous classes
PHP 7.0.x

Exception Class Changes

PHP 7.0 introduces a new hierarchy for error handling:

interface Throwable
    |- Exception implements Throwable
        |- ...
    |- Error implements Throwable
        |- TypeError extends Error
        |- ParseError extends Error
        |- ArithmeticError extends Error
        |- AssertionError extends Error

All exceptions and errors now implement the Throwable interface.

Example usage:

try {
    // Some code that might throw an exception or error
} catch (Exception $e) {
    // Handle regular exceptions
} catch (Error $e) {
    // Handle PHP errors
} catch (Throwable $e) {
    // Handle any throwable not caught by the previous blocks
}

Note: You cannot implement Throwable directly in user code. Use Exception or Error instead.

PHP 7.0.x

Severity changes in PHP 7.0

E_STRICT errors reclassified:

Some converted to E_DEPRECATED or E_NOTICE Most converted to E_WARNING E_STRICT constant remains, but unused

PHP 7.0.x

Severity changes in PHP 7.0

E_STRICT errors reclassified:

Examples:

Using $this in a static method: E_DEPRECATED

class MyClass {
    public static function myStaticMethod() {
        echo $this->someProperty;
    }
}

Accessing static property non-statically: E_NOTICE

class AnotherClass {
    public static $staticProperty = 'Static Value';
}
$instance = new AnotherClass();
echo $instance->staticProperty;
PHP 7.0.x

PHP 7.0 indirect changes

Variables, properties, and methods

Syntax:

$$foo['bar']['baz']

PHP 5.x:

${$foo['bar']['baz']}

PHP 7.x

($$foo)['bar']['baz']
PHP 7.0.x

Severity changes in PHP 7.0

New fatal errors:

  • Calling non-static methods statically
  • Incompatible method signatures in strict mode
  • Invalid octal literals (e.g., 0789)

Catchable fatal errors now throw TypeError:

  • Type hints violations
  • Invalid argument types to internal functions
PHP 7.0.x

Removal of mysql_* functions

The mysql extension has been removed in PHP 7.0.

Replace mysql_* functions with mysqli or PDO_MySQL:

// Old code using mysql_*
$connection = mysql_connect($host, $username, $password);
mysql_select_db($database, $connection);
$result = mysql_query("SELECT * FROM table");
while ($row = mysql_fetch_assoc($result)) {
    // Process row
}
mysql_close($connection);

// New code using mysqli
$mysqli = new mysqli($host, $username, $password, $database);
$result = $mysqli->query("SELECT * FROM table");
while ($row = $result->fetch_assoc()) {
    // Process row
}
$mysqli->close();

// Or using PDO
$pdo = new PDO("mysql:host=$host;dbname=$database", $username, $password);
$stmt = $pdo->query("SELECT * FROM table");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    // Process row
}
$pdo = null;
PHP 7.0.x

PHP 7.1.0

Released: December 1, 2016
EOL: December 1, 2019

Latest version: 7.1.33 (released October 24, 2019)

Major changes:

  • Nullable types
  • Void return type
  • Iterable pseudo-type
  • Class constant visibility modifiers
  • Multi-catch exception handling
  • Support for keys in list()
PHP 7.1.x

Fatal errors to Error exceptions

Conversions in various extensions

  • Date
  • mysqli
  • mcrypt
  • tidy
  • LDAP
  • SimpleXML
  • Various others.
PHP 7.1.x

User defined functions:

Variable arguments should be explicit:

<?php
function example_function(...$args) {
    $total = 0;
    foreach ($args as $arg) {
        $total += $arg;
    }
    return $total;
}

$result1 = example_function(1, 2, 3);
$result2 = example_function(10, 20, 30, 40, 50);

echo "Result 1: " . $result1 . "\n";
echo "Result 2: " . $result2 . "\n";
?>

Results:

Result 1: 6
Result 2: 150
PHP 7.1.x

User defined functions:

Exception on passing too few function arguments

Example:

<?php
function say_hello($tom){ echo "Hi, $tom"; }

say_hello();
?>

Result in PHP 7.1.0:

PHP Fatal error:  Uncaught ArgumentCountError: Too few arguments to function say_hello(), 0 passed 
in Standard input code on line 4 and exactly 1 expected in Standard input code:2
PHP 7.1.x

PHP 7.2.0

Released: November 30, 2017
EOL: November 30, 2020

Latest version: 7.2.34 (released October 1, 2020)

Major changes:

  • New object type
  • Abstract method overriding
  • Libsodium extension added
  • TLS v1.0 and v1.1 deprecated
  • Improved error messages for object to string conversion
PHP 7.2.x

number_format

No longer returns negative zero.

var_dump(number_format(-0.01)); // now outputs string(1) "0" instead of string(2) "-0"

Before 7.2.0:

string(2) "-0"

7.2.0:

string(1) "0"
PHP 7.2.x

Improve SSL/TLS defaults

tls:// now defaults to TLSv1.0 or TLSv1.1 or TLSv1.2
STREAM_CRYPTO_METHOD_TLS_* constants default to TLSv1.0 or TLSv1.1 + TLSv1.2, instead of TLSv1.0 only
PHP 7.2.x

PHP 7.3.0

Released: December 6, 2018
EOL: December 6, 2021

Latest version: 7.3.33 (released November 18, 2021)

Major changes:

  • Flexible Heredoc and Nowdoc syntax
  • PCRE2 migration
  • Multiple catch exception handling
  • Comma after the last argument in function calls
  • JSON_THROW_ON_ERROR flag for json_encode() and json_decode()
PHP 7.3.x

Heredoc Ending Label Interpretation

Due to the introduction of flexible heredoc/nowdoc syntax, doc strings that contain the ending label inside their body may cause syntax errors or change in interpretation. For example in:

Broken:

$xyz = <<<FOO
abcdefg
   FOO
FOO;

echo $xyz;

Works:

$xyz = <<<ENDDOC
abcdefg
   FOO
ENDDOC;

echo $xyz;
PHP 7.3.x

PHP 7.4.0

Released: November 28, 2019
EOL: November 28, 2022

Latest version: 7.4.33 (released November 3, 2022)

Major changes:

  • Typed properties
  • Arrow functions
  • Null coalescing assignment operator (??=)
  • Spread operator in array expressions
  • Numeric literal separator
  • Weak references
PHP 7.4.x

Password algorithm constants

Password hashing algorithm identifiers are now nullable strings rather than integers.

  • PASSWORD_DEFAULT was int 1; now is string 2y (it was null in PHP 7.4.x)
  • PASSWORD_BCRYPT was int 1; now is string 2y
  • PASSWORD_ARGON2I was int 2; now is string argon2i
  • PASSWORD_ARGON2ID was int 3; now is string argon2id

If you’re not hardcoding or duplicating constants, this shouldn’t effect anything.

PHP 7.4.x

Misc changes in 7.4.0

fn is now a reserved keyword. It can no longer be used as a function or class name - ithough it can still be used as a method or class constant name.

o serialization format has been removed. This was produced by PHP 3.x, though note that apparently serialization has been broken for a long time.

PHP 7.4.x

PHP 8.0.0

Released: November 26, 2020
EOL: November 26, 2023

Latest version: 8.0.30 (released August 3, 2023)

Major changes:

  • Named arguments
  • Union types
  • Attributes
  • Constructor property promotion
  • Match expression
  • Nullsafe operator
  • JIT compiler
PHP 8.0.x

Offset access via curly braces removed

Before After
$something{0} $something[0]
$something{'key'} $something['key']
PHP 8.0.x

each removed.

Before 8.0

each($some_array as $v){ ... }

After 8.0

foreach($some_array as $v) { ... }
PHP 8.0.x

Mixed-type comparison changes

Expr 5.6 8.0
0.0 > '' False True
0 > '' False True
0.0 == '' True False
0 == '' True False
0.0 != '' False True
0 != '' False True
PHP 8.0.x

money_format() removed

replaced by NumberFormatter::formatCurrency() and NumberFormatter::parseCurrency()

PHP 8.x
$number = 2345.67;
setlocale(LC_MONETARY, 'en_US');
echo money_format('%i', $number); # => USD 2,345.67

… becomes:

$number = 2345.67;
$formatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
echo $formatter->formatCurrency($number, 'USD'); # => USD 2,345.67
PHP 8.0.x

PHP 8.1.0

Released: November 25, 2021
EOL: December 31, 2026

Latest version: 8.1.29 (released June 6, 2024)

Major changes:

  • Enumerations
  • Readonly properties
  • First-class callable syntax
  • Fibers
  • Pure intersection types
  • Never return type
  • Final class constants
PHP 8.1.x

8.1.0 GLOBALS changes

Write access to entire $GLOBALS array disallowed, as in:

array_pop($GLOBALS) 

Read access still works.

Write access to individual elements still works:

$GLOBALS['foo'] = 'baz';
PHP 8.1.x

HTML encoding/decoding changes

Affected functions:

htmlspecialchars()
htmlentities()
htmlspecialchars_decode()
html_entity_decode()
get_html_translation_table() 

Default mode is now ENT_QUOTES | ENT_SUBSTITUTE rather than ENT_COMPAT.

Changes: ' is escaped to &#039.

Malformed UTF-8 will be replaced by a Unicode substitution character, instead of resulting in an empty string.

PHP 8.1.x

PHP 8.2.0

Released: December 8, 2022
EOL: December 31, 2027

Latest version: 8.2.22 (released August 1, 2024)

Major changes:

  • Readonly classes
  • Disjunctive normal form types
  • Allow null, false, and true as stand-alone types
  • New “random” extension
  • Deprecate dynamic properties
PHP 8.2.x

String localization Example:

<?php

echo strtoupper("Jäschke");
echo "\n";
echo mb_strtoupper("Jäschke");

?>

Output:

JäSCHKE
JÄSCHKE
PHP 8.2.x

String function changes.

strtolower(), strtoupper(), stristr(), stripos(), strripos(), lcfirst(), ucfirst(), ucwords(), and str_ireplace() are longer locale-sensitive.

Literally, this is implemented with subtraction and addition:

  "A" (0x41) to "Z" (0x5a)
  "a" (0x61) to "z" (0x7a)

strtolower adds 32 to characters in the first range, strtoupper subtracts, etc.

Localized versions of these functions are available in the MBString extension.

PHP 8.2.x

PHP 8.3.0

Released: November 23, 2023
EOL: December 31, 2026 End of Support: December 31, 2025

Latest version: 8.3.3 (released February 15, 2024)

Major changes:

  • Typed class constants
  • Dynamic class constant fetch
  • Anonymous readonly classes
  • #[\Override] attribute
  • json_validate() function
  • Improved support for Randomizer objects
PHP 8.3.x

Backwards Incompatible Changes in PHP 8.3

  • Some programs very close to stack oveflow may now throw an error.

  • proc_get_status() bugs fixed.

  • Numerous DOM extension changes

PHP 8.3.x

Useful tools

AST rewriting tools:

  • PHPStan

  • Semgrep

  • rector

  • others

  • Tend to produce large diffs.

Other Useful Tools for PHP Upgrades

  • IDE plugins and extensions

    • PHPStorm’s PHP Inspections
    • Visual Studio Code PHP extensions
  • Personal preferred workflow:

    • ack
    • nvim
PHP Upgrade Tools

Using Large Language Models for PHP Upgrades

LLM-powered tools can be valuable assets in upgrading PHP applications:

  • Documentation Assistance.
  • Test Case Generation.
  • Refactoring

Limitations to consider:

  • AI suggestions should always be reviewed by developers.
  • Complex logic or custom implementations may require human expertise.
  • May not always understand full context or business logic.
  • Surprisingly inconsistent.

Nothing replaces human understanding.

AI-Assisted PHP Upgrades

Compatibility Layers for PHP Upgrades

Compatibility layers can help bridge the gap between PHP versions:

  • Symfony Polyfill

    • Provides features from newer PHP versions to older ones
    • Useful for libraries supporting multiple PHP versions
  • mysql/mysqli

  • DIY

PHP Compatibility Tools

Parting Tips for PHP Upgrades

  • Caution if original developers not available.
  • Communicate with stakeholders.
  • Feature freeze can be helpful.
  • Consider if bug fixes for existing bugs can be delayed.
  • Plan for performance testing.
  • Consider long-term maintainability.
PHP Upgrade Best Practices