PHP 8.5.0 Alpha 1 available for testing

stream_select

(PHP 4 >= 4.3.0, PHP 5, PHP 7, PHP 8)

stream_selectSupervisa la modificación de uno o varios flujos

Descripción

stream_select(
    ?array &$read,
    ?array &$write,
    ?array &$except,
    ?int $seconds,
    ?int $microseconds = null
): int|false

stream_select() acepta un array de flujos y espera a que alguno de ellos cambie de estado. Esta operación es equivalente a lo que hace la función socket_select(), salvo que trabaja sobre un flujo.

Parámetros

read

Los flujos que están listados en el parámetro read serán supervisados en lectura, es decir, si hay nuevos bytes disponibles para lectura (para ser precisos, si una lectura no bloqueará, lo que incluye también flujos que están al final de archivo, en cuyo caso una llamada a la función fread() retornará un string de tamaño 0).

write

Los flujos que están listados en el parámetro write serán supervisados en escritura (para ser precisos, si una escritura no bloqueará).

except

Los flujos que están listados en el parámetro except serán supervisados para ver si se lanza una excepción.

Nota:

Cuando stream_select() termina, los arrays read, write y except son modificados para indicar qué flujos han cambiado de estado actualmente. Las claves originales del array se preservan.

seconds

Los parámetros seconds y microseconds forman el tiempo límite, seconds especifica el número de segundos mientras que microseconds, el número de microsegundos. El parámetro timeout representa el límite superior del tiempo que stream_select() debe esperar antes de terminar. Si seconds y microseconds están ambos definidos a 0, stream_select() no esperará datos - en su lugar, terminará inmediatamente, indicando el estado actual del flujo.

Si seconds vale null, stream_select() puede bloquearse indefinidamente, terminando únicamente cuando un evento en alguno de los flujos supervisados ocurra (o si una señal interrumpe la llamada al sistema).

Advertencia

Utilizar un valor de 0 permite probar instantáneamente el estado de los flujos, pero debe saberse que no se recomienda utilizar 0 en un bucle, ya que esto hará que el script consuma una gran cantidad de procesador.

Es mucho mejor especificar un valor de algunos segundos, incluso si se debe supervisar y ejecutar diferentes códigos de manera simultánea. Por ejemplo, utilizar un valor de al menos 200000 microsegundos, se reducirá considerablemente el consumo de procesador del script.

No se debe olvidar que el valor de expiración es la duración máxima de espera, si no ocurre nada: stream_select() retornará un resultado tan pronto como uno de los flujos suministrados esté listo para su uso.

microseconds

Véase la descripción de seconds.

Valores devueltos

En caso de éxito, stream_select() retorna el número de flujos que han cambiado, lo que puede ser 0, si el tiempo límite fue alcanzado antes de que los flujos cambien. En caso de error, la función retornará false y un aviso será devuelto (esto puede ocurrir si la llamada al sistema es interrumpida por una señal entrante).

Historial de cambios

Versión Descripción
8.1.0 microseconds ahora es nullable.

Ejemplos

Ejemplo #1 Ejemplo con stream_select()

Este ejemplo supervisa si los datos llegan para ser leídos ya sea en $stream1 o en $stream2. Si el tiempo límite es 0, la función termina inmediatamente:

<?php
/* Preparación del array de flujos de lectura */
$read = array($stream1, $stream2);
$write = NULL;
$except = NULL;
if (
false === ($num_changed_streams = stream_select($read, $write, $except, 0))) {
/* Manejo de errores */
} elseif ($num_changed_streams > 0) {
/* Al menos uno de los flujos ha cambiado */
}
?>

Notas

Nota:

Debido a una limitación del motor Zend actual, no es posible pasar el valor null directamente como parámetro de una función que espera parámetros pasados por referencia. En su lugar, se recomienda utilizar una variable temporal, o una expresión cuyo miembro izquierdo sea una variable temporal. Como esto:

<?php
$e
= NULL;
stream_select($r, $w, $e, 0);
?>

Nota:

Asegúrese de utilizar el operador === cuando busque errores. Como stream_select() puede retornar 0, una comparación realizada con == lo evaluaría como true:

<?php
$e
= NULL;
if (
false === stream_select($r, $w, $e, 0)) {
echo
"stream_select() falló\n";
}
?>

Nota:

Si ha escrito o leído en un flujo que es retornado en los arrays de flujos, sea consciente de que estos flujos pueden no haber escrito o leído la totalidad de los datos solicitados. Sea capaz de leer un solo byte.

Nota:

Algunos flujos (como zlib) no pueden ser seleccionados por esta función.

Nota: Compatibilidad con Windows

Utilizar la función stream_select() en un puntero de archivo retornado por proc_open() fallará y retornará false en Windows.

STDIN desde una consola cambia su estado tan pronto como cualquier evento de entrada esté disponible, pero leer desde un flujo puede seguir siendo bloqueante.

Ver también

add a note

User Contributed Notes 12 notes

up
5
php at richardneill dot org
16 years ago
Note that reading from a regular file which is on end-of-file will *not* block. You'll get a non-blocking, zero-byte read. However, stream_select *will* block if the input is a pipe, and there is no more data to be had.
up
5
Sitsofe
6 years ago
When stream_select() fails you SHOULD NOT use the results of the arrays (i.e. read, write, except) that were passed into it as parameters. While doing so anyway won't trigger undefined behaviour, you are depending on unspecified behaviour that by definition is not guaranteed.

