Modern PHP Cheat Sheet

Hacker News - Thu Nov 25 09:47

# 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;

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', …);

7.4 Arrays with numerical keys can also be spread into a new array:

$a = [1, 2];
$b = [3, 4];

$result = [...$a, ...$b]; 

8.1 You can also spread arrays with textual keys starting from PHP 8.1

# Attributes 8.0

Make your own by tagging a class with


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 `

getAttributes

` in order to filter the result:

$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;

# Trailing Commas 8.0

Trailing commas are allowed in several places:

  • Arrays
  • Function calls
  • Function definitions
  • Closure use statements

# 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';

7.4

You can use the null coalescing assignment operator to write the value into the original variable when it's null:

$temporaryPaymentDate = $invoice->paymentDate ??= Date::now();

# 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

# 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

# Using new in initializers 8.1

PHP 8.1 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.

# 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

# 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;

# 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; 

# 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);
}