Understanding self vs static in PHP: Why Your Interface Implementation May Fail
When working with interfaces and return types in modern PHP (especially PHP 8.0 and PHP 8.1+), you might run into a confusing but very real error:
Fatal error: Declaration of B::new(): B must be compatible with A::new(): static
This error trips up many developers because it seems like you're doing everything right. After all, self
and static
often feel interchangeable. But PHP's type system sees them very differently—especially in the context of interfaces and inheritance.
The Root of the Problem
Let's start with an example:
interface A
{
public static function new(): static;
}
final class B implements A
{
public static function new(): self // ❌ Fatal error
{
return new self();
}
}
You may think that since B
is final
, there’s no way static
and self
can differ. True at runtime, but not true at compile time.
static
≠ self
Here’s the key difference:
self
refers to the class where the method is declared.static
uses late static binding, which means it refers to the class that is called, even in a child class.
Even in a final class
, PHP treats self
and static
as distinct types in terms of method signatures. That’s why using self
where static
is expected causes a fatal error.
The Correct Fix
To resolve the error, update the return type to match the interface exactly:
final class B implements A
{
public static function new(): static // ✅ Now valid
{
return new static();
}
}
Even though the class is final
, type declarations must align exactly with the interface definition. PHP won't assume self
is “good enough.”
Why PHP Is Strict Here
PHP’s strictness ensures that:
- Interfaces can be relied upon: other code using the interface can safely expect the return type to be what was promised.
- Inheritance behaves correctly: if someday the class is not
final
, or ifA
is reused by another class, it still works.
Even when a class is final
, PHP doesn't relax these rules to avoid accidental breaking changes later.
Bonus: Other Considerations
- Trait methods also must match return types when implementing an interface indirectly.
- Covariant return types (introduced in PHP 7.4) allow child classes to return more specific types, but not less specific.
- Abstract classes must follow the same contract rules as interfaces regarding
static
return types.
Finally
To avoid frustrating fatal errors:
- Always match interface return types exactly—even if it seems redundant.
- Never substitute
self
forstatic
when the contract explicitly saysstatic
. - Use
static
in return types when you want to support late static binding.
Understanding the subtleties between self
, static
, and even parent
in PHP will help you write more robust, future-proof, and contract-safe code.
Comments ()