PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
30: Enums in PHP
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:
- Instantiates a variable with the
Viewerrole. - Calls
canEditPost()and prints whether the user is allowed to edit or not. - Demonstrates how to create an Enum instance from the integer
1using thefrom()method.
There are no comments for now.