phpstreamphp-8

fopen and stream_select not working after PHP 8 upgrade


I upgraded a PHP 7 application to PHP 8. The following code was working before, but no longer:

$options = [
    'http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
            . 'Content-Length: ' . strlen($content),
        'content' => $content,
        'timeout' => 30,
    ],
];
$context = stream_context_create($options);
$stream = fopen($url, 'r', false, $context);

$read[] = $stream;
$write = null;
$except = null;
$streamsChanged = stream_select($read, $write, $except, 0, 50000);

Now the call to stream_select produces the following error:

Type: ValueError
Message: No stream arrays were passed

I've tried the same POST call using Insomnia without any issues, so it does appear to be related to how I'm using PHP 8.

A var_dump of $stream returns resource(stream), so the type seems correct. I also ran:

is_resource($stream) 
    && get_resource_type($stream) === 'stream' 
    && !feof($stream)

and it passed.


Solution

  • It is a known issue in PHP 8.x

    In PHP 8.0+, the default protocol_version for an HTTP context changed to 1.1. HTTP/1.1 often uses Transfer-Encoding: chunked, which PHP implements internally as a filter, triggering this error when stream_select() is used.

    One of the solutions is forcing the use of HTTP/1.0 in order to avoid the implicit chunked encoding filter.

    For PHP , it is like:

      'http' => [
            'protocol_version' => '1.0',
      // other parameters...
        ]
    

    So, the following code will be fine:

    <?php
    
    $url="https://somedomain.com/index.php";
    
    $content='test_data';
    
    $options = [
        'http' => [
            'protocol_version' => '1.0',
            'method' => 'POST',
            'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
                . 'Content-Length: ' . strlen($content),
            'content' => $content,
            'timeout' => 30,
        ],
    ];
    $context = stream_context_create($options);
    $stream = fopen($url, 'r', false, $context);
    
    if (is_resource($stream) && get_resource_type($stream) === 'stream' && !feof($stream)) { 
       // echo "ok";
    }
    
    $read[] = $stream;
    $write = null;
    $except = null;
    $streamsChanged = stream_select($read, $write, $except, 0, 50000);
    
    // echo $streamsChanged; (it may now return say 1)
    ?>
    

    Apart from the above simple method, the following are other alternatives :