Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

For a long time, those of us writing PHP handled "types" of things using constants or just plain old strings. You've probably seen it in your own code: if ($status === 'pending'). The problem is that strings are loose. One typo—maybe you write 'pendnig' in one file and 'pending' in another—and suddenly you have a bug that's a nightmare to track down because the code is technically "valid," it's just logically wrong.

That's where Enums (Enumerations) come in. They allow us to define a fixed set of possible values that the language actually understands as a specific type. Let's build a small order tracking system to see how this works in practice.

Replacing strings with a Pure Enum

I want to make sure an order can only ever be in one of four states: Pending, Processing, Shipped, or Cancelled. Instead of relying on a developer to remember the exact spelling of those strings, I'll create a Pure Enum.

enum OrderStatus 
{
    case Pending;
    case Processing;
    case Shipped;
    case Cancelled;
}

Now, I can type-hint this in my functions. If I have a function that updates an order, I don't just ask for a string $status; I ask for an OrderStatus $status. If someone tries to pass a random string or an invalid Enum case, PHP will throw a TypeError immediately. This is a huge win for stability.

function updateOrderStatus(int $orderId, OrderStatus $status) {
    echo "Updating order $orderId to " . $status->name;
}

// This works perfectly
updateOrderStatus(123, OrderStatus::Shipped);

// This would throw a TypeError
// updateOrderStatus(123, 'shipped'); 

The mistake: Trying to save a Pure Enum to a database

Here is where I usually trip up when I first start using Enums in a new project. I'll write my logic, and then I'll try to save that status to a database column. I'll do something like this:

$status = OrderStatus::Processing;
$db->execute("UPDATE orders SET status = '$status' WHERE id = 123");

This will fail. Why? Because OrderStatus::Processing isn't a string; it's an object of the OrderStatus class. PHP will try to cast it to a string, and you'll get a fatal error because the Enum doesn't have a __toString() method. I forgot that Pure Enums don't have a scalar value associated with them.

Fixing it with Backed Enums

To fix this, I need a "Backed Enum." This allows me to associate each case with a scalar value (either a string or an integer) that can be stored in a database or sent in a JSON response.

enum OrderStatus: string 
{
    case Pending = 'p';
    case Processing = 'pr';
    case Shipped = 's';
    case Cancelled = 'c';
}

Now, I can access the underlying string using the ->value property. This gives me the best of both worlds: type safety in my PHP logic, and a compact string for my database.

$status = OrderStatus::Processing;
// Now I use ->value to get 'pr'
$db->execute("UPDATE orders SET status = '{$status->value}' WHERE id = 123");

If I'm pulling data out of the database and want to turn that 'pr' string back into a real Enum object, I use the from() method:

$dbValue = 'pr'; 
$status = OrderStatus::from($dbValue); 
// $status is now OrderStatus::Processing

Adding logic directly to the Enum

One of the things I love about PHP Enums is that they aren't just lists; they can have methods. Instead of writing a helper function elsewhere to check if an order is "finished," I'll put that logic inside the Enum itself.

enum OrderStatus: string 
{
    case Pending = 'p';
    case Processing = 'pr';
    case Shipped = 's';
    case Cancelled = 'c';

    public function isFinal(): bool 
    {
        return match($this) {
            self::Shipped, self::Cancelled => true,
            default => false,
        };
    }
}

$currentStatus = OrderStatus::Shipped;
if ($currentStatus->isFinal()) {
    echo "No more changes allowed to this order.";
}

Using match($this) inside an Enum method is a very clean pattern. It forces you to handle the cases explicitly, and it keeps your business logic right next to the data it operates on.




📋 Practical Task

Exercise: Implementing a User Role Permission System

You are building a simple permission system. Create a Backed Enum called UserRole that uses integers as its values. Include the following roles: Admin (1), Editor (2), and Viewer (3).

Inside the UserRole enum, add a method called canEditPost(). This method should return true if the role is Admin or Editor, and false if it is a Viewer.

Finally, write a small script that:

  1. Instantiates a variable with the Viewer role.
  2. Calls canEditPost() and prints whether the user is allowed to edit or not.
  3. Demonstrates how to create an Enum instance from the integer 1 using the from() method.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.