Skip to content

Instantly share code, notes, and snippets.

Last active June 11, 2024 10:30
Show Gist options
  • Save mindplay-dk/1b0dde22160c31e4f76b4347bb3da1b1 to your computer and use it in GitHub Desktop.
Save mindplay-dk/1b0dde22160c31e4f76b4347bb3da1b1 to your computer and use it in GitHub Desktop.
PHP upgrades

Upgrading PHP

Guidelines for upgrading the minimum PHP version requirements of packages and projects.

This isn't meant to be an exhaustive guide to upgrading, but as a checklist for the most important upgrades.

PHP 5.3

The first version to support namespaces - any relevant PHP packages/projects usually have this version as the minimum requirement, so this document won't concern itself with upgrades prior to that.

PHP 5.4

  1. Short array syntax: replace array(...) with [...]
  2. Traits: probably don't use this feature, unless maybe you're already doing dodgy run-time hocus-pocus to achieve something similar - but even then, consider refactoring to composition.
  3. Closures now support $this in classes: remove e.g. redundant $that = $this and function () use ($that) declarations in favor of $this.
  4. Replace <?php echo with <?= in templates: it's now always available, regardless of short_open_tag in php.ini.
  5. Use the upload progress feature instead of other solutions to achieve the same.

PHP 5.5

  1. Consider refactoring to generators, if applicable.
  2. Refactor to finally in try-catch blocks, if applicable.
  3. Use native password hashing: avoid insecure sha1 with fixed salt, avoid custom libraries, etc. (Avoid using custom salt, which was deprecated in PHP 7.0.)
  4. Use ::class constants: avoid string literals when specifying class-names.

PHP 5.6

  1. Use variable-length argument lists: avoid using func_get_args() etc. where possible.
  2. Import functions and constants: avoid fully-qualified references to namespaced functions and constants.

PHP 7.0

  1. Use scalar type-hints int, string, float and bool, where applicable.
  2. Add return-type declarations to functions and methods. (If targeting PHP 7.1, add void return-type declarations to functions/methods that return nothing.)
  3. Use the ?? null-coalescing operator where applicable.
  4. Consider using anonymous classes: mainly for things like mocks in unit tests.
  5. Consider grouping use-statements: mainly useful with use function statements to import a group of functions. (PSR-2 requires individual use statements per declaration - it does not consider imports of functions/constants, which did not exist when this was standardized. PSR-12 does not require individual import statements.)
  6. Generator return-expressions: adds confusing generator/function-hybrids, probably don't use.
  7. Generator delegation via yield from: replace verbose foreach ($x as $y => $z) { yield $y => $z; } with better performing yield from $x.
  8. Avoid PHP 4 style constructors: always use __construct.
  9. Static calls to methods not explicitly declared as static: add static where needed.
  10. Consider using cryptographically secure random numbers where applicable.
  11. If using reflection, consider using new reflection methods to obtain parameter-types and return-types.
  12. Improve any error-handlers to make use of Throwable and Error classes, where applicable.
  13. Long-deprecated mysql, mssql and ereg extensions have been removed: must use PDO and preg_* functions now.

PHP 7.1

  1. Add void return-type declarations to functions/methods that return nothing.
  2. Use nullable types where applicable: avoid nullable parameters when defaulting to null is undesirable, e.g. function (Foo $foo = null) should be function (?Foo $foo) when the parameter is non-optional.
  3. Add iterable type-hint where applicable: essentially, iterable is equivalent to the array|Iterator|Traversable type union, which you were unable to properly type-hint in previous PHP versions.
  4. Add visibility modifiers to class constants: at least private and protected, where meaningful. (PSR-12 requires explicit visibility modififers on all constant declarations.)
  5. Use multiple catch where applicable.
  6. Use Closure::fromCallable where applicable: add proper Closure type-hints where possible - avoid using untyped or pseudotyped callable parameter/return-values.

PHP 7.2

  1. Use object type-hint where previously no type-hint was applicable.
  2. Use variance, where applicable: you may have been forced to omit type-hints in base-classes or interfaces. (Note: this works only for parameter types - support for return-types arrived in PHP 7.4.)

PHP 7.3

Nothing to see here, move along.

PHP 7.4

  1. Add property type-hints where applicable. Probably don't replace getter/setter methods with type-hinted properties, which support neither abstraction nor validation.
  2. Arrow functions: if you must, use this for very simple inline functions only. (Considerations: syntax inconsistently uses the fn (fnuh) keyword; syntax is ambiguous with array syntax; using a statement requires refactoring to regular inline function syntax.)
  3. Use variance for return-types, where applicable: you may have been forced to omit type-hints in base-classes or interfaces.
  4. Use array unpacking where applicable: $all_items = [$first, $second, ...$more_items, $last]
  5. Specify username/password for PDO connections via DSN string: separate arguments for username and password are no longer required.
  6. Avoid Serializable interface: use magic methods __serialize and __unserialize instead. (The interface will be deprecated in the future.)
  7. Nested ternaries must now use parenthesis to explicitly indicate evaluation order.
  8. Avoid curly braces when accessing array or string elements: use square brackets, e.g. $var[$x] rather than $var{$x}.

PHP 8.0

  1. Use named arguments where applicable: constructors with many arguments, the typical array $options parameter often used in lieu of named arguments, and so on.
  2. Use native attributes where applicable: replace third-party annotation libraries. Consider using this where other patters are used to establish meta-data of any sort - any library where property- or method-names are specified as strings are likely candidates for refactoring to native attributes.
  3. Constructor property promotion: probably don't use this except for pure data-structures, where you can be certain no validation (beyond type-hints) will ever be required - or you will likely end up refactoring back and forth between this and regular properties.
  4. Add union types where previously these were untyped or annotated with doc-blocks.
  5. Consider refactoring from switch statements to match expressions, where the outcome is a value or expression, as opposed to an action/statement. (Note that match uses strict comparison, whereas switch uses loose comparison.)
  6. Consider using null-safe property/method access: note that this is applicable to branches with strict null comparisons and no exceptions only.
  7. Consider using the mixed type-hint: mostly useful for return-types - where the void return-type means "returns nothing", this essentially means the opposite, explicitly indicating that a function "returns something". (Contrast this with having no return-type, which could mean either "returns something" or "returns nothing".)

PHP 8.1

  1. Consider using enums, where previously you might have been using public const members. The motivation to use enums is type-safety - if you decide to use enums instead, be sure to add type-hints where applicable.
  2. Consider using readonly properties, which can replace private fields with boilerplate getter-methods. Note that this is a breaking change, which can lead to more breaking changes if, for any reason, at a later time, the returned value needs to change to a computed value - and, for the same reason, this will introduce some inconsistencies in your codebase. (This advice would change if PHP introduces property getter/setters in the future.)
  3. Consider migrating from Closure::fromCallable or [$object, "method"] callables to first-class callables, e.g. $object->method(...). Note that this feature does not allow partial applications - to create partial applications, use e.g. fn($b) => $object->method("a", $b) instead.
  4. Consider using new in initializers - as opposed to manually implementing default initializations. This removes the need to use null as a default where null is not actually valid.
  5. Nested attributes are now possible. Please try not to go overboard.
  6. Consider using intersection types, mostly to combine interface types such as Foo&Bar, where previously, type-hinting may not have been possible, or you may have been forced to add a meaningless interface, such as FooAndBar. Note that, while union types exist (and were added in PHP 8.0) these may not be combined (e.g. A&B|C) with intersection types in PHP 8.1 - this feature was added in PHP 8.2.
  7. The never return-type was added, perhaps for that incredibly rare use-case where a daemon runs forever. (You will probably never use this.)
  8. final const is now a thing. (I hope you've never actually overridden a constant.)
  9. Fibers are a really exciting new feature for things like background task-runners, message queues, or other daemons.
  10. Merging arrays with string keys now works, e.g. [...$A, ...$B] rather than array_merge($A, $B).
  11. Classes that implement Serializable must now actually implement the __serialize and __unserialize methods. Look for __sleep and __wakeup implementations and get rid of them.
  12. MySQLi now defaults to throwing exceptions - if you hard coded your DB access using error codes, you should fix that. (or explicitly set the error mode, leave a TODO, come back and fix it later.)

PHP 8.2

  1. readonly class is now a thing - pretty sweet for services/repositories with immutable data models.
  2. Union and intersection types may now be combined, e.g. (A&B)|C - note that parens in groupings are required.
  3. null, false and true are now types, if you need them. (recall that void is already a return-type since PHP 7, so don't start using null in place of that.)
  4. An enormous framework for generating random numbers, why not.
  5. const is now allowed in trait - for those who insist on using traits for some reason.
  6. Dynamic properties have been deprecated! This will cause lots of breakage in really old PHP scripts, and will hopefully break a few classes where you forgot to declare a property. This has no other impact on your code, but this is a really good thing, and the RFC is worth reading - hopefully this paves the way for getter/setters in the future, since two prior proposals (this one and that one) were both dead in the water (in part) because of this.
  7. String interpolation syntax Hello ${world} has been deprecated - search your codebase for ${ and replace with {$ and you should be all good.
  8. utf8_encode and utf8_decode have been deprecated - use mb_* functions instead.
  9. strtoupper and strtolower no longer support current locale - this is a breaking change - use mb_strtoupper and mb_strtolower instead.

PHP 8.3

New features and migration guide are available, docs are still lagging behind...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment