Enums (PHP 8.1)
Stop passing fragile magic strings like "published" around your app. PHP 8.1 enums give you a fixed, type-safe set of named values — with their own methods — so invalid states become impossible and your editor can autocomplete every option.
Learn Enums (PHP 8.1) in our free PHP course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick recall.
Part of the free Php course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ A Pure Enum
The simplest enum lists a handful of named cases and nothing else. Each case is a singleton object of the enum's type, so you can compare them with === and check them with instanceof . Reading ->name gives you the case name as a string. This is called a pure enum because the cases carry no scalar value.
2️⃣ Backed Enums
Most real-world enums need a value you can store or transmit — the string 'published' that goes in a database column, or an integer code. A backed enum declares a type after the name ( : string or : int ) and gives every case a value. You read it with ->value , and you convert back from a raw value using from() (throws on a bad value) or tryFrom() (returns null ).
A backed enum's values must all be the same scalar type and must be unique — two cases can't share a value, and you can't mix ints and strings. This is what makes a backed enum a perfect, lossless bridge between your code and a database column.
3️⃣ Methods on an Enum
Enums aren't just data — they can hold methods , which makes them an excellent home for behaviour that depends on the value. Inside an instance method, $this is the current case, so pairing a method with match($this) gives you clean, exhaustive logic. Enums can also have static methods (handy as named constructors), implement interfaces, and declare constants.
4️⃣ Listing Cases with cases()
Every enum gets a free static cases() method that returns all its cases as an array, in declaration order. This is the idiomatic way to build a <select> dropdown, validate that an input is one of the allowed options, or simply iterate over every possibility.
Now you try — fill in each ___ using the 👉 hint, then run it and check against the Output panel.
These lines define a backed enum and use it — but they're scrambled. Put them in the order PHP needs to run without error.
The enum must be fully declared first: open it ( B ), list the case ( D ), then close the body ( C ). Only then can you reference a case ( E ) and read its ->value ( A ), which prints admin .
L large — ->name is the case name, ->value is the backing string.
NULL — tryFrom() returns null for an unknown value instead of throwing. ( from('off') would throw a ValueError .)
bool(true) — every case is a singleton, so the same case is always identical to itself with === .
📋 Quick Reference — Enums
No code is filled in this time — just a brief and an outline. Write it yourself, run it on onecompiler.com/php or your own machine, then check your result against the expected output in the comments.
Practice quiz
What is the difference between a pure enum and a backed enum?
- A pure enum is faster
- A pure enum can hold properties
- A backed enum gives every case a scalar value (->value); a pure enum has only ->name
- There is no difference in PHP 8.1
Answer: A backed enum gives every case a scalar value (->value); a pure enum has only ->name. A backed enum (enum Status: string) attaches an int or string value to each case, exposed via ->value. A pure enum's cases are just named singletons with ->name.
Given enum Size: string { case S = 'small'; case L = 'large'; }, what does Size::L->name . ' ' . Size::L->value print?
- L large
- large large
- L L
- large L
Answer: L large. ->name is the case name (L) and ->value is the backing string (large), so it prints 'L large'.
What does Status::tryFrom('off') return when 'off' is not a valid backing value?
- false
- An empty string
- It throws a ValueError
- null
Answer: null. tryFrom() returns null for an unknown value instead of throwing, making it ideal for untrusted input.
What does from() do when given a value no case has?
- Returns null
- Throws a ValueError
- Returns the first case
- Returns false
Answer: Throws a ValueError. from() throws a ValueError on an invalid value — use it when an invalid value is a bug that should stop execution.
Which built-in method returns an array of all cases in declaration order?
- cases()
- all()
- values()
- toArray()
Answer: cases(). Every enum gets a static cases() method returning all its case objects in declaration order — great for dropdowns and validation.
What must be true about the backing values of a backed enum?
- They can mix ints and strings freely
- They must be sequential integers
- They must all be the same scalar type and be unique
- They are optional
Answer: They must all be the same scalar type and be unique. A backed enum's values must all be the same scalar type (all int or all string) and must be unique — no two cases can share a value.
How do you add per-case behaviour to an enum?
- Give each case a mutable property
- Define a method that uses match($this) to return the right value
- Subclass the enum per case
- Enums cannot have behaviour
Answer: Define a method that uses match($this) to return the right value. Enums can't hold mutable per-case state, but they can have methods. A common pattern is match($this) inside a method to map each case to a value.
Inside an enum's instance method, what does $this refer to?
- The enum class
- The backing value
- Always the first case
- The current case
Answer: The current case. In an instance method, $this is the current case, which is why pairing it with match($this) gives clean per-case logic.
For a pure enum Suit { case Hearts; }, what is var_dump(Suit::Hearts === Suit::Hearts)?
- bool(false)
- bool(true)
- It throws an error
- NULL
Answer: bool(true). Each case is a singleton object, so the same case is always identical to itself under ===, giving bool(true).
Which safe one-liner turns request data into an enum with a default fallback?
- Status::from($input) ?? Status::Draft
tryFrom() returns null on an invalid value, so the ?? operator can supply a sensible default in one line.