All skills
asyrafhussin avatar

/php-best-practices

@e7ea05d

PHP 8.x modern patterns, PSR standards, and SOLID principles. Use when reviewing PHP code, checking type safety, auditing code quality, or ensuring PHP best practices. Triggers on "review PHP", "check PHP code", "audit PHP", or "PHP best practices".

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/php-best-practices

This session only. Nothing lands on disk.

rulesmodern-enums.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Type-Safe Enums

Enums (PHP 8.1+) provide type-safe constants with methods. They prevent invalid values, enable IDE autocompletion, and encapsulate related behavior. Always prefer enums over class constants for finite sets of values.

Bad Example

<?php

// Class constants - no type safety
class OrderStatus
{
    public const PENDING = 'pending';
    public const PROCESSING = 'processing';
    public const SHIPPED = 'shipped';
    public const DELIVERED = 'delivered';
    public const CANCELLED = 'cancelled';
}

// Anyone can pass invalid value
function updateStatus(string $status): void
{
    // 'invalid_status' would be accepted
}

updateStatus('typo'); // No error!

// Constants scattered or duplicated
class Order
{
    public const STATUS_PENDING = 1;
    public const STATUS_ACTIVE = 2;
}

class Payment
{
    public const STATUS_PENDING = 1; // Duplicated
    public const STATUS_COMPLETED = 2;
}

Good Example

Basic Enum (Unit Enum)

<?php

declare(strict_types=1);

// Unit enum - no backing value
enum Direction
{
    case North;
    case South;
    case East;
    case West;

    public function opposite(): self
    {
        return match($this) {
            self::North => self::South,
            self::South => self::North,
            self::East => self::West,
            self::West => self::East,
        };
    }
}

$direction = Direction::North;
$opposite = $direction->opposite(); // Direction::South

Backed Enum (String or Int)

<?php

// String-backed enum - for database/API values
enum OrderStatus: string
{
    case Pending = 'pending';
    case Processing = 'processing';
    case Shipped = 'shipped';
    case Delivered = 'delivered';
    case Cancelled = 'cancelled';

    public function label(): string
    {
        return match($this) {
            self::Pending => 'Awaiting Processing',
            self::Processing => 'Being Prepared',
            self::Shipped => 'On the Way',
            self::Delivered => 'Delivered',
            self::Cancelled => 'Cancelled',
        };
    }

    public function color(): string
    {
        return match($this) {
            self::Pending => 'yellow',
            self::Processing => 'blue',
            self::Shipped => 'purple',
            self::Delivered => 'green',
            self::Cancelled => 'red',
        };
    }

    public function canTransitionTo(self $newStatus): bool
    {
        return match($this) {
            self::Pending => in_array($newStatus, [self::Processing, self::Cancelled]),
            self::Processing => in_array($newStatus, [self::Shipped, self::Cancelled]),
            self::Shipped => $newStatus === self::Delivered,
            self::Delivered, self::Cancelled => false,
        };
    }
}

// Usage
$status = OrderStatus::Pending;
$status->value;  // 'pending'
$status->name;   // 'Pending'
$status->label(); // 'Awaiting Processing'

// From database/API value
$status = OrderStatus::from('pending'); // OrderStatus::Pending
$status = OrderStatus::tryFrom('invalid'); // null (no exception)

Int-Backed Enum

<?php

// Int-backed enum - for legacy databases
enum Priority: int
{
    case Low = 1;
    case Medium = 2;
    case High = 3;
    case Critical = 4;

    public function isUrgent(): bool
    {
        return $this->value >= self::High->value;
    }
}

// Comparison
$priority = Priority::High;
if ($priority->value > Priority::Medium->value) {
    // Handle high priority
}

Enum with Interface

<?php

interface Labelable
{
    public function label(): string;
}

enum PaymentMethod: string implements Labelable
{
    case CreditCard = 'credit_card';
    case BankTransfer = 'bank_transfer';
    case PayPal = 'paypal';

    public function label(): string
    {
        return match($this) {
            self::CreditCard => 'Credit Card',
            self::BankTransfer => 'Bank Transfer',
            self::PayPal => 'PayPal',
        };
    }

    public function processingFee(): float
    {
        return match($this) {
            self::CreditCard => 0.029,
            self::BankTransfer => 0.01,
            self::PayPal => 0.034,
        };
    }
}

Enum with Traits

<?php

trait EnumHelpers
{
    /** Only works with backed enums (string/int) */
    public static function values(): array
    {
        return array_column(self::cases(), 'value');
    }

    public static function names(): array
    {
        return array_column(self::cases(), 'name');
    }

    public static function options(): array
    {
        return array_combine(
            array_column(self::cases(), 'value'),
            array_map(fn($case) => $case->label(), self::cases())
        );
    }
}

enum Role: string
{
    use EnumHelpers;

    case Admin = 'admin';
    case Editor = 'editor';
    case Viewer = 'viewer';

    public function label(): string
    {
        return match($this) {
            self::Admin => 'Administrator',
            self::Editor => 'Content Editor',
            self::Viewer => 'Read Only',
        };
    }

    public function permissions(): array
    {
        return match($this) {
            self::Admin => ['create', 'read', 'update', 'delete', 'manage'],
            self::Editor => ['create', 'read', 'update'],
            self::Viewer => ['read'],
        };
    }
}

// Usage
Role::values();  // ['admin', 'editor', 'viewer']
Role::options(); // ['admin' => 'Administrator', ...]

Type-Safe Function Parameters

<?php

// Function accepts only valid enum values
function updateOrderStatus(Order $order, OrderStatus $newStatus): void
{
    if (!$order->status->canTransitionTo($newStatus)) {
        throw new InvalidStatusTransitionException(
            $order->status,
            $newStatus
        );
    }

    $order->status = $newStatus;
}

// Type safety - invalid values rejected
updateOrderStatus($order, OrderStatus::Shipped); //
updateOrderStatus($order, 'shipped'); // TypeError

In Eloquent/Database

<?php

// Model with enum casting
class Order extends Model
{
    protected $casts = [
        'status' => OrderStatus::class,
        'priority' => Priority::class,
    ];
}

// Query with enum
Order::where('status', OrderStatus::Pending)->get();

// Validation rule
'status' => ['required', new Enum(OrderStatus::class)],

Why

  • Type Safety: Invalid values caught immediately (TypeError)
  • IDE Support: Autocompletion and refactoring support
  • Encapsulation: Related behavior lives with the data (methods)
  • Self-Documenting: Code clearly shows all valid values
  • Match Expressions: Natural pairing with exhaustive match
  • Database Integration: Backed enums map to DB values
  • Safe Conversion: from()/tryFrom() for converting from raw values

Source: SKILL.md on GitHub

No alerts17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill is a comprehensive and safe resource for PHP 8.x best practices. It provides structured guidance on type safety, modern PHP syntax, PSR standards, and secure coding practices (such as prepared statements and password hashing). The agent is instructed to use standard environment detection commands (php -v, grep) to tailor its advice. No malicious patterns or security risks were identified.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer6mo

    39 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at e7ea05d. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
Other metadata
metadata
{
  "author": "php-community",
  "version": "2.1.0",
  "phpVersion": "8.0 - 8.5"
}

README badge

README badge for asyrafhussin/agent-skills/php-best-practices