Exceptions

Throw

Use throw when a function cannot keep its contract. Throwing stops the current path at once and carries an object with the reason.

Since PHP 8.0 throw is also an expression, so it fits inside null coalescing and arrow functions.

function findUser(int $id): array { if ($id <= 0) { throw new InvalidArgumentException("Bad id"); } return ["id" => $id]; } $port = $_ENV["PORT"] ?? throw new RuntimeException("PORT is not set");

Catch by type

Wrap risky calls in try and handle each failure with a matching catch block. The first block with a compatible type wins.

Put specific types first and general types last, otherwise the general block hides the rest.

try { $user = findUser(-5); echo $user["id"]; } catch (InvalidArgumentException $e) { echo "Fix the id"; } catch (Throwable $e) { echo "Something failed"; }

finally always runs

A finally block runs whether the code succeeds, throws, or returns early. It is the right place to close files, unlock resources, and stop timers.

Keep it small and exception free so it never masks the original error.

$lock = fopen("lock.txt", "w"); try { echo "work"; } catch (Throwable $e) { echo "failed"; } finally { fclose($lock); }

Your own exception class

Create a domain exception by extending Exception or RuntimeException. Callers can then catch your failure without catching every unrelated error.

Remember the split in the engine: Exception is for logic failures and Error is for engine failures. Catch Throwable when you must handle both branches at once.

class UserNotFound extends RuntimeException { } try { throw new UserNotFound("Ada is missing"); } catch (UserNotFound $e) { echo $e->getMessage(); }

Next: Strict types

Article author: Arthur Isaev