It passed 25 to 1, with 5 abstaining, against a two-thirds requirement. Voting ran from May 4 to May 18, 2026. It targets PHP 8.6, and the page lists it as implemented.
Stream Error Handling Improvements
Adds exception and silent modes for stream errors, with structured StreamError objects and a StreamErrorCode enum you can inspect.
Should the Stream Error Handling Improvements be added to PHP core?
This poll has closed.
Summary
Streams are how PHP reads and writes files, URLs and sockets, and when they fail, they don't all fail the same way. Some raise warnings, some raise notices, and the messages carry little detail. This RFC from Jakub Zelenka adds one consistent way to handle stream errors: you can get exceptions, or stay silent and read the errors later as objects.
Before and after
Today, catching a failed fopen() usually means dealing with warnings and a custom error handler. With this RFC, you can ask for an exception through the stream context:
$context = stream_context_create([ 'stream' => [ 'error_mode' => StreamErrorMode::Exception, ] ]); try { $stream = fopen('/nonexistent/file.txt', 'r', false, $context); } catch (StreamException $e) { echo "Error: " . $e->getMessage() . "\n"; }
The new options
Three new context options go under stream:
error_modepicks how errors surface.StreamErrorMode::Errorkeeps today's warnings and is the default,Exceptionthrows aStreamExceptionwhen an operation can't complete, andSilentreports nothing.error_storepicks which errors PHP keeps for later, which you read withstream_last_errors().error_handleris a callback that receives the errors as an array.
Each error is a StreamError object. It has a code from a new StreamErrorCode enum, like NotFound or PermissionDenied, along with a message, the wrapper name, and usually the file name or URL. One operation can produce several errors, so you get them as an array with the primary error first.
stream_select(), stream_copy_to_stream(), stream_socket_pair() and stream_is_local() gain an optional $context parameter so they can use these options.
You can't set these options on the default context with stream_context_set_default(), because that throws a ValueError. The RFC says this protects libraries that expect the usual warnings.
What it means for existing code
The default mode stays the same, so your current warnings don't change. The RFC lists a few small differences. Some errors that were reported incorrectly are fixed, child streams now inherit the context, and errors are reported closer to when the function returns, so their order relative to other errors might change.