nette/safe
nette/safe is a lightweight Nette library providing exception-throwing wrappers for risky PHP functions, helping Laravel and PHP projects fail loudly instead of silently returning false. It offers safer file, stream, and process helpers for more reliable error handling.
nette/safe is an archived, discontinued package; treat any adoption as a legacy maintenance decision, not a new dependency. Before anything else, confirm the code you're touching already depends on it (composer show nette/safe) rather than reaching for it on a fresh project — the actively maintained equivalent is thecodingmachine/safe, and new code should use that instead.
If you're working inside an existing codebase that already has it:
composer require nette/safe (or confirm it's in composer.json).
Inspect vendor/nette/safe/src/ first. The package ships a set of namespaced wrapper functions under Nette\Safe plus an internal error-handler mechanism that traps PHP warnings and converts them into a thrown exception. The exact exception class name matters for catch blocks — verify it in the source rather than assuming \Exception.
Check how the package is autoloaded. Function-based libraries register their functions via a files entry in composer.json; run composer dump-autoload after adding or updating the dependency, otherwise calls to Nette\Safe\... will fatal with "undefined function".
First real use case: replace a false-returning built-in that silently corrupts flow. Instead of:
$contents = file_get_contents($path);
if ($contents === false) { /* often forgotten */ }
use the wrapped equivalent and let the failure surface as an exception:
try {
$contents = \Nette\Safe\file_get_contents($path);
} catch (\Throwable $e) {
// handle missing file, permission denied, etc.
}
Start with file_get_contents, fopen, mkdir, unlink, json_decode-adjacent utilities — the calls where a silent false or warning is easiest to overlook.
Replace-and-catch at the I/O boundary. The package is most valuable at the edges of your application: filesystem, streams, and process handling. Wrap those calls and let exceptions bubble to a handler that can log context (path, operation) — something the original false return could never carry.
Localized error-handler scoping. Because the package works by installing an error handler around the call, keep wrapper usage inside narrow try/catch blocks rather than sprinkling it through a long method. This prevents one caught warning from masking an unrelated notice later in the same function.
Adopt incrementally behind a small facade. In a large legacy codebase, don't rewrite every file_get_contents. Introduce a thin helper — e.g. App\Support\Files::read() — that internally calls Nette\Safe\file_get_contents. That gives you one place to swap the implementation later (to thecodingmachine/safe or native try/finally logic) when you finally retire the archived package.
Translate to domain exceptions. Catch Nette\Safe's exception at the boundary of your service and rethrow a domain-specific one (FileUnreadableException, StorageException). This keeps the discontinued package's API from leaking through your whole codebase and makes the eventual removal a mechanical change.
Pair with transactions/cleanup. Use exceptions as the trigger for rollback or cleanup: an exception from a wrapped fopen/flock/rename should flow into a finally that releases handles. This is exactly where silent false returns used to leave dangling state.
Testing. Assert on thrown exception type, not just the boolean result. Because the wrapper converts warnings to exceptions, a test that used to check assertFalse($result) becomes expectException(...), which documents the failure mode far better.
set_error_handler/restore_error_handler balance can break — you may see warnings leak through as warnings, or worse, get swallowed. Debug by checking set_error_handler(null) state and whether a framework bootstrap re-registers handlers after the wrapper runs.@ suppression and error_reporting. Suppressed calls and restrictive error_reporting settings can prevent the wrapper from seeing a warning. If a call that should throw instead returns false, check for a leading @ or a level that filters E_WARNING.Nette\Safe\ variant; otherwise you'll get an "undefined function" fatal.catch (\Throwable) vs catch (Exception). Determine whether the wrapper throws an Error-derived class; catching only \Exception can miss it. Catch \Throwable at boundaries and narrow from there.files is sensitive to stale vendor/composer/autoload_files.php. If CI sees "undefined function" but local doesn't, run composer dump-autoload --optimize and clear caches.thecodingmachine/safe (which has a broader function set and active maintenance) behind it. The call sites stay unchanged.How can I help you explore Laravel packages today?