0

PHP version upgrade strategy — migration and deprecation handling

Intermediate5 min read·php-09-026
interview

Concept

PHP version upgrades require understanding what changed, what broke, what was deprecated, and how to migrate safely in a production system.

PHP release cycle: PHP follows an annual release cycle. Each major.minor version is supported for 3 years: 2 years of active support (security + bug fixes) + 1 year of security-only support. Running end-of-life PHP is a significant security risk — vulnerabilities are not patched.

Migration strategy:

  1. Audit current PHP version — what warnings, deprecations, and errors does current code produce under error_reporting(E_ALL)?
  2. Use CI/CD with the target PHP version — run tests against the new version before upgrading production.
  3. Static analysis — PHPStan and Psalm can detect issues without running the code. php -l syntax-checks files.
  4. Rector — automated PHP upgrade tool that applies codemod rules to fix deprecated patterns.
  5. Feature flags or staged rollout — upgrade one server at a time behind a load balancer.

Breaking changes across major versions:

  • PHP 7.0: Scalar type declarations, strict_types, uniform variable syntax (some expressions broke).
  • PHP 8.0: String-to-int comparison semantics changed, several deprecated features removed, __toString exceptions allowed.
  • PHP 8.1: Readonly properties, enums, fibers — mostly additive. Some implicit nullable changes.
  • PHP 8.2: Dynamic properties deprecated (must declare #[AllowDynamicProperties]).
  • PHP 8.4: JIT improvements, property hooks, implicit nullable deprecated.

Code Example

php
<?php
// Checking PHP version at runtime
echo PHP_VERSION;          // "8.4.1"
echo PHP_MAJOR_VERSION;    // 8
echo PHP_MINOR_VERSION;    // 4
echo PHP_RELEASE_VERSION;  // 1

// Conditional code for different versions
if (PHP_VERSION_ID >= 80400) {
    // PHP 8.4+ specific code
} elseif (PHP_VERSION_ID >= 80100) {
    // PHP 8.1+ specific code
}

// Deprecation check — run your test suite with:
// error_reporting(E_ALL);
// ini_set('display_errors', '1');
// All E_DEPRECATED notices will surface

// PHP 8.0 breaking change example — string to int comparison
// Before 8.0: 0 == "any string" was TRUE
// PHP 8.0+:   0 == "any string" is FALSE (string coerced to 0 only if numeric)
$x = 0;
var_dump($x == "hello"); // PHP 7: true, PHP 8: false!
var_dump($x == "0");     // PHP 7+8: true (numeric string)

// PHP 8.2 — dynamic properties deprecated
class OldStyle
{
    // Setting $obj->newProp = 'value' where $newProp isn't declared → E_DEPRECATED in 8.2
}
// Fix: declare the property, or add #[AllowDynamicProperties]
#[\AllowDynamicProperties]
class NewStyle {}

// Rector upgrade command
// composer require rector/rector --dev
// ./vendor/bin/rector process src --config rector.php
// rector.php:
// $rectorConfig->sets([LevelSetList::UP_TO_PHP_84]);

// PHP_EOL, PHP_INT_MAX, PHP_INT_MIN, PHP_FLOAT_MAX constants
// are stable across all PHP versions — safe to use

// composer.json platform constraint — enforce minimum PHP version
// "require": { "php": "^8.2" }
// Prevents installing on incompatible PHP versions