Navigation

Php

How to Use PHP's Enums Effectively

Use PHP 8.1+ enums to create type-safe constants and status values. Replace class constants with powerful, feature-rich enumerations

Table Of Contents

Problem

You need to define a fixed set of values like statuses, types, or categories, but class constants are not type-safe and lack functionality.

Solution

// Basic enum
enum Status {
    case PENDING;
    case APPROVED;
    case REJECTED;
}

function processOrder(Status $status): string {
    return match ($status) {
        Status::PENDING => 'Order is waiting for approval',
        Status::APPROVED => 'Order has been approved',
        Status::REJECTED => 'Order was rejected'
    };
}

echo processOrder(Status::PENDING);   // Order is waiting for approval
echo processOrder(Status::APPROVED); // Order has been approved

// Backed enums (with values)
enum Priority: string {
    case LOW = 'low';
    case MEDIUM = 'medium';
    case HIGH = 'high';
    case URGENT = 'urgent';
}

enum HttpStatus: int {
    case OK = 200;
    case NOT_FOUND = 404;
    case INTERNAL_ERROR = 500;
    case BAD_REQUEST = 400;
}

// Using backed enum values
function createTask(string $title, Priority $priority): array {
    return [
        'title' => $title,
        'priority' => $priority->value,  // Get the backing value
        'created_at' => date('Y-m-d H:i:s')
    ];
}

$task = createTask('Fix bug', Priority::HIGH);
echo $task['priority']; // 'high'

// Creating enums from values
$priorityFromDb = Priority::from('medium');     // Returns Priority::MEDIUM
$httpStatus = HttpStatus::from(404);            // Returns HttpStatus::NOT_FOUND

// Safe creation with tryFrom
$maybePriority = Priority::tryFrom('invalid');  // Returns null
$validPriority = Priority::tryFrom('low');      // Returns Priority::LOW

// Enum with methods
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 => 'Pending Payment',
            self::PROCESSING => 'Being Processed',
            self::SHIPPED => 'Shipped',
            self::DELIVERED => 'Delivered',
            self::CANCELLED => 'Cancelled'
        };
    }
    
    public function canBeCancelled(): bool {
        return match ($this) {
            self::PENDING, self::PROCESSING => true,
            default => false
        };
    }
    
    public function nextStatus(): ?self {
        return match ($this) {
            self::PENDING => self::PROCESSING,
            self::PROCESSING => self::SHIPPED,
            self::SHIPPED => self::DELIVERED,
            default => null
        };
    }
}

$status = OrderStatus::PENDING;
echo $status->label();                // Pending Payment
echo $status->canBeCancelled() ? 'Yes' : 'No'; // Yes

$nextStatus = $status->nextStatus();
echo $nextStatus?->value;             // 'processing'

// Enum implementing interfaces
interface ColorInterface {
    public function hex(): string;
    public function rgb(): array;
}

enum Color: string implements ColorInterface {
    case RED = 'red';
    case GREEN = 'green';
    case BLUE = 'blue';
    case WHITE = 'white';
    case BLACK = 'black';
    
    public function hex(): string {
        return match ($this) {
            self::RED => '#FF0000',
            self::GREEN => '#00FF00',
            self::BLUE => '#0000FF',
            self::WHITE => '#FFFFFF',
            self::BLACK => '#000000'
        };
    }
    
    public function rgb(): array {
        return match ($this) {
            self::RED => [255, 0, 0],
            self::GREEN => [0, 255, 0],
            self::BLUE => [0, 0, 255],
            self::WHITE => [255, 255, 255],
            self::BLACK => [0, 0, 0]
        };
    }
    
    public static function fromHex(string $hex): ?self {
        foreach (self::cases() as $color) {
            if ($color->hex() === $hex) {
                return $color;
            }
        }
        return null;
    }
}

$color = Color::RED;
echo $color->hex();                   // #FF0000
print_r($color->rgb());               // [255, 0, 0]

// Permission system with enums
enum Permission: string {
    case READ = 'read';
    case WRITE = 'write';
    case DELETE = 'delete';
    case ADMIN = 'admin';
    
    public function implies(Permission $permission): bool {
        return match ($this) {
            self::ADMIN => true,                    // Admin can do everything
            self::WRITE => in_array($permission, [self::READ, self::WRITE]),
            self::READ => $permission === self::READ,
            self::DELETE => $permission === self::DELETE
        };
    }
    
    public static function getHierarchy(): array {
        return [
            self::ADMIN->value => [self::READ, self::WRITE, self::DELETE],
            self::WRITE->value => [self::READ],
            self::READ->value => [],
            self::DELETE->value => []
        ];
    }
}

function hasPermission(Permission $userPermission, Permission $requiredPermission): bool {
    return $userPermission->implies($requiredPermission);
}

// API response types
enum ResponseFormat: string {
    case JSON = 'json';
    case XML = 'xml';
    case CSV = 'csv';
    case PDF = 'pdf';
    
    public function contentType(): string {
        return match ($this) {
            self::JSON => 'application/json',
            self::XML => 'application/xml',
            self::CSV => 'text/csv',
            self::PDF => 'application/pdf'
        };
    }
    
    public function fileExtension(): string {
        return match ($this) {
            self::JSON => '.json',
            self::XML => '.xml',
            self::CSV => '.csv',
            self::PDF => '.pdf'
        };
    }
}

function generateResponse(array $data, ResponseFormat $format): string {
    return match ($format) {
        ResponseFormat::JSON => json_encode($data),
        ResponseFormat::XML => convertToXml($data),
        ResponseFormat::CSV => convertToCsv($data),
        ResponseFormat::PDF => generatePdf($data)
    };
}

function convertToXml(array $data): string {
    // Simple XML conversion
    return '<data>' . json_encode($data) . '</data>';
}

function convertToCsv(array $data): string {
    // Simple CSV conversion
    return implode(',', array_keys($data)) . "\n" . implode(',', array_values($data));
}

function generatePdf(array $data): string {
    // PDF generation placeholder
    return 'PDF content for: ' . json_encode($data);
}

// Database entity states
enum EntityState {
    case NEW;
    case MANAGED;
    case DETACHED;
    case REMOVED;
    
    public function canPersist(): bool {
        return in_array($this, [self::NEW, self::MANAGED]);
    }
    
    public function canDelete(): bool {
        return $this === self::MANAGED;
    }
}

class Entity {
    private EntityState $state = EntityState::NEW;
    
    public function getState(): EntityState {
        return $this->state;
    }
    
    public function setState(EntityState $state): void {
        $this->state = $state;
    }
    
    public function persist(): bool {
        if (!$this->state->canPersist()) {
            throw new InvalidArgumentException('Cannot persist entity in ' . $this->state->name . ' state');
        }
        
        // Persistence logic
        $this->state = EntityState::MANAGED;
        return true;
    }
}

// Configuration enums
enum Environment: string {
    case DEVELOPMENT = 'development';
    case TESTING = 'testing';
    case STAGING = 'staging';
    case PRODUCTION = 'production';
    
    public function isDebugEnabled(): bool {
        return in_array($this, [self::DEVELOPMENT, self::TESTING]);
    }
    
    public function getDatabaseConfig(): array {
        return match ($this) {
            self::DEVELOPMENT => [
                'host' => 'localhost',
                'database' => 'app_dev',
                'debug' => true
            ],
            self::TESTING => [
                'host' => 'localhost',
                'database' => 'app_test',
                'debug' => true
            ],
            self::STAGING => [
                'host' => 'staging-db.example.com',
                'database' => 'app_staging',
                'debug' => false
            ],
            self::PRODUCTION => [
                'host' => 'prod-db.example.com',
                'database' => 'app_prod',
                'debug' => false
            ]
        };
    }
}

// Validation with enums
enum ValidationType: string {
    case EMAIL = 'email';
    case URL = 'url';
    case NUMERIC = 'numeric';
    case REQUIRED = 'required';
    
    public function validate(mixed $value): bool {
        return match ($this) {
            self::EMAIL => filter_var($value, FILTER_VALIDATE_EMAIL) !== false,
            self::URL => filter_var($value, FILTER_VALIDATE_URL) !== false,
            self::NUMERIC => is_numeric($value),
            self::REQUIRED => !empty($value)
        };
    }
}

// Getting all enum cases
$allStatuses = OrderStatus::cases();
foreach ($allStatuses as $status) {
    echo $status->name . ': ' . $status->value . "\n";
}

// Enum in arrays and serialization
$statusArray = [
    'current' => OrderStatus::PROCESSING,
    'previous' => OrderStatus::PENDING
];

$serialized = json_encode([
    'status' => OrderStatus::SHIPPED->value,
    'priority' => Priority::HIGH->value
]);

// Type safety demonstration
function updateOrderStatus(OrderStatus $newStatus): void {
    // This function can only accept OrderStatus enum values
    echo "Order status updated to: " . $newStatus->label();
}

// updateOrderStatus('invalid');        // TypeError
// updateOrderStatus(Priority::HIGH);   // TypeError
updateOrderStatus(OrderStatus::SHIPPED); // Works correctly

Explanation

PHP enums provide type-safe constants with additional functionality. Use backed enums when you need specific values, and pure enums for simple categorization.

Enums can have methods, implement interfaces, and provide type safety that class constants cannot. Use cases() to get all enum values and from()/tryFrom() for safe value conversion.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Php