Abstract Classes
Learn how abstract classes define a shared base with some implemented behavior and some behavior subclasses must provide.
Introduction
Some classes only really make sense as a blueprint for other classes. A generic "Shape" is a useful idea — every shape has an area — but it never makes sense to create a plain, unspecified `Shape` object on its own, since you would have no way to calculate its area. PHP lets you express exactly this idea with abstract classes.
An abstract class defines a shared foundation for a family of related classes, mixing behavior it already implements with behavior it deliberately leaves unfinished for each subclass to fill in.
- What the abstract keyword means on a class, and why it cannot be instantiated directly.
- What the abstract keyword means on a method, and why it must be implemented by subclasses.
- Why abstract classes are useful for sharing partial implementation.
- A worked example with an abstract Shape class and concrete Circle and Square subclasses.
The abstract Keyword
Adding `abstract` before `class` marks the class as incomplete on purpose. PHP will not let you create an object directly from an abstract class with `new` — you can only create objects from its concrete (non-abstract) subclasses.
<?php
abstract class Shape
{
public function describe(): string
{
return "This shape has an area of " . $this->area();
}
abstract public function area(): float;
}
// $s = new Shape(); // Fatal error: Cannot instantiate abstract class ShapeTrying to run `new Shape()` produces a fatal error. PHP enforces that `Shape` is only ever used as a base for other classes, never as a standalone object — which makes sense, since `Shape` alone has no way to know how to calculate an area.
Abstract Methods
An abstract class can also declare abstract methods — methods with no body, marked with the `abstract` keyword. An abstract method only lists its name, parameters, and return type; every concrete subclass is required to provide a full implementation, or PHP raises an error.
<?php
abstract class Shape
{
abstract public function area(): float;
}
class Triangle extends Shape
{
// Forgot to implement area()
}
// Fatal error: Class Triangle contains 1 abstract method
// and must therefore be declared abstract or implement the remaining methods (area)PHP will not let `Triangle` exist as written, because it inherited an abstract method it never fulfilled. Every concrete subclass of `Shape` must implement `area()` with matching visibility and a compatible signature.
Why Use Abstract Classes?
Abstract classes let you share real, working code across a family of related classes (like `describe()` above, which every shape inherits unchanged) while still forcing each subclass to supply the one piece of behavior that genuinely differs between them (like `area()`).
An abstract class is a contract plus a head start: it guarantees every subclass will provide certain methods (the abstract ones), while also handing subclasses some already-working shared behavior for free (the regular, implemented methods). This avoids duplicating shared logic in every subclass, while still guaranteeing consistency for the parts that must vary.
Worked Example: Shape, Circle, Square
Let's build out the `Shape` hierarchy fully, with two concrete subclasses that each calculate area differently.
<?php
abstract class Shape
{
public function describe(): string
{
$name = static::class;
return "{$name} has an area of " . round($this->area(), 2);
}
abstract public function area(): float;
}
class Circle extends Shape
{
public function __construct(private float $radius) {}
public function area(): float
{
return M_PI * $this->radius ** 2;
}
}
class Square extends Shape
{
public function __construct(private float $side) {}
public function area(): float
{
return $this->side ** 2;
}
}
$shapes = [new Circle(3), new Square(4)];
foreach ($shapes as $shape) {
echo $shape->describe() . "\n";
}Circle has an area of 28.27
Square has an area of 16`describe()` is written once, in `Shape`, and works correctly for every subclass because it calls `$this->area()` — which resolves to whichever subclass's implementation is actually running. `Circle` and `Square` each only had to write the one method that genuinely differs between them: `area()`. This example also combines the previous two lessons — polymorphism (the shared `describe()`/`area()` calls) built on top of an abstract base class.
Common Mistakes
- Trying to instantiate an abstract class directly with new — PHP always rejects this.
- Forgetting to implement an abstract method in a subclass, causing a fatal error.
- Marking a method abstract but still giving it a body — abstract methods must have no implementation at all.
- Making a class abstract when it has no abstract methods and no real reason to forbid direct instantiation.
Best Practices
- Use an abstract class when you have shared, working behavior plus at least one method that must differ per subclass.
- Give abstract methods clear, descriptive names and complete type declarations so subclasses know exactly what to implement.
- Put genuinely shared logic (like describe() above) in the abstract class itself, not repeated in every subclass.
- Reach for an interface instead (next lesson) when you need a shared contract but no shared implementation at all.
Frequently Asked Questions
Can an abstract class have a constructor?
Yes. Abstract classes can define constructors, regular methods, and properties just like any other class — subclasses inherit and can call them with parent::__construct().
Can an abstract class contain zero abstract methods?
Yes, though it is unusual. Simply marking a class abstract is enough to prevent it from being instantiated directly, even without any abstract methods.
What happens if a subclass does not implement every abstract method?
PHP raises a fatal error, since the subclass would itself have to be declared abstract to remain valid without implementing all inherited abstract methods.
How is an abstract class different from an interface?
An abstract class can provide real, working method implementations alongside abstract ones, and a class can extend only one abstract class. An interface (covered next) provides no implementation at all, and a class can implement several interfaces at once.
Key Takeaways
- The abstract keyword on a class prevents it from being instantiated directly.
- The abstract keyword on a method means it has no body and must be implemented by every concrete subclass.
- Abstract classes let you share real implementation while still requiring subclasses to fill in specific behavior.
- A worked Shape/Circle/Square example showed shared describe() logic paired with a required area() implementation per subclass.
Summary
Abstract classes strike a balance between reuse and flexibility: they let a family of related classes share real, working code, while still guaranteeing that each subclass fills in the specific behavior that must vary. The Shape example showed both halves of that balance working together in a single, clean hierarchy.
- You understand what abstract means on a class and on a method.
- You can explain why abstract classes are useful for sharing partial implementation.
- You built a working abstract Shape hierarchy with Circle and Square subclasses.