At the time of writing the PHP 7.2 interpreter does not modify the arrays upon stream_select() failure (see the code around https://github.com/php/php-src/blob/php-7.2.14/ext/standard/streamsfuncs.c#L842) thus a PHP program that doesn't heed the advice above can deceive itself as to the state of those streams.

(Hopefully this warning can be added to the main documentation one day)
up
5
Martin
7 years ago
Maintaining connection with multiple clients can be tricky, PHP script is single-thread process, so if you like to do more than one thing at once (like waiting for new connections and waiting for new data), you’ll have to use some sort of multiplexing.

<?php
$socket
= stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
stream_set_blocking($socket, 0);

$connections = [];
$read = [];
$write = null;
$except = null;

while (
1) {

// look for new connections
if ($c = @stream_socket_accept($socket, empty($connections) ? -1 : 0, $peer)) {
echo
$peer.' connected'.PHP_EOL;
fwrite($c, 'Hello '.$peer.PHP_EOL);
$connections[$peer] = $c;
}

// wait for any stream data
$read = $connections;
if (
stream_select($read, $write, $except, 5)) {

foreach (
$read as $c) {
$peer = stream_socket_get_name($c, true);

if (
feof($c)) {
echo
'Connection closed '.$peer.PHP_EOL;
fclose($c);
unset(
$connections[$peer]);
} else {
$contents = fread($c, 1024);
echo
$peer.': '.trim($contents).PHP_EOL;
}
}
}
}
?>
up
5
aidan at php dot net
21 years ago
If you want to set an absolute maximum execution time for stream_select in a loop, it's important to decrement the max_time value passed to stream_select.

<?php
// The maximum time for execution in milliseconds
$maxtime = 200000;
// The time the loop started
$starttime = microtime(true);
// Original array of sockets
$r = $orig_sockets;

// The function to calculate the timeout
function calctimeout($maxtime, $starttime)
{
return
$maxtime - ((microtime(true) - $starttime) * 1000000);
}

while (
stream_select($r, $w = null, $e = null, 0, calctimeout($maxtime, $starttime)) !== 0)
{
// loop through the sockets that showed activity
foreach ($r as $socket) {
// $socket talked
}

// stream_select modifies the contents of $r
// in a loop we should replace it with the original
$r = $orig_sockets;
}

?>
up
6
maartenwolzak at gmail dot com
18 years ago
Note that you should change the calctimeout function below to divide the outcome by 1.000.000 otherwise you'll be waiting for two years instead of one minute for the socket to timeout...

<?php

// The function to calculate the timeout
function calctimeout($maxtime, $starttime)
{
return (
$maxtime - ((microtime(true) - $starttime) * 1000000))/1000000;
}

?>
up
4
asphp at dsgml dot com
9 years ago
Make sure not to pass the same variable in the 3 arguments to stream_select, or you'll only get the results from one of them and the others will be overwritten.
up
3
mark2xv at gmail dot com
11 years ago
If you're getting unexplainable problems with nonblocking sockets using stream_select, disable the buffers using:

stream_set_read_buffer($socket, 0);
stream_set_write_buffer($socket, 0);

For some reason when writing (in total) ~256k, sockets start returning FALSE when reading, yet always appear in the stream_select arrays. This fixed that problem. (for us.)
up
3
phpdoc at shemesh dot biz
20 years ago
Please note that, on return, the key of "read" will be zero based, serially numbered according to the streams for which there is read data ready only. In other words, if you want to know which of the original streams placed in "read" is ready, there is no immediate way of knowing that.

If you want to know which of the original stream is which, you can either use "==", or possibly set a reverse map array, in which the stream is the key, and the key to the original "read" array is the data.
up
2
rob at associatedtechs dot com
10 years ago
If you try to use stream_select() with fread(), you may get bit by a combination of bugs (https://bugs.php.net/bug.php?id=52602 and https://bugs.php.net/bug.php?id=51056). As of PHP 5.5.10, fread() and stream_select() don't reliably play well together.

If you need stream_select() and you don't need an encrypted connection (e.g. TLS), use stream_socket_recvfrom() instead of fread().

I can't find a way to reliably handle an encrypted connection with blocking functions in PHP; non-blocking might be the only way to do it.
up
2
Ben
18 years ago
You can key on file descriptors just fine by casting them to an int or a string, which returns what you would expect.
up
1
aks at esoft dot dk
14 years ago
stream_select() looks deceptively like a simple wrapper around POSIX select(2).

But beware: while select(2) allows you to pass no file descriptors and use it as a "portable subsecond sleep", PHP will complain with "Warning: stream_select(): No stream arrays were passed in ****" if all arrays are empty or null, and it WONT sleep, it will return immediately. So... if the number of file descriptors you have isn't static, you have to deal with the special case yourself.
up
1
doingitwrong at mailismagic dot com
10 years ago
If you use stream_select() with a blocking stream, you are doing it wrong!

Just because this function returns something in one or more of the arrays does NOT mean that a future read or write operation will not block.

The above sentence is the most important sentence you will ever read regarding stream manipulation. Using stream_select() with blocking streams is a very common amateur mistake and causes major headaches when tracking down usage of this and similar select() system functions. PHP (and really the underlying OS) should verify that the supplied stream set is not blocking and throw an error/exception if any socket is set to block so that people are forced to fix their code. The documentation for stream_select() is, at best, misleading.

If you want a non-blocking stream, then set the stream to not block. Otherwise, live with the blocking stream. That is, after all, the whole point of blocking - to block indefinitely until the operation completes. select() is built for non-blocking streams ONLY. Any other use will result in very hard to track down bugs.

I got the above lecture many years ago after encountering the very bugs I mention. I fixed my code and now correct similar mistakes when I run into the issue elsewhere. Writing code for non-blocking streams is simpler than trying to write hacks for blocking streams with select() functions and ending up with application bugs.
To Top