A Bit of History
PHP is a language full of quirks—quirks that developers either love to hate or hate to admit they love. One of the oldest (and most annoying) quirks is its error reporting system.
Many legacy functions—those written before PHP introduced its modern exception system—don’t throw exceptions when something goes wrong. Instead, they emit a warning and return false
. This behavior clutters your logs, adds noise to your debugging, and forces you to handle two different signals for the same error.
Take fopen()
for example: if it can’t open a file, it raises a warning and returns false
. Wouldn’t throwing an exception be cleaner?
The Current Status
Some argue that not every error should become an exception, and not every issue should stop execution. Fair enough. But let’s be real: if fopen()
returns false
, something is already broken. Does the extra warning really help?
Sure, maybe it’ll show up in your logs and you’ll fix it immediately. Or maybe you’ll shrug it off as “not a big deal.” Either way, you’re left with duplicated signals and inconsistent handling.
The Problem
In practice, warnings are a mixed bag:
- Sometimes they’re expected and harmless.
- Sometimes they’re critical and need immediate attention.
- Sometimes they’re just noise.
The tricky part is that PHP doesn’t make it easy to decide how to handle them consistently. I’ve worked on projects where warnings were both irrelevant and mission-critical—sometimes within the same codebase. That’s not sustainable.
The Solution: A Warning
Utility
After wrestling with this problem for years, I built a small utility class I now use in my open source projects: Warning
.
Its job is simple: give you full control over how warnings are handled. You can either trap them (convert them into exceptions) or cloak them (silence them entirely).
/**
* @internal Utility class to wrap callbacks to control emitted warnings during their execution.
*
* @template TReturn
*/
final class Warning
{
/**
* Converts PHP Warning into ErrorException.
*
* @param mixed ...$arguments the callback arguments if needed
*
* @throws ErrorException If the callback internally emits a Warning
* @throws Throwable on callback execution if the callback throws
*
* @return TReturn The result returned by the callback.
*/
public static function trap(callable $callback, mixed ...$arguments): mixed
{
set_error_handler(
fn (int $errno, string $errstr, string $errfile, int $errline): bool =>
in_array($errno, [E_WARNING, E_USER_WARNING], true)
? throw new ErrorException($errstr, 0, $errno, $errfile, $errline)
: false
);
return self::execute($callback, $arguments);
}
/**
* Hides PHP Warnings.
*
* @param mixed ...$arguments the callback arguments if needed
*
* @throws Throwable on callback execution if the callback throws
*
* @return TReturn The result returned by the callback.
*/
public static function cloak(callable $callback, mixed ...$arguments): mixed
{
set_error_handler(
fn (int $errno, string $errstr, string $errfile, int $errline): bool =>
in_array($errno, [E_WARNING, E_USER_WARNING], true),
);
return self::execute($callback, $arguments);
}
private static function execute(callable $callback, array $arguments)
{
try {
return $callback(...$arguments);
} finally {
restore_error_handler();
}
}
}
That’s it. It’s tiny, requires no dependencies, and IMHO doesn’t deserve its own package.
How It Works
// Old behavior: returns false + emits a warning
$resource = fopen('/path/to/missing/file.md', 'r');
// Cloak: returns false, no warning emitted
$resource = Warning::cloak(fopen(...), '/path/to/missing/file.md', 'r');
// Trap: throws ErrorException and halts execution
$resource = Warning::trap(fopen(...), '/path/to/missing/file.md', 'r');
The Takeaway
Instead of juggling noisy warnings and inconsistent error handling, you can now decide how PHP should behave in each context:
- Cloak when you expect errors and don’t want logs cluttered.
- Trap when an error should be treated as an exception.
It’s a simple, explicit, and consistent way to deal with PHP’s legacy warnings. Until every core function throws proper exceptions (don’t hold your breath), the Warning
class can save you a lot of headaches.