Readonly properties

class PostData
{
    public function __construct(
        public readonly string $title,
        public readonly string $author,
        public readonly string $body,
        public readonly DateTimeImmutable $createdAt,
        public readonly PostState $state,
    ) {}
}

Constructor property promotion

PHP ≥ 8

class Class
{
	private int $x;
	public ?float $y;
	
	function __construct(
        int $x = 23,
        ?float $y = null
	) {
        $this->x = $x;
        $this->y = $y;
	}
}

Can be simplified to:

class Class
{
    function __construct(
        private int $x = 23,
        public ?float $y = null
    ) { }
}

__toString

class Car
{
    public function __construct(
        private string name,
        private int maxSpeed
    ) {}
    
    public function __toString()
    {
        return "The car ".$this->name." drives at max ".$this->maxSpeed." km/h";
    }
}
 
$car = new Car("Peugeot", 210);
echo $car; // The car Peugeot drives at max 210 km/h

Class function chaining

class Transaction
{
private int $amount;
 
    function __construct()
    {
        $this->amount = 100;
    }
    
    function add(int $value)
    {
        $this->amount += $value;
        return this;
    }
    
    function remove(int $value)
    {
        $this->amount -= $value;
        return this;
    }
}
$transaction = new Transaction();
 
$transaction->add(40)->remove(90);

Static keyword

function count()
{
    static $i = 0;
    return $i++;
}
 
echo count(); // 1
echo count(); // 2
echo count(); // 3

Class constants

Recommended: Always define access modifiers

class Transaction 
{
    public const STATUS_PAID = "paid";
    
    public function __construct() 
    {
        echo self::STATUS_PAID;
    }
}
 
echo Transaction::STATUS_PAID;
 
$transaction = new Transaction();

Magic Class Constant ::class

Resolves the fully qualified class name

namespace foo 
{
    class bar 
    {
        // ...
    }
    
    echo bar::class; // foo\bar
}

Static properties

class Transaction 
{
    static int $count = 0;
    
    public function __construct()
    {
        $self::count++;
    }
}
 
$transaction = new Transaction();
$transaction = new Transaction();
$transaction = new Transaction();
 
echo Transaction::$count; // 3

Clone Classes

$obj1 = new Class();
 
$obj2 = clone $obj1;

Patterns

Enums

Enumerations are a restricting layer on top of classes and class constants, intended to provide a way to define a closed set of possible values for a type.

enum InvoiceStatus: int
{
    case Pending = 0;
    case Paid = 1;
    case Failed = 2;
}

Interfaces

Interfaces are like abstract classes They can be implemented like classes can be extended Interfaces can extend other interfaces Classes can implement more than one interface, separated by comma

interface Collector
{
    public function collect($amount);
}
 
class CollectingAgency implements Collector
{
    public function collect($amount)
    {
        // ...
    }
}

Traits

Like classes, can be extended and provide functionality.

However, are not valid classes itself

trait Hello {
    public function sayHello() {
        echo 'Hallo ';
    }
}
    
trait World {
    public function sayWorld() {
        echo ' Welt';
    }
}
    
class MyHelloWorld {
    use Hello, World;
 
    public function sayExclamationMark() {
        echo '!';
    }
}

Namespaces

Namespace aliasing

use Namespace\To\The\Thing as NewThing;
new NewThing(); // works

Importing multiple things from one namespace

use Namespace\To\More\Things\{Thing1, Thing2};
// Or just import the whole namespace
use Namespace\To\More\Things;