Back to list
FortiumPartners

developing-with-php

by FortiumPartners

Ensemble Plugin Ecosystem - Modular Claude Code plugins for AI-augmented development workflows

0🍴 1📅 Jan 22, 2026

SKILL.md


name: developing-with-php description: Modern PHP 8.x development with type system, attributes, enums, error handling, and Composer. Use when writing PHP code or working with PHP projects.

PHP 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

  1. PHP 8.x Type System
  2. Constructor Property Promotion
  3. Enums
  4. Match Expression & Named Arguments
  5. Attributes
  6. Null Safety
  7. OOP Essentials
  8. Handler/Service Pattern
  9. Map/DTO Pattern
  10. PDO Database Access
  11. Composer & Autoloading
  12. Error Handling
  13. Testing with PHPUnit
  14. Array Operations Quick Reference
  15. Security Essentials
  16. Quick Reference Tables
  17. 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

ConstraintMeaning
^1.0>=1.0.0 <2.0.0
~1.2>=1.2.0 <1.3.0
1.0.*>=1.0.0 <1.1.0

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

TypePHP VersionExample
?Type (nullable)7.1+?int
void7.1+function f(): void
mixed8.0+function f(mixed $x)
Type1|Type2 (union)8.0+int|string
never8.1+function f(): never
Type1&Type2 (intersection)8.1+A&B
true, false, null8.2+function f(): true

PDO Fetch Modes

ModeReturns
PDO::FETCH_ASSOCAssociative array
PDO::FETCH_OBJstdClass object
PDO::FETCH_CLASSInstance of specified class

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon