Everything you can throw implements Throwable, and your classes must extend Error or Exception to do so. Error means the engine found what is almost always a bug: a wrong type, a division by zero. Exception means a failure to expect, such as bad input or a timeout.

Reflection prints the real tree. The script keeps core and SPL classes; extensions add about thirty more, such as JsonException and PDOException.
<?php
$tree = [];
foreach (get_declared_classes() as $name) {
$c = new ReflectionClass($name);
if ($c->implementsInterface(Throwable::class)
&& in_array($c->getExtensionName(), ['Core', 'SPL'])) {
$tree[$c->getParentClass()?->name ?? 'Throwable'][] = $name;
}
}
foreach ($tree as $parent => $kids) echo "$parent <- ", implode(', ', $kids), "\n";Output
Throwable <- Exception, Error Exception <- ErrorException, RequestParseBodyException, ClosedGeneratorException, LogicException, RuntimeException Error <- CompileError, TypeError, ValueError, ArithmeticError, UnhandledMatchError, FiberError CompileError <- ParseError TypeError <- ArgumentCountError ArithmeticError <- DivisionByZeroError LogicException <- BadFunctionCallException, DomainException, InvalidArgumentException, LengthException, OutOfRangeException BadFunctionCallException <- BadMethodCallException RuntimeException <- OutOfBoundsException, OverflowException, RangeException, UnderflowException, UnexpectedValueException
Every Throwable offers getMessage(), getCode(), getFile(), getLine(), getTrace() and getPrevious(). catch (Exception $e) does not catch a TypeError, so a top-level safety net catches Throwable, and catch (RuntimeException $e) also receives a PDOException.