# Arrays
# Destructuring 7.1
You can destructure arrays to pull out several elements into separate variables:$array = [1, 2, 3]; // Using the list syntax: list($a, $b, $c) = $array; // Or the shorthand syntax: [$a, $b, $c] = $array; You can skip elements: [, , $c] = $array; // $c = 3 As well as destructure based on keys: $array = [ 'a' => 1, 'b' => 2, 'c' => 3, ]; ['c' => $c, 'a' => $a] = $array; # Rest and Spread Operators 5.6
Arrays can be spread into functions:$array = [1, 2]; function foo(int $a, int $b) { /* … */ } foo(...$array); Functions can automatically collect the rest of the variables using the same operator: function foo($first, ...$other) { /* … */ } foo('a', 'b', 'c', 'd', …); Rest parameters can even be type hinted: function foo($first, string ...$other) { /* … */ } foo('a', 'b', 'c', 'd', …); $a = [1, 2]; $b = [3, 4]; $result = [...$a, ...$b]; // [1, 2, 3, 4] # Attributes 8.0
Make your own by tagging a class with#[Attribute] #[Attribute] class ListensTo { public string $event; public function __construct(string $event) { $this->event = $event; } } You can add them to properties, (anonymous) classes, functions, constants, closures, function arguments: #[ Route(Http::POST, '/products/create'), Autowire, ] class ProductsCreateController { public function __invoke() { /* … */ } } Use reflection to get them, you can pass in optional arguments to `$attributes = $reflectionClass->getAttributes( ContainerAttribute::class, ReflectionAttribute::IS_INSTANCEOF ); # Closures
# Short Closures 7.4
Short closures have automatic access to the outer scope, and only allow a single expression which is automatically returned:
array_map( fn($x) => $x * $this->modifier, $numbers ); # First class callables 8.1
You can now make a closure from a callable by calling that callable and passing it '...' as its argument
function foo(int $a, int $b) { /* … */ } $foo = foo(...); $foo(a: 1, b: 2); # Cosmetics
# Class Names 8.0
As of PHP 8, you can use ::class on objects as well:
Order::class; $object::class; # Numeric Values 7.4
Use the _ operator to format numeric values:
$price = 100_10; // $100 and 10 cents # Trailing Commas 8.0
Trailing commas are allowed in several places:
- Arrays
- Function calls
- Function definitions
- Closure
usestatements
# Enums
# Declaring Enums 8.1
Enums are built-in in the language:enum Status { case DRAFT; case PUBLISHED; case ARCHIVED; } # Enum methods 8.1
Enums can have methods, as well as have a string or integer value per case:enum Status: int { case DRAFT = 1; case PUBLISHED = 2; case ARCHIVED = 3; public function color(): string { return match($this) { Status::DRAFT => 'grey', Status::PUBLISHED => 'green', Status::ARCHIVED => 'red', }; } } # Exceptions 8.0
Throwing an exception is an expression now, which means there are more places you can throw from, such as short closures or as a null coalescing fallback:
$error = fn($message) => throw new Error($message); $input = $data['input'] ?? throw new Exception('Input not set'); You also don't have to catch an exception with a variable anymore:
try { // … } catch (SpecificException) { throw new OtherException(); } # Match 8.0
Similar to switch, but with strong type checks, no break keyword, combined arms and it returns a value:
$message = match ($statusCode) { 200, 300 => null, 400 => 'not found', 500 => 'server error', default => 'unknown status code', }; # Dealing with null
# Null Coalescing 7.0
Use the null coalescing operator to provide a fallback when a property is null:
$paymentDate = $invoice->paymentDate ?? Date::now(); It also works nested:
$input = $data['few']['levels']['deep'] ?? 'foo'; null: $temporaryPaymentDate = $invoice->paymentDate ??= Date::now(); // $invoice->paymentDate is now also set # Nullsafe Operator 8.0
Chain methods that possibly return null:
$invoice ->getPaymentDate() ?->format('Y-m-d'); The nullsafe operator can also be chained multiple times:
$object ?->methodA() ?->methodB() ?->methodC(); # Named Arguments 8.0
Pass in arguments by name instead of their position:
setcookie( name: 'test', expires: time() + 60 * 60 * 2, ); Named arguments also support array spreading:
$data = [ 'name' => 'test', 'expires' => time() + 60 * 60 * 2, ]; setcookie(...$data); # Performance
# The JIT 8.0
Enable the JIT by specifying a buffer size in your ini settings; you can switch the jit mode between function or tracing:
opcache.jit_buffer_size=100M opcache.jit=function ; opcache.jit=tracing # Preloading 7.4
Add opcache.preload to your ini settings:
opcache.preload=/path/to/project/preload.php Every file that's loaded in the preload script will be preloaded into memory until server restart.
# Properties
# Property Promotion 8.0
Prefix constructor arguments with public, protected or private to make them promoted:
class CustomerDTO { public function __construct( public string $name, public string $email, public DateTimeImmutable $birth_date, ) {} } You can still add a constructor body, and combine both promoted and non-promoted properties:
class MyClass { public string $b; public function __construct( public string $a, string $b, ) { assert($this->a !== 'foo'); $this->b = $b; } } # Using new in initializers 8.1
PHP 8.2 allows you to use the new keyword in function definitions as a default parameter, as well as in attribute arguments and other places.class MyController { public function __construct( private Logger $logger = new NullLogger(), ) {} } This also means that nested attributes are a thing now: #[Assert\All( new Assert\NotNull, new Assert\Length(max: 6), )] # Read only properties 8.1
Properties can be marked readonly:class Offer { public readonly ?string $offerNumber = null; public readonly Money $totalPrice; } Once a readonly property is set, it cannot change ever again. readonly class Offer { public ?string $offerNumber = null; public Money $totalPrice; } All properties of a class will become readonly, adding properties dynamically is impossible. # Types
# Built-in Types
During the PHP 7.x releases and with PHP 8, several new built-in types were added:
-
void: a return type indicating nothing's returned 7.1 -
static: a return type representing the current class or its children 8.0 -
object: anything that is an object 7.2 -
mixed: anything 8.0
# Union Types 8.0
Combine several types into one union, which means that whatever input must match one of the given types:
interface Repository { public function find(int|string $id); } # Typed properties 7.4
Add types to your class properties:
class Offer { public ?string $offerNumber = null; public Money $totalPrice; } Beware of the uninitialized state, which is only checked when reading a property, and not when constructing the class:
$offer = new Offer(); $offer->totalPrice; // Error # Intersection Types 8.1
Combine several types into one intersection, which means that whatever input must match all of the given types
public function url(WithId&WithSlug $obj): string; # DNF Types 8.2
You can create a union of intersection types, useful for creating a nullable intersection type:
public function url((WithId&WithSlug)|null $obj): string;