---
title: "What Can Implement An Interface In PHP?"
url: https://www.exakat.io/what-can-implement-an-interface-in-php/
date: 2026-08-13
modified: 2026-08-13
author: "dams"
description: "What Can Implement An Interface In PHP? Ask a PHP developer what implements an interface, and you will get class Foo implements Bar. Ask again, insist a little, and you..."
categories:
  - "Code auditing"
tags:
  - "interface"
  - "syntax"
image: https://www.exakat.io/wp-content/uploads/2026/08/wrapping.320.jpg
word_count: 2844
---

# What Can Implement An Interface In PHP?

# What Can Implement An Interface In PHP?

Ask a PHP developer what implements an interface, and you will get `class Foo implements Bar`. Ask again, insist a little, and you might get `abstract class`. Ask a third time and people start looking at their shoes.

Yet the `implements` keyword, and its cousin, `extends`, has a guest list that is longer than most of us remember. Interfaces are the only form of multiple inheritance PHP allows, so they end up being the tool we reach for whenever we need to say `these unrelated things share a common ground`. It pays to know exactly which things are allowed at the party.

Let's walk from the obvious to the exotic. There are surprises coming up, by the end of the post.

## 1. Classes, obviously

Nothing to see here. Move along. Except for two footnotes that people trip over.

### Abstract classes may implement an interface and then implement nothing at all

The contract silently migrates to the children. This is a lovely way to hide a requirement three levels up a hierarchy, and a lovely way to spend an afternoon wondering why the engine insists your class is abstract. Static analysis tools love reporting this one; developers love ignoring the report.

### Native classes can be extended, and the child may implement interfaces the parent never heard of

`ArrayObject` knows nothing about JSON. It doesn't need to. This is the classic escape hatch for third-party or internal classes you cannot modify: wrap or extend, then bolt the interface on the outside. It is also the only way to make some internal classes fit your own type system, since a good number of them are `final`.

## 2. Interfaces implement interfaces, except they call it extends

Note the keyword: `extends`, not `implements`. And note the plural. A class may extend exactly one class, but an interface may extend as many interfaces as it likes. This is the multiple inheritance that PHP officially does not have, sitting in plain sight.

The practical trick is the composite interface: one name that bundles several contracts, so consumers type-hint one thing instead of three.

Interfaces also carry constants, and since PHP 8.1 an implementing class is allowed to redefine them: a rule that quietly changed under everyone's feet and broke exactly nobody, because nobody was using interface constants.

And since PHP 8.4, interfaces can require properties, not just methods, thanks to [property hooks](https://wiki.php.net/rfc/property-hooks):

An implementer satisfies this with a plain public property, or a virtual one computed on the fly. Twenty years of writing `getName()` to express a read-only attribute, and it turns out we could have just asked for the attribute. Better late than never.

## 3. Enums and the extensible enum trick

Enums implement interfaces. Backed or pure, it makes no difference:

This is where the fun begins, because it is the closest PHP will ever get to extending an enum. You cannot add a case to `Suit` from the outside: the whole point of an enum is that the set is closed. But you can declare a second enum that implements the same interface, and type-hint the interface:

Two closed sets, one open type. Now, as you said: conditions apply, and they deserve to be spelled out, because this pattern is sold more often than it is examined.

- You lose exhaustiveness. `match($card)` over an interface can never be complete, so you always need a `default`, and your `UnhandledMatchError` moves from compile-time-ish to runtime.
- There is no `HasColor::cases()`. Enumerating "all values of the group" means enumerating each enum by hand, which rather defeats the purpose.
- Enums are stateless by construction, so the interface can only ever ask for behaviour, never for data, unless you count constants, or an 8.4 virtual property backed by a `match`.
- Two enums may hold cases with the same name or the same backing value, and nothing will warn you. `Suit::from('H')` and `Tarot::from('H')` are different objects with different classes and no relation whatsoever.

Used with discipline, say, a `Priority` interface implemented by a core enum and a plugin's enum, it is genuinely useful. Used to fake an open enum, it becomes a class hierarchy wearing a false moustache.

Two more enum details worth pocketing. Every enum automatically implements `UnitEnum`, and backed ones also implement `BackedEnum`: you never write those, they arrive on their own, and `instanceof BackedEnum` is a perfectly good way to ask "does this thing have a `->value`?. And enums are explicitly forbidden from implementing `Serializable`, which is deprecated since 8.1 anyway. The [enum RFC](https://wiki.php.net/rfc/enumerations) is worth a read for the full list of restrictions.

## 4. Traits: they can't, but they should

Traits cannot implement anything. `trait Foo implements Bar` is a parse error, and rightly so: a trait is not a type. You cannot type-hint it, you cannot `instanceof` it, it has no existence at runtime beyond a copy-paste directive with delusions of grandeur.

What a trait can do is fulfill an interface. And this is the standard pairing everyone ends up rediscovering:

The interface declares the promise, the trait delivers the goods, the class signs the contract. Three files to say one thing, which is either excellent separation of concerns or PHP being PHP, depending on the day of the week.

A trait can also push in the other direction, by declaring abstract methods:

Now the trait imposes requirements on its host: a poor man's interface, without the type.

And then there is the naming problem you pointed at. Interfaces, classes, traits and enums all live in the same symbol table. `Countable` the interface and `Countable` the trait cannot coexist in one namespace. So the ecosystem invented conventions, none of which won: `CountableTrait`, `TCountable`, `CountableBehavior`, or a dedicated `Acme\Traits` namespace. Pick one and stick with it, because the day you have `Acme\Loggable` and `Acme\Traits\Loggable` imported in the same file, your `use`statements start needing aliases, and reviewing that diff is nobody's idea of a good Friday.

## 5. Anonymous classes too

This is the one that gets forgotten, and it is arguably the most useful of the lot.

An anonymous class can implement interfaces, extend a class, use traits, take constructor arguments, and be returned from a factory. And all that without occupying a name in your global namespace. See the [manual page](https://www.php.net/manual/en/language.oop5.anonymous.php) for the full syntax.

At that point, using an interface gives the class a category of usage, without giving it a name. It is a dog, it barks, no one know how it is called: just run!

Three places where they shine:

- Test doubles. A five-line fake that implements the interface, with no mocking framework, no `expects()->willReturn()`incantation, and no separate fixture file.
- Returning a private implementation. Your factory promises an interface; the concrete type is not part of the API, so it does not need a name and nobody can couple to it.
- One-shot strategies. The callback that needs three methods instead of one.

The catch is identity. Anonymous classes get a generated name that looks roughly like `class@anonymous` followed by a null byte and the file and line where it was declared. It shows up in stack traces looking distinctly like a corrupted string, and it changes every time you insert a line above it. Do not store it, do not compare it, do not put it in a cache key. Type the interface and forget the class ever existed, which is, after all, the entire point.

## 6. Attributes: classes in disguise

An attribute is just a class with a decoration on top. So of course it can implement an interface:

Is this useful? Mostly when you have a family of attributes and you want to process them uniformly:

That `IS_INSTANCEOF` flag is the reason to bother: it lets you fetch attributes by interface, which turns a pile of unrelated markers into a queryable family. Without it, `newInstance()` hands you an `object` and you go back to `instanceof` chains.

Now, the more interesting claim: attributes are better markers than marker interfaces. I'll agree, with a caveat.

A marker interface, the `implements Serializable`-with-no-methods pattern, abuses the type system to store one bit of metadata. It is inherited whether you want it or not, it cannot carry parameters, and it makes your class be something when you only wanted it to have something. An attribute carries data, targets methods and properties and parameters rather than just classes, and stays out of your type hierarchy entirely.

The caveat is cost. `instanceof` is a pointer comparison. Reading an attribute means instantiating a `ReflectionClass`, walking the attribute list, and calling `newInstance()`. In a hot loop that difference is real; in a bootstrap that runs once and caches, it is noise. Frameworks resolve this by doing the reflection at compile time and dumping the result into a container, which is why every serious framework now has a cache warmer.

## 7. Invokables: the only way to type a closure

`Closure` is `final`. You cannot extend it, you cannot make it implement anything. And PHP's `callable` type is a blunt instrument: it accepts anything callable, and tells you nothing about the signature. There is no `callable(int, int): bool` in the language, only in your PHPStan or Psalm docblocks, where the engine cannot enforce it.

An invokable object fixes this:

You get a typed, enforced function signature, checked by the engine at call time. It composes, it can be decorated, it can hold configuration in its constructor, and it still slots straight into `usort`, `array_map` and every other function expecting a `callable`. This is what other languages call a functional interface, and it is the single best reason to reach for `__invoke`.

The irony is that the [first-class callable syntax](https://wiki.php.net/rfc/first_class_callable_syntax) added in PHP 8.1 goes the other way: `$comparator(...)` turns your carefully typed invokable back into an anonymous `Closure`, and the type evaporates. Convenient, and slightly heartbreaking.

## 8. Exceptions, where interfaces earn their keep

Every class above can implement an interface. Exceptions are the only ones where the language itself gives you dedicated syntax for it:

`catch` accepts an interface exactly as it accepts a class name. That one fact restructures how libraries should handle errors. Instead of forcing users to catch your concrete `Acme\Exception\ConfigNotFound`, you publish an interface:

Now `catch (AcmeException $e)` catches everything your library throws, regardless of which SPL class each one extends. Users can catch broadly or narrowly. You can move an exception from `RuntimeException` to `LogicException` without breaking anyone. This is exactly what PSR-11 does with [`ContainerExceptionInterface`](https://www.php-fig.org/psr/psr-11/) and PSR-18 does with [`ClientExceptionInterface`](https://www.php-fig.org/psr/psr-18/), and it is the pattern to copy.

One restriction: you cannot implement `Throwable` directly on a class that does not descend from `Exception` or `Error`. The engine refuses. `interface AcmeException extends Throwable` is fine, an interface extending `Throwable` is allowed, but the concrete classes still have to inherit from the real thing.

Which brings us to a broader category.

## 9. The interfaces you are not allowed to implement

Not every interface is open for business. The engine keeps a short list of reserved ones:

| Interface | Why you can't | What to do instead |
| --------- | ------------- | ------------------ |
| `Traversable` | Marker for internal iteration | Implement `Iterator` or `IteratorAggregate` |
| `Throwable` | Needs engine-level internals | Extend `Exception` or `Error` |
| [`DateTimeInterface`](https://www.php.net/manual/en/class.datetimeinterface.php) | Explicitly closed to userland | Extend `DateTimeImmutable` |
| `UnitEnum` / `BackedEnum` | Enums only | Declare an enum |
| `Serializable` | Deprecated since 8.1 | `__serialize()` / `__unserialize()` |

`DateTimeInterface` is the memorable one. It looks like an interface, it is named like an interface, and the manual states flatly that userland classes cannot implement it. Everyone discovers this the same way: by writing `class MyDate implements DateTimeInterface`, running it, and reading a fatal error that feels personally hostile.

## 10. The finals: Closure, Generator, Fiber

Three internal classes are `final`, so they will never implement your interface: `Closure`, `Generator`, and [`Fiber`](https://wiki.php.net/rfc/fibers). Composition is the only route.

You asked what one would even want from a `Generator` implementing an interface, and it is a fair question with a real answer: a name for a lazy stream. `iterable` tells you nothing. `Generator` tells you "lazy, but of what?". Sometimes you want the type to say "this is a stream of users, consumed once, from the database", and you want to hang a couple of methods off it.

The idiomatic move is `IteratorAggregate`:

The generator keeps the laziness, the wrapper provides the type and the extra methods, and `foreach` never notices the difference. Note that `interface UserStream extends Traversable` is legal: extending `Traversable` from an interface is allowed even though implementing it from a class is not. PHP contains multitudes.

## 11. Interfaces that are secretly implemented

Two contracts arrive without an invitation.

Since PHP 8.0, any userland class declaring `__toString()` automatically implements [`Stringable`](https://wiki.php.net/rfc/stringable). You do not write it. The engine adds it at compile time, which means a decade-old class written long before the interface existed will happily pass `instanceof Stringable` today. It is retroactive typing, and it is delightful.

And as mentioned, enums get `UnitEnum` and `BackedEnum` for free. That's the complete list of automatic implementations: do not go looking for `Countable` to appear when you define `count()`. It won't.

## The recap

| Construct | `implements`? | Notes |
| --------- | ------------- | ----- |
| Class | Yes | The one everyone knows |
| Abstract class | Yes | May implement nothing at all |
| Child of an internal class | Yes | Bolt interfaces onto classes you don't own |
| Interface | Yes, via `extends` | And multiple ones at that |
| Enum | Yes | Plus automatic `UnitEnum` / `BackedEnum` |
| Anonymous class | Yes | The most underused feature in this table |
| Attribute class | Yes | Fetch by interface with `IS_INSTANCEOF` |
| Invokable class | Yes | The only way to type a function signature |
| Exception / Error | Yes | And `catch` accepts the interface |
| Trait | **No** | Fulfills, never implements |
| Closure / Generator / Fiber | **No** | `final`; wrap them instead |

The pattern underneath all of this is worth stating plainly. An interface is not a class feature: it is the language's mechanism for saying "this thing keeps this promise", and PHP has quietly extended that mechanism to nearly every construct it has. Classes were first. Enums arrived in 8.1. Properties in interfaces arrived in 8.4. The trend is clear enough that the interesting question is no longer `what can implement an interface`, but `what still can't`.

Go check your codebase for `catch (\Exception $e)`. There is at least one, and it is not catching what you think it is.