Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Modern PHP 8.3+ development with strict typing, PHPStan level 9, PSR standards, and Laravel/Symfony support.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/modern-php-features.md
1# Modern PHP 8.3+ Features23## Strict Types & Type Declarations45```php6<?php78declare(strict_types=1);910namespace App\Domain\User;1112final readonly class User13{14public function __construct(15public int $id,16public string $email,17public UserStatus $status,18public \DateTimeImmutable $createdAt,19) {}20}2122function calculateTotal(int $price, float $taxRate): float23{24return $price * (1 + $taxRate);25}2627// Union types28function processId(int|string $id): string29{30return is_int($id) ? (string)$id : $id;31}3233// Intersection types34interface Timestamped {}35interface Authenticatable {}3637function handleUser(Timestamped&Authenticatable $user): void {}38```3940## Enums with Methods4142```php43<?php4445declare(strict_types=1);4647enum UserStatus: string48{49case ACTIVE = 'active';50case SUSPENDED = 'suspended';51case DELETED = 'deleted';5253public function label(): string54{55return match($this) {56self::ACTIVE => 'Active User',57self::SUSPENDED => 'Suspended',58self::DELETED => 'Deleted User',59};60}6162public function canLogin(): bool63{64return $this === self::ACTIVE;65}6667public static function fromString(string $value): self68{69return self::from(strtolower($value));70}71}7273enum HttpStatus: int74{75case OK = 200;76case CREATED = 201;77case BAD_REQUEST = 400;78case UNAUTHORIZED = 401;79case NOT_FOUND = 404;80case SERVER_ERROR = 500;8182public function isSuccess(): bool83{84return $this->value >= 200 && $this->value < 300;85}86}87```8889## Readonly Properties & Classes9091```php92<?php9394declare(strict_types=1);9596// Readonly class (PHP 8.2+)97final readonly class Money98{99public function __construct(100public int $amount,101public string $currency,102) {103if ($amount < 0) {104throw new \InvalidArgumentException('Amount cannot be negative');105}106}107108public function add(Money $other): self109{110if ($this->currency !== $other->currency) {111throw new \InvalidArgumentException('Currency mismatch');112}113return new self($this->amount + $other->amount, $this->currency);114}115}116117// Individual readonly properties118class Configuration119{120public function __construct(121public readonly string $apiKey,122public readonly string $apiSecret,123private string $cache = '',124) {}125}126```127128## Attributes (Metadata)129130```php131<?php132133declare(strict_types=1);134135#[\Attribute(\Attribute::TARGET_CLASS)]136final readonly class Route137{138public function __construct(139public string $path,140public string $method = 'GET',141public array $middleware = [],142) {}143}144145#[\Attribute(\Attribute::TARGET_PROPERTY)]146final readonly class Validate147{148public function __construct(149public ?string $rule = null,150public ?int $min = null,151public ?int $max = null,152) {}153}154155// Using attributes156#[Route('/api/users', method: 'POST', middleware: ['auth'])]157final class CreateUserController158{159public function __invoke(CreateUserRequest $request): JsonResponse160{161// ...162}163}164165class UserDto166{167#[Validate(rule: 'email')]168public string $email;169170#[Validate(min: 8, max: 100)]171public string $password;172}173```174175## First-Class Callables176177```php178<?php179180declare(strict_types=1);181182class UserService183{184public function findById(int $id): ?User {}185public function create(array $data): User {}186}187188$service = new UserService();189190// PHP 8.1+ first-class callable syntax191$finder = $service->findById(...);192$user = $finder(42);193194// Array operations195$numbers = [1, 2, 3, 4, 5];196$doubled = array_map(fn($n) => $n * 2, $numbers);197198// Named arguments with callable199$result = array_filter(200array: $numbers,201callback: fn($n) => $n % 2 === 0,202);203```204205## Match Expressions206207```php208<?php209210declare(strict_types=1);211212function getStatusColor(UserStatus $status): string213{214return match ($status) {215UserStatus::ACTIVE => 'green',216UserStatus::SUSPENDED => 'yellow',217UserStatus::DELETED => 'red',218};219}220221function calculateShipping(int $weight, string $zone): float222{223return match (true) {224$weight < 1000 => 5.00,225$weight < 5000 && $zone === 'local' => 10.00,226$weight < 5000 => 15.00,227default => 25.00,228};229}230231// Match with multiple conditions232function getHttpMessage(int $code): string233{234return match ($code) {235200, 201, 204 => 'Success',236400, 422 => 'Client Error',237401, 403 => 'Unauthorized',238500, 502, 503 => 'Server Error',239default => 'Unknown',240};241}242```243244## Fibers (PHP 8.1+)245246```php247<?php248249declare(strict_types=1);250251// Basic fiber example252$fiber = new \Fiber(function (): void {253$value = \Fiber::suspend('fiber started');254echo "Received: {$value}\n";255\Fiber::suspend('second suspend');256echo "Fiber completed\n";257});258259$result1 = $fiber->start();260echo "First result: {$result1}\n";261262$result2 = $fiber->resume('data from main');263echo "Second result: {$result2}\n";264265$fiber->resume('final data');266267// Async-style with fibers268function async(callable $callback): \Fiber269{270return new \Fiber($callback);271}272273function await(\Fiber $fiber): mixed274{275if (!$fiber->isStarted()) {276return $fiber->start();277}278return $fiber->resume();279}280```281282## Never Type283284```php285<?php286287declare(strict_types=1);288289function redirect(string $url): never290{291header("Location: {$url}");292exit;293}294295function abort(int $code, string $message): never296{297http_response_code($code);298echo json_encode(['error' => $message]);299exit;300}301302class NotFoundException extends \Exception303{304public static function throw(string $resource): never305{306throw new self("Resource not found: {$resource}");307}308}309```310311## Quick Reference312313| Feature | PHP Version | Usage |314|---------|-------------|-------|315| Readonly properties | 8.1+ | `public readonly string $name` |316| Readonly classes | 8.2+ | `readonly class User {}` |317| Enums | 8.1+ | `enum Status: string {}` |318| First-class callables | 8.1+ | `$fn = $obj->method(...)` |319| Never type | 8.1+ | `function exit(): never` |320| Fibers | 8.1+ | `new \Fiber(fn() => ...)` |321| Pure intersection types | 8.1+ | `A&B $param` |322| DNF types | 8.2+ | `(A&B)\|C $param` |323| Constants in traits | 8.2+ | `trait T { const X = 1; }` |324