Claude-skill-registry developing-with-php
Modern PHP 8.x development with type system, attributes, enums, error handling, and Composer. Use when writing PHP code or working with PHP projects.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/developing-with-php" ~/.claude/skills/majiayu000-claude-skill-registry-developing-with-php && rm -rf "$T"
skills/data/developing-with-php/SKILL.mdPHP Skill - Quick Reference
Core PHP language patterns for modern development. For Laravel-specific patterns, see the Laravel skill.
Progressive Disclosure: This is the quick reference. See REFERENCE.md for comprehensive patterns, advanced examples, and deep-dives.
Table of Contents
- PHP 8.x Type System
- Constructor Property Promotion
- Enums
- Match Expression & Named Arguments
- Attributes
- Null Safety
- OOP Essentials
- Handler/Service Pattern
- Map/DTO Pattern
- PDO Database Access
- Composer & Autoloading
- Error Handling
- Testing with PHPUnit
- Array Operations Quick Reference
- Security Essentials
- Quick Reference Tables
- Related Resources
PHP 8.x Type System
<?php // Union types (8.0+) function process(string|int $value): string|false { } // Intersection types (8.1+) function handle(Countable&Iterator $collection): void { } // Nullable types function find(int $id): ?User { } // Never return type (8.1+) function fail(): never { throw new Exception('Fatal error'); } // True, false, null as standalone types (8.2+) function isValid(): true { return true; }
Constructor Property Promotion
<?php // Promoted properties (8.0+) - replaces boilerplate class User { public function __construct( private string $name, private string $email, private bool $active = true, ) {} } // Readonly class (8.2+) - all properties are readonly readonly class ValueObject { public function __construct( public string $value, public DateTimeImmutable $createdAt, ) {} }
Enums
<?php // Backed enum with values (8.1+) enum Status: string { case Draft = 'draft'; case Published = 'published'; case Archived = 'archived'; public function label(): string { return match($this) { self::Draft => 'Draft', self::Published => 'Published', self::Archived => 'Archived', }; } } // Usage $status = Status::Published; $value = $status->value; // 'published' $all = Status::cases(); // Array of all cases $fromValue = Status::from('draft'); // Status::Draft $tryFrom = Status::tryFrom('invalid'); // null (no exception)
See REFERENCE.md for EnumTrait pattern and advanced enum usage.
Match Expression & Named Arguments
<?php // Match returns value, uses strict comparison $result = match($status) { Status::Draft => 'Not ready', Status::Published => 'Live', Status::Archived => 'Hidden', }; // Multiple conditions $category = match($code) { 200, 201, 204 => 'success', 400, 422 => 'client_error', 500, 502, 503 => 'server_error', default => 'unknown', }; // Named arguments (skip defaults, any order) $user = createUser( email: 'john@example.com', name: 'John Doe', role: 'admin', );
Attributes
<?php #[Attribute] class Route { public function __construct( public string $path, public string $method = 'GET', ) {} } class UserController { #[Route('/users', 'GET')] public function index(): array { } } // Reading attributes via reflection $reflection = new ReflectionMethod(UserController::class, 'index'); $attributes = $reflection->getAttributes(Route::class); $route = $attributes[0]->newInstance(); echo $route->path; // '/users'
Null Safety
<?php // Null coalescing $name = $user->name ?? 'Anonymous'; // Null coalescing assignment $data['count'] ??= 0; // Nullsafe operator (8.0+) $country = $user?->address?->country?->name; // Combined $timezone = $user?->settings?->timezone ?? 'UTC';
OOP Essentials
Interfaces & Abstract Classes
<?php interface RepositoryInterface { public function find(int $id): ?object; public function save(object $entity): void; } abstract class BaseHandler { public function __construct(protected PDO $db) {} abstract public function handle(array $data): mixed; }
Traits
<?php trait Timestamps { protected ?DateTimeImmutable $createdAt = null; public function setCreatedAt(): void { $this->createdAt = new DateTimeImmutable(); } } class User { use Timestamps; }
See REFERENCE.md for trait conflict resolution, late static binding, and magic methods.
Handler/Service Pattern
<?php class UserHandler { public function __construct( private readonly PDO $db, private readonly CacheInterface $cache, ) {} public function get(int $id): ?array { $cacheKey = "user:{$id}"; if ($cached = $this->cache->get($cacheKey)) { return $cached; } $stmt = $this->db->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$id]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if ($user) { $this->cache->set($cacheKey, $user, 3600); } return $user ?: null; } }
See REFERENCE.md for CRUD handlers, aggregation patterns, and collection mapping.
Map/DTO Pattern
<?php readonly class UserMap { public function __construct( public int $id, public string $name, public string $email, ) {} public static function fromRow(array $row): self { return new self( id: (int) $row['id'], name: trim($row['first_name'] . ' ' . $row['last_name']), email: $row['email'], ); } public function toArray(): array { return ['id' => $this->id, 'name' => $this->name, 'email' => $this->email]; } }
See REFERENCE.md for nested mapping and collection patterns.
PDO Database Access
Connection
<?php $dsn = 'mysql:host=localhost;dbname=myapp;charset=utf8mb4'; $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; $pdo = new PDO($dsn, 'username', 'password', $options);
Prepared Statements
<?php // Named parameters $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email'); $stmt->execute(['email' => $email]); $user = $stmt->fetch(); // Positional parameters $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$id]);
Transactions
<?php try { $pdo->beginTransaction(); // Multiple operations... $pdo->commit(); } catch (Exception $e) { $pdo->rollBack(); throw $e; }
See REFERENCE.md for stored procedures, batch operations, and SQL file execution.
Composer & Autoloading
composer.json Essentials
{ "require": { "php": "^8.2" }, "autoload": { "psr-4": { "App\\": "src/" } }, "autoload-dev": { "psr-4": { "Tests\\": "tests/" } } }
Version Constraints
| Constraint | Meaning |
|---|---|
| |
| |
| |
Error Handling
Exception Pattern
<?php class AppException extends Exception { public function __construct( string $message, int $code = 0, public readonly array $context = [], ) { parent::__construct($message, $code); } } class NotFoundException extends AppException { public function __construct(string $resource, int|string $id) { parent::__construct("{$resource} not found", 404, ['id' => $id]); } }
Exception Handling
<?php try { $user = $handler->findOrFail($id); } catch (NotFoundException $e) { return ['error' => $e->getMessage(), 'code' => 404]; } catch (AppException $e) { $this->logger->error($e->getMessage(), $e->context); return ['error' => 'An error occurred', 'code' => 500]; }
See REFERENCE.md for global exception handlers and validation exceptions.
Testing with PHPUnit
<?php use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\Test; class UserHandlerTest extends TestCase { private UserHandler $handler; protected function setUp(): void { $pdo = new PDO('sqlite::memory:'); $this->handler = new UserHandler($pdo); } #[Test] public function it_creates_a_user(): void { $id = $this->handler->create(['name' => 'John', 'email' => 'john@example.com']); $this->assertIsInt($id); $this->assertGreaterThan(0, $id); } }
See REFERENCE.md for data providers, mocking, and integration testing.
Array Operations Quick Reference
<?php // Mapping $names = array_map(fn($u) => $u['name'], $users); // Filtering $active = array_filter($users, fn($u) => $u['active']); // Reducing $total = array_reduce($items, fn($sum, $i) => $sum + $i['price'], 0); // Column extraction $emails = array_column($users, 'email'); $byId = array_column($users, null, 'id'); // Index by 'id' // Sorting usort($users, fn($a, $b) => $a['name'] <=> $b['name']);
See REFERENCE.md for generators and advanced collection patterns.
Security Essentials
Password Hashing
<?php $hash = password_hash($password, PASSWORD_DEFAULT); if (password_verify($password, $hash)) { /* valid */ }
SQL Injection Prevention
<?php // NEVER: $sql = "SELECT * FROM users WHERE id = {$_GET['id']}"; // ALWAYS: Use prepared statements $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$_GET['id']]);
Input Validation
<?php $email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL); $cleanHtml = htmlspecialchars($input, ENT_QUOTES | ENT_HTML5, 'UTF-8');
See REFERENCE.md for CSRF protection and comprehensive security patterns.
Quick Reference Tables
Type Declarations
| Type | PHP Version | Example |
|---|---|---|
(nullable) | 7.1+ | |
| 7.1+ | |
| 8.0+ | |
(union) | 8.0+ | |
| 8.1+ | |
(intersection) | 8.1+ | |
, , | 8.2+ | |
PDO Fetch Modes
| Mode | Returns |
|---|---|
| Associative array |
| stdClass object |
| Instance of specified class |
Related Resources
- REFERENCE.md - Comprehensive patterns and advanced examples
- examples/ - Real-world code examples
- templates/ - Boilerplate templates
- Laravel Skill - Laravel-specific patterns