is_subclass_of
Checks whether an object or class descends from a given class or implements a given interface.
is_subclass_of(mixed $object_or_class, string $class, bool $allow_string = true): bool- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$object_or_class | mixed | An object instance, or a class name string. No error is raised if the class does not exist. |
$class | string | The class or interface name to test against, fully qualified. |
$allow_stringoptional | bool= true | When false, a string first argument is rejected and no autoloader runs. |
Return value
booltrue if the object or class is a descendant of $class or implements it, false otherwise.
Errors
Does not throw and raises no error for unknown class names; it simply returns false.
is_subclass_of() asks whether something is a strict descendant: the whole ancestry is searched, plus every interface implemented anywhere in that chain, but the class itself does not count. is_subclass_of(new User(), 'User') is false.
That strictness is the whole reason to prefer it over instanceof. When you want "a specialised version of the base, not the base itself" — abstract-class registries, plugin discovery that must exclude the abstract parent — this is the check that expresses it directly.
When to reach for it
- Scanning discovered classes for concrete implementations of an abstract base, skipping the base.
- Validating that a user-supplied exception class extends your package's base exception before you throw it.
- Enforcing "must override" rules in a factory that would otherwise happily build the abstract parent.
What trips people up
Unlike is_a(), allow_string defaults to true here, so a class name works out of the box — and so does the autoloader. Passing an unvetted string can therefore fire your autoloader for a class that will never exist. Pass false as the third argument when the name is untrusted or you only care about already-loaded classes.
As always, the second argument is not resolved against use statements: write BaseModel::class, not 'BaseModel'.
Example
When to use it
- Collecting every concrete command class under an abstract Command base while excluding the base.
- Rejecting a configured exception class that does not extend your package's own exception type.
- Building a handler registry that only accepts classes extending an abstract handler.
Frequently asked questions
Does is_subclass_of work with interfaces?
Yes. It returns true when the class implements the named interface, directly or through any ancestor. It returns false if you pass the interface name for a class that is that interface.
Why does is_subclass_of return false for the class itself?
By design — it tests strict descent. Use instanceof or is_a() when the class itself should also count.
Does is_subclass_of trigger the autoloader?
Yes, when the first argument is a string and allow_string is left at its default of true. Pass false to check only already-loaded classes.
More examples
Finding concrete implementations of an abstract base
Both Square and Circle print because the check walks the full chain, while Shape itself is excluded by the strictness rule — exactly what you want when instantiating the results.
<?php
abstract class Shape { abstract public function area(): float; }
class Square extends Shape {
public function area(): float { return 4.0; }
}
class Circle extends Square {}
foreach (get_declared_classes() as $c) {
if (is_subclass_of($c, 'Shape', false)) {
echo $c, ' => ', (new $c())->area(), "\n";
}
}is_subclass_of compared with instanceof and is_a
On the base instance the first two are true and is_subclass_of() is false. On the child all three are true. The last line shows the differing string defaults: is_a() rejects the string, is_subclass_of() accepts it.
<?php
class Base {}
class Child extends Base {}
$b = new Base();
$c = new Child();
var_dump($b instanceof Base, is_a($b, 'Base'), is_subclass_of($b, 'Base'));
var_dump($c instanceof Base, is_a($c, 'Base'), is_subclass_of($c, 'Base'));
var_dump(is_a('Child', 'Base'), is_subclass_of('Child', 'Base'));
Discussion