An error has a level such as E_WARNING; PHP passes it to the error handler and, unless it is fatal, carries on with a fallback value such as null. An exception is a thrown object: the function stops and the stack unwinds until a matching catch takes it, or the script dies with an "Uncaught" fatal error. PHP 7 turned most engine fatal errors into thrown Error objects, and PHP 8.0 made built-in functions throw TypeError and ValueError for bad arguments.
<?php
declare(strict_types=1);
$stock = ['BK-101' => 4];
$qty = $stock['BK-999']; // warning: the script carries on with null
try {
echo intdiv(12, $qty); // TypeError: thrown, so catchable
} catch (TypeError $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}Output
PHP Warning: Undefined array key "BK-999" in /home/dev/shop/stock.php on line 4 TypeError: intdiv(): Argument #2 ($num2) must be of type int, null given
The warning is the dangerous one: the script ran on with null, and only the next line noticed. Warnings to Exceptions therefore turns warnings into exceptions, so every failure takes one loud path.