Crashed vehicle

Crash early

A dead program normally does a lot less damage than a crippled one.

The Pragmatic Programmer, Tip #38, page 113

Have you ever thought about what can go wrong when the application’s logic is broken, but it continues to work? That might happen when the values are not the ones that you expect. For example, a method/function returns an integer, but a negative one, but you expect it to be positive. Another example is trying to access an unknown array key. The database returns an invalid value. You name it.

What if you won’t allow it and crash early? Yes, the client will see “an error occurred, our engineers are working on it”. The actual benefit is that the program will exit and not cause any harm. No corrupted data in the database nor missing files.

How to crash early in the PHP? There are two ways. Throwing a \LogicExpcetion or enabling assertions on production and using them.

The first one is quite self-describing, but you might be asking, why \LogicException?

Exception that represents error in the program logic. This kind of exception should lead directly to a fix in your code.

https://www.php.net/manual/en/class.logicexception.php
if (!is_int($ttl) || $ttl < 1) {
    throw new \LogicException('Expected TTL to be a positive int');
}

The benefit of this solution is that you don’t need to enable anything, it works out of the box. The drawback is that there is some boilerplate code to do.

Assertions are even easier to use:

assert(is_int($ttl) && $ttl >= 1, 'Expected TTL to be a positive int');

If the assertion will fail, the PHP will throw the \AssertionException with the message that was passed as a second argument:

Psy Shell v0.11.22 (PHP 8.2.12 — cli) by Justin Hileman
> $ttl = -1;
= -1

> assert(is_int($ttl) && $ttl >= 1, 'Expected TTL to be a positive int');

   AssertionError  Expected TTL to be a positive int.

The problem with the assertions is that they have to be enabled, most production systems have them disabled. The benefit is that there is less boilerplate code.

Speaking of crashing early, I can not mention the static analysis tools like PHPStan or Psalm. These tools will prevent the passing of wrong values to the functions/methods! Let’s take a look at the concrete example:

/** @param positive-int $ttl */
function setInCache(mixed $value, int $ttl): void {}

setInCache('lorem-ipsum', -1);

The PHPStan will complain with the message:

Parameter #2 $ttl of function setInCache expects int<1, max>, -1 given.

You can try it by yourself on the PHPStan playground.

My current setup is to use the PHPStan with strict rules, the highest level and throwing \LogicException. The first prevents passing wrong values when possible, second one ensures that the program will crash early. I do not use assertions as they have to be enabled first.