PHP 8.0: What's New and Changed
PHP 8.0 is a major version update and a remarkable milestone in PHP, as it brings several new features to type system, syntax, error handling, strings, object-oriented programming, and more.
It is the efforts of hundreds of people coming together to shape the future of a programming language that powers a significant portion of the Internet web sites and applications.
PHP tries to be conservative with changes that can break a majority of the applications, and yet, it brings several new major features to PHP 8.0.
Features such as Named Parameters, JIT, Attributes, and Constructor Properties bring major improvements and syntax changes, while several minor improvements such as resource
to object migrations, improved error handling, and changes and improvements in operators and engine comparisons help seamlessly reduce the chances of overlooked bugs.
Thank you for all the contributors, of whom there are hundreds, for all your efforts, in PHP core, documentation, libraries, testing tools, extensions, authors, package maintainers, PHP developers, and everyone else including you ❤.
- Major New Features
- New Functions and Classes
- Type System Improvements
- Error Handling Improvements
- Resource to Object Migration
- PHP Object Oriented Programming Changes
- String-related changes
Major New Features
Named Parameters
PHP 8.0 allows named parameters in function/method calls in addition to traditional positional parameters.
str_contains(needle: 'Bar', haystack: 'Foobar');
This makes the function/method parameter names part of the public API. The non-standardized DocBlock @no-named-arguments
expresses that the library does not provide backwards-compatibility for named parameters.
Attributes
Attributes allows declaring meta-data for functions, classes, properties, and parameters. Attributes map to PHP class names (declared with an Attribute itself), and they can be fetched programmatically with PHP Reflection API.
#[CustomAttribute]
class Foo {
#[AnotherAttribute(42)]
public function bar(): void {}
}
Attributes makes it easy and performant to declare Attributes/annotations that previously required storing them in DocBlock comments, and parsing the string to infer them.
Constructor Properties
A new syntax to declare class properties right from the class constructor (__construct
magic method).
class User {
public function __construct(private string $name) {}
}
In the constructor, PHP 8.0 supports declaring the visibility (public
, private
, or protected
) and type. Those properties will be registered as class properties with same visibility and type they are declared in the constructor.
This backwards-incompatible feature can help reduce boilerplate code when declaring value-object classes.
Just-In-Time Compilation
PHP Opcache supports JIT. It's disabled by default, and if enabled, JIT compiles and caches native instructions. It does not make a noticeable difference in IO-bound web applications, but provides a performance boost for CPU-heavy applications.
# Enabling JIT in php.ini
opcache.enable=1
opcache.jit_buffer_size=100M
opcache.jit=tracing
Note that JIT is still new, and had bug fixes as late as a day before PHP 8.0.0 release. It makes debugging and profiling more difficult with the added extra layer.
Union Types
Union Types extend type declarations (return types, parameters, and class properties) to declare more than one type.
function parse_value(string|int|float): string|null {}
It also supports false
as a special type (for Boolean false
), a trait that's prevalent in legacy code that did not use Exceptions.
Null-safe Operator
Null-safe operator provides safety in method/property chaining when the return value or property can be null
.
return $user->getAddress()?->getCountry()?->isoCode;
The ?->
null-safe operator short-circuits the rest of the expression if it encounters a null
value, and immediately returns null
without causing any errors.
match
expressions
Match expressions are similar to switch
blocks, but match
blocks provide type-safe comparisons, supports a return value, does not require break
statements to break-out, and supports multiple matching values. it also guarantees that at least one branch is matched, ensuring all cases are accounted for.
$response = match('test') {
'test' => $this->sendTestAlert(),
'send' => $this->sendNuclearAlert(),
};
Not all switch
blocks might convert well to match
blocks. Code that requires backwards-compatibility, switch
blocks with multiple statements (as opposed to single-line expressions), or expects fall-through functionality still fits the switch
statements.
WeakMaps
A WeakMap allows to store and associate arbitrary values for object keys, but without preventing the garbage collector from clearing it the object falls out of scope in everywhere else.
A WeakMap
is similar to SplObjectStorage
, as in both WeakMap
and splObjectStorage
use objects
s as the key, and allows storage of arbitrary values. However, a WeakMap
does not prevent the object from being garbage collected.
New Functions and Classes
PHP 8.0 introduces a few new functions to ease string inspections (contains, starts with sub-string, or ends with sub-string) to replace the meticulous strpos() !== false
calls that are less readable, and error-prone due to weak type comparisons.
Function | Description | Example |
---|---|---|
str_contains |
Haystack contains the needle somewhere | str_contains('Foobar', 'Foo') |
str_starts_with |
Haystack starts with the needle string | str_starts_with('PHP 8.0', 'PHP') |
str_ends_with |
Haystack ends with needle string | str_ends_with('PHP 8.0', '8.0') |
PHP 8.0 also brings functions such as fdiv
, get_resource_id
, get_debug_type
, and preg_last_error_msg
Function | Description |
---|---|
fdiv |
Float division supporting IEEE-754 standard on Floating-Point Arithmetic. |
get_resource_id |
Returns the internal ID for a given PHP resource |
get_debug_type |
Returns the internal type of a passed variable |
preg_last_error_msg |
Returns a human-friendly error message for the last preg operation. |
New Stringable
interface
The new Stringable
interface is automatically added to all classes that implement __toString
method, and those explicitly declare that they implements Stringable
.
With the Stringable
interface, it is now easy to declare types as string|Stringable
for on functions that can accept/return strings or objects with a __toString()
method.
New PhpToken
Tokenizer class
The new PhpToken
class provides a more fluent Object-Oriented interface as an alternative to the legacy array-based to token_get_all
function.
Type System Improvements
PHP 8.0 improves typing system with the addition of Union Types and the mixed
type.
Union Types
Union Types extend type declarations (return types, parameters, and class properties) to declare more than one type.
function parse_value(string|int|float): string|null {}
It also supports false
as a special type (for Boolean false
), a trait that's prevalent in legacy code that did not use Exceptions.
New mixed
pseudo type
PHP 8.0 brings mixed
type, that was already being widely used in DocBlock comments.
function dd(mixed $var): void {
var_dump($var);
}
mixed
type can be used to indicate that it accepts any type, or can return any type. In a class/interface context, mixed
type plays by the same rules of Liskov Substitution Principle.
static
return type for class methods
static
return type, already support as a DocBlock return type, is now supported in PHP 8.0. The static
return type declares an object of the called class will be returned.
class Foo {
public static function getInstance(): static {
return new static();
}
}
Error Handling Improvements
A major and backwards-incompatible change in PHP is that internal functions now throw exceptions on type errors or value errors.
This corrects PHP's historical behavior of emitting a warning, and returning null
when it encounters a value that it cannot use. This behavior is often undesired because PHP warnings does not halt the execution of the remaining block.
Internal function warnings now throw TypeError
and ValueError
exceptions
Almost entirety of PHP internal functions now enforce type checking, and instead of warnings, PHP now throws TypeError
or ValueError exceptions. On legacy code bases, this change can cause issues, now that the errors are more boldly and unforgivably handled.
throw
in expressions
Prior to PHP 8.0, it was not possible to throw
exceptions from an expression (e.g a ternary statement). This is now allowed in PHP 8.0.
$value = isset($_GET['value'])
? $_GET['value']
: throw new \InvalidArgumentException('value not set');
catch
exceptions only by type
It is possible to catch
exceptions by their type, without capturing the exception object.
try {}
catch(TypeError) {
// Did not catch the $exception object
}
Default error reporting is set to E_ALL
PHP 8.0 default configuration is to show all error messages by default. It was configured to hide deprecation and strict warnings in older versions.
PHP Startup Errors are displayed by default
PHP now displays startup-errors (failure to load dynamic extensions, invalid INI configurations, etc) by default.
Assertions throw exceptions by default
PHP assertions (assert()
) now throw exceptions on assertion failures. Prior to PHP 8.0, it required an explicit configuration that was disabled by default.
@
Error Suppression operator does not silent fatal errors
PHP 8.0 corrects the behavior of @
error suppression operator, where it silenced fatal errors, which lead to a script failure, because the @
operator does not prevent fatal errors, but rather hide the error message display.
Resource to Object Migration
One of the long-term efforts in PHP development was to move away from the resource
types. They were difficult to deal with, and even in PHP 8.0, does not offer typing support.
PHP resource
objects do not play well with garbage-collector well either, which resulted in memory leaks in resource
objects such as xml
.
In PHP 8.0, some of the most used extensions changes moves away from the traditional resource
objects to standard PHP classes.
In PHP 8.0, they are work as value-objects as opposed to fully-features classes with methods in them. Majority of these classes do not allow instantiating with the new Foo()
construct either, and must be instantiated with the existing functions that returned resource
objects in prior versions.
PHP 8.0's resource
to object migration is quite seamless, as in all functions return and accept the new objects, and behave by the same semantics of the previous resource
objects.
Extension | resource (PHP < 8.0) |
object (PHP >= 8.0) |
---|---|---|
Curl | Curl |
CurlHandle |
Curl | curl_multi |
CurlMultiHandle |
Curl | curl_share |
CurlShareHandle |
GD | gd |
GdImage |
Sockets | Socket |
Socket |
Sockets | AddressInfo |
AddressInfo |
OpenSSL | OpenSSL key |
OpenSSLAsymmetricKey |
OpenSSL | OpenSSL X.509 |
OpenSSLCertificate |
OpenSSL | OpenSSL X.509 CSR |
OpenSSLCertificateSigningRequest |
XMLWriter | xmlwriter |
XMLWriter |
XML | xml |
XMLParser |
PHP Object Oriented Programming changes
PHP 8.0 is the first major version to be strict on Liskov Substitution Principle violations. Prior to PHP 8.0, PHP was not consistent on how it handled incompatible method signatures.
In PHP 8.0, all signature mismatches, including abstract traits, result in a fatal error. Further, it enforces signatures for PHP magic methods.
Fatal errors on incompatible method signatures
PHP 8.0 throws fatal errors when Liskov Substitution Principle is not followed when classes are extended, or interfaces are implemented.
Prior to PHP 8.0, incompatible signatures only emitted a warning.
class Foo {
public function process(stdClass $item): array{}
}
class SuperFoo extends Foo{
public function process(array $items): array{}
// ^^^^^ mismatch
}
Fatal error: Declaration of SuperFoo::process(array $items): array must be compatible with Foo::process(stdClass $item): array in ... on line ...
Class magic method signatures are strictly enforced
From PHP 8.0 and later, magic methods (e.g __toString()
, __get()
, etc), if they declare types, must implement the signature PHP expects. This is to avoid the smallest chance of the user declaring a magic method that doesn't follow the semantic meaning.
class Foo {
public function __toString(): object {
}
}
Declarations like Foo::__toString(): object
was allowed in previous PHP versions, but PHP 8.0 and throws an exception if the signature does not meet the requirements.
Calling non-static class methods statically result in a fatal error
PHP 8.0 no longer allows calling class methods as a static method.
class Foo {
public function bar() {}
}
Foo::bar();
Previous versions emitted a deprecation notice, but from PHP 8.0 and later, this results in a fatal error.
Inheritance rules are not applied to private
class methods
PHP 8.0 relaxes the signature, abstract
, and static
flag enforcement for private
class methods. This change comes from the rationale that private
methods are just that: Private.
From PHP 8.0, it is now allowed for the child classes to declare abstract methods, and change static/flags for private
methods.
::class
magic constant is now supported on objects
The ::class
magic constant returns the fully-qualified class name. This was only allowed on class names (such as Foo\Bar::class
), but in PHP 8.0, the ::class
magic constant works on instantiated objects too.
String-related changes
In PHP 8.0, there are several subtle changes that might be not obvious at first, but can result in quite unexpected results.
A major difference in PHP 8.0 is that, PHP now considers there is an empty string between every character in a given string.
Prior to PHP 8.0, checking for an empty string needle (""
) was not allowed, but in PHP 8.0, PHP will happily accept it, and return that there is indeed an empty string between each character.
The multi-byte handling, or functions like strlen
still returns same values as the older versions, but all functions that check for a substring in a given string are changed.
In addition, PHP 8.0 changes how the string concatenation operator priorities, and supports new modifiers in sprintf
functions such as %h
and %H
modifiers and *
width and precision modifier.
substr
, iconv_substr
, grapheme_substr
return empty string on out-of-bound offsets
These functions clamp the needle and offset parameters to the string length, and returns an empty string instead of returning false
.
substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
mb_substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
iconv_substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
grapheme_substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
+
/-
operators take higher precedence when used with concat (.
) operator
When the mathematical + and - operators are used in the same expression with the concatenation operator (.
), the + and - operators take higher precedence. This resulted in a deprecation notice in PHP versions prior to 8.0, but now it happens silently and as per the warning.
echo 35 + 7 . '.' . 0 + 5;
// 42.5 in PHP >= 8.0
// 47 in PHP <= 8.0
Locale-independent float
to string
casting
When a float
value is coerced to string
, PHP no longer uses the locale. Prior to PHP 8.0, PHP considered the current locale (which is not thread-safe) or system locale when doing so. This resulted in inconsistent string outputs because certain locales, specially European ones, swap the thousand separator and decimal sign from the US locale.
For locale-aware float
to string
cast, the %f
modifier in printf
class of functions can be used.
In addition, the new %h
and %H
modifiers for printf
class of functions provide locale-indepdent variants of %g
and %G
modifiers.
$thanks ❤
PHP 8.0 is an amazing effort from hundreds of awesome people. It's a major milesone in PHP's history. Thank you to everyone who helped from code, to documentation, to conferences, to articles to all of the developers.
New Features in PHP 8.0
- Named Parameters
- Attributes
- Class constructor property promotion
- JIT
- New
%h
and%H
printf
specifiers - Stack trace as string - Parameter max length is configurable
- New
*
precision and width modifiers inprintf
- New
Stringable
interface - Built-in web server supports dynamic port selection
- WeakMaps
- Null-safe operator
static
return type for class methods- New
get_resource_id
function - New
str_contains
function - New
fdiv
function - New
get_debug_type
function - New
preg_last_error_msg
function ::class
magic constant is now supported on objects- New
ValueError
Error Exception - New
PhpToken
Tokenizer class - New
str_starts_with
andstr_ends_with
functions - New
mixed
pseudo type - New
p
date format for UTCZ
time zone designation - Match Expressions
- New
DateTime/DateTimeImmutable::createFromInterface()
methods - Union Types
Syntax/Functionality Changes in PHP 8.0
- Default error reporting is set to
E_ALL
- Inheritance rules are not applied to
private
class methods - Calling non-static class methods statically result in a fatal error
- Apache Handler: Module name and file path changes
- Locale-independent
float
tostring
casting - Class magic method signatures are strictly enforced
substr
,iconv_substr
,grapheme_substr
return empty string on out-of-bound offsets- PHP Startup Errors are displayed by default
- GD Extension: Windows DLL file name changed from
php_gd2.dll
tophp_gd.dll
crypt()
function requires$salt
parameter- PDO: Default error mode set to exceptions
@
Error Suppression operator does not silent fatal errors- Trailing commas are allowed in parameter lists and closure
use
lists - Implicit negative array key increments do not skip negative numbers
XMLWriter
objects replacexmlwriter
resources- OpenSSL:
resource
to object migration XMLParser
objects replacexml
resources- String to number comparisons no longer coerce string to a number
- Strict type checks on arithmetic operators
- Sorting functions maintain positions of equal value items
phar://
stream wrapper no longerunserialize
s meta data automatically- Internal function warnings now throw
TypeError
andValueError
exceptions - Expressions can now
throw
Exceptions - JSON extension is always available
catch
exceptions only by type+
/-
operators take higher precedence when used with concat (.
) operatorCurlHandle
class objects replace curl handlers- Fatal errors on incompatible method signatures
- Disabled functions behave as if they do not exist
GdImage
class objects replace GD image resources- Assertions throw exceptions by default
- Sockets extension resources (
Socket
andAddressInfo
) are class objects
Deprecations in PHP 8.0
- PostgreSQL: Several aliased functions are deprecated
- Deprecate required parameters after optional parameters in function/method signatures
ReflectionParameter::getClass())
,::isArray()
, and::isCallable()
methods deprecated- Disabled functions: Reflection and
get_defined_functions()
deprecations libxml_disable_entity_loader
function is deprecated