What's new in PHP 8: New Features and Improvements
PHP 8.0 is a major update of the PHP language. It contains a range of new features and optimizations such as the JIT compiler, union types, attributes, constructor property promotion, match expression, nullsafe operator, JIT, and improvements in the type system, error handling, and more.
Union Types (RFC)
A union type accepts values of multiple different types, instead of a single one. PHP is a dynamically typed structure, So union types are extremely helpful in many places.
The syntax for Union types are T1|T2|...
(eg. int|float $price
).
In PHP 7.x
// in PHP7.x
class Order {
/**
* @var int|float $total
*/
private $total;
/**
* @param int|float $total
*/
public function setTotal($total) {
$this->total = $total;
}
/**
* @return int|float
*/
public function getTotal() {
return $this->total;
}
}
In PHP 8
//php8
class Order {
private int|float $total;
public function setTotal(int|float $total) : void {
$this->total = $total;
}
public function getTotal() : int|float {
return $this->total;
}
}
// $order = new Order();
// $order->setTotal(100);
// var_dump($order->getTotal());
WeakMap (RFC)
Weak maps allow creating a map from objects to arbitrary values (similar to SplObjectStorage) without preventing the objects that are used as keys from being garbage collected. If an object key is garbage collected, it will simply be removed from the map.
It is utilized for storing references. It permits us to make a map from objects to arbitrary values without preventing the objects that are utilized as keys from being garbage collected.
In long-running processes, this would prevent memory leaks and improve execution.
Here is an example.
<?php
class FooBar {
public WeakMap $cache;
public function __construct() {
$this->cache = new WeakMap();
}
public function getSomethingWithCaching(object $obj) {
return $this->cache[$obj] ??= $this->computeSomethingExpensive($obj);
}
public function computeSomethingExpensive(object $obj) {
dump("I got called");
return rand(1, 100);
}
}
$cacheObject = new stdClass;
$obj = new FooBar;
// "I got called" only will be printed once
$obj->getSomethingWithCaching($cacheObject);
$obj->getSomethingWithCaching($cacheObject);
dump(count($obj->cache));
// When unsetting our object, the WeakMap frees up memory
unset($cacheObject);
dump(count($obj->cache));
exit;
New `static` return type (RFC)
Although self
return was already possible, static
was not a legitimate return type until PHP 8. It's a function that will be useful to numerous developers, thinking about the dynamically typed nature of PHP.
class Foo
{
public function test(): static
{
return new static();
}
}
New mixed type (RFC)
PHP 8 presents another type called mixed
that you can utilize. A type of mixed would be equivalent to array
|bool
|callable
|int
|float
|null
|object
|resource
|string
.
<?php
function debug_function(mixed ...$data) {
dump($data);
}
debug_function(1, 'string', []);
$object::class - Class name literal on object (RFC)
The $object::class
used to get the name of the class of a given object. $object::class
gives a similar outcome as get_class($object)
. If $object
isn't an object, it throws a TypeError
exception.
<?php
$object = new stdClass;
var_dump($object::class); // "stdClass"
$object = null;
var_dump($object::class); // TypeError
New `Stringable` interface (RFC)
The Stringable
interface can be utilized to type-hint whatever executes __toString()
. If a class has __toString()
, it auto-implements the interface behind scenes and there's definitely no need to manually implement it.
<?php
class Foo {
public function __toString() {
return 'I am a class';
}
}
$obj = new Foo;
dump($obj instanceof Stringable);
Abstract methods in traits improvements (RFC)
Traits support the use of abstract methods in order to impose requirements upon the exhibiting class.
trait Test {
abstract public function test(int $input): int;
}
class UsesTrait
{
use Test;
public function test($input)
{
return $input;
}
}
Throw expression (RFC)
The throw
statement can now be used to be utilized in spots where just expressions are permitted, similar to arrow functions, the coalesce operator, and the ternary/elvis operator.
$triggerError = fn () => throw new MyError();
$foo = $bar['offset'] ?? throw new OffsetDoesNotExist('offset');
Trailing Comma in Parameter List (RFC)
PHP 8 permits an optional trailing comma in the parameter list. This includes parameter lists for functions, methods, and closures.
public function(
string $parameterA,
int $parameterB,
Foo $objectfoo, // trailing comma
) {
// …
}
Catching exceptions without storing in a variable (RFC)
Before PHP 8, it required that captured the exception should be stored Variable, whether or not you utilized that variable or not.
// Before PHP 8
try {
// Something goes wrong
} catch (MySpecialException $exception) {
Log::error("Something went wrong");
}
You can now do this:
try {
// Something goes wrong
} catch (MySpecialException) {
Log::error("Something went wrong");
}
Attributes
Attributes are certainly the major change in PHP 8, and they can be somewhat tricky to comprehend from the start. In short, attributes permit you to add meta-data to PHP functions, parameters, classes, and so on. This meta-data would then be able to be retrieved programmatically - while in PHP 7 and lower, you would need to parse doclocks, attributes permit you to access to this data deeply integrated into PHP itself.
For example, let's say that you need to allow users to add a middleware to a controller class/method by utilizing an attribute.
<?php
// First, we need to define the attribute. An Attribute itself is just a plain PHP class, that is annotated as an Attribute itself.
#[Attribute]
class ApplyMiddleware
{
public array $middlware = [];
public function __construct(...$middleware) {
$this->middleware = $middleware;
}
}
// This adds the attribute to the MyController class, with the "auth" middleware as an argument.
#[ApplyMiddleware('auth')]
class MyController
{
public function index() {}
}
// We can then retrieve all ApplyMiddleware attributes on our class using reflection
// And read the given middleware arguments.
$reflectionClass = new ReflectionClass(MyController::class);
$attributes = $reflectionClass->getAttributes(ApplyMiddleware::class);
foreach ($attributes as $attribute) {
$middlewareAttribute = $attribute->newInstance();
dump($middlewareAttribute->middleware);
}
exit;
PHP 8's attributes actually consist of a number of RFCs:
Constructor Property promotion (RFC)
This RFC proposes to introduce a shorthand syntax, which permits combining the definition of properties and the constructor.
In PHP 7.x
class Money
{
public Currency $currency;
public int $amount;
public function __construct(
Currency $currency,
int $amount,
) {
$this->currency = $currency;
$this->amount = $amount;
}
}
Now in PHP 8:
class Money
{
public function __construct(
public Currency $currency,
public int $amount,
) {}
}
Match Expression (RFC)
The match
expression presented in PHP 8 is like the switch statement. The match
can return values, doesn't need lengthy statements like a switch
. It utilizes a strict type of comparison and doesn't do any sort of coercion.
<?php
echo match (1) {
0 => 'Foo',
1 => 'Bar',
2 => 'Baz',
};
Null Safe Operator (RFC)
Greatly improve the readability of the code, Instead of writing a few 'If statements' you can utilize the ?->
operator to write all those in just 1 line of code.
<?php
class User {
public function getAddress() {}
}
$user = new User();
$country = $user?->getAddress()?->country?->iso_code;
dump($country);
Named Arguments (RFC)
Named arguments allow passing arguments to a function based on the parameter name, rather than the parameter position. This makes the meaning of the argument self-documenting, makes the arguments order-independent, and allows skipping default values arbitrarily.
<?php
// Using positional arguments:
array_fill(0, 100, 50);
// Using named arguments:
array_fill(start_index: 0, num: 100, value: 50);
JIT Compiler (RFC)
The most interesting feature coming with PHP 8 is the Just-in-time (JIT) compiler.
PHP JIT is implemented as an almost independent part of
OPcache
. It may be enabled/disabled at PHP compile time and at run-time. When enabled, native code of PHP files is stored in an additional region of the OPcache shared memory and op_array->opcodes[].handler(s) keep pointers to the entry points of JIT-ed code. This approach doesn't require engine modification at all.
New PHP Functions
`str_contains()` function - RFC
str_contains checks if a string is contained in another string and returns a boolean value (true/false) whether or not the string was found.
Now, don't have to depend on strpos()
any longer to know whether a string contains another string.
Instead of doing this:
if (strpos('string with lots of words', 'words') !== false)
{
/* … */
}
You can now do this
if (str_contains('string with lots of words', 'words'))
{
/* … */
}
`str_starts_with()` and `str_ends_with()` functions - RFC
str_starts_with()
checks if a string begins with another string and returns a boolean value (true/false) whether it does.
str_starts_with('haystack', 'hay'); // true
str_ends_with()
checks if a string ends with another string and returns a boolean value (true/false) whether it does.
str_ends_with('haystack', 'stack'); // true
`get_debug_type()` function - RFC
This RFC proposes to add a new function get_debug_type
that will return the given type of a variable.
class Book {}
class Comments {}
$book = new Book();
// earlier
if(! ($book instanceof Comment)) {
echo 'Expected ' . Comment::class . ' but got the ' . (is_object($book) ? get_class($book) : gettype($book));
}
//in PHP8
if(! ($book instanceof Comment)) {
echo 'Expected ' . Comment::class . ' but got the ' . get_debug_type($book);;
}
Conclusions
A lot of work has gone into the release of PHP 8. And the PHP 8 is a sure step towards cleaning up the language and a delivery that brings some long-awaited features.
For a full list of changes or to get some context on why something was introduced, be sure to look at the implemented RFCs for PHP 8.
Please login or create new account to add your comment.