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.
stream_select
(PHP 4 >= 4.3.0, PHP 5)
stream_select — Surveille la modification d'un ou plusieurs flux
Description
&$read
, array &$write
, array &$except
, int $tv_sec
[, int $tv_usec = 0
] )stream_select() accepte un tableau de flux et attend que l'un d'entre eux change de statut. Cette opération est équivalente à ce que fait la fonction socket_select(), hormis le fait qu'elle travaille sur un flux.
Liste de paramètres
-
read -
Les flux qui sont listés dans le paramètre
readseront surveillés en lecture, c'est-à-dire si de nouveaux octets sont disponibles en lecture (pour être précis, si une lecture ne bloquera pas, ce qui inclut aussi les flux qui sont en fin de fichier, auquel cas un appel à la fonction fread() retournera une chaîne de taille 0). -
write -
Les flux qui sont listés dans le paramètre
writeseront surveillés en écriture (pour être précis, si une écriture ne bloquera pas). -
except -
Les flux qui sont listés dans le paramètre
exceptseront surveillés pour voir si une exception est levée.
Vous n'êtes pas obligés de remplir tous les tableaux arguments de la fonction stream_select(). Vous pouvez les laisser et utiliser un tableau vide, ou encoreNote:
Lorsque stream_select() se termine, les tableaux
read,writeetexceptsont modifiés pour indiquer quels flux ont actuellement changé de statut.NULL. De plus, n'oubliez pas que ces tableaux sont passés par référence et seront modifiés une fois que stream_select() s'achève. -
tv_sec -
Les paramètres
tv_secettv_usecforment le délai d'expiration,tv_secspécifie le nombre de secondes tandis quetv_usec, le nombre de microsecondes. Le paramètretimeoutreprésente la limite supérieure du temps que stream_select() doit attendre avant de se terminer. Sitv_secettv_usecsont tous les deux définis à 0, , stream_select() n'attendra pas de données - à la place, elle se terminera immédiatement, indiquant le statut courant du flux.Si
tv_secvautNULL, stream_select() peut se bloquer indéfiniment, se terminant uniquement lorsqu'un événement sur un des flux surveillés survient (ou si un signal interrompt l'appel système).AvertissementUtiliser un valeur de 0 vous permet de tester instantanément le statut des flux, mais il faut savoir qu'il n'est pas recommandé d'utiliser 0 dans une boucle, car cela va faire consommer énormément de processeur à votre script.
Il est bien mieux de spécifier une valeur de quelques secondes, même si vous devez surveiller et exécuter différents codes de manière simultanée. Par exemple, utiliser une valeur d'au moins 200000 microsecondes, vous réduirez considérablement la consommation processeur de votre script.
N'oubliez pas que la valeur d'expiration est la durée maximale d'attente, si rien ne se passe : stream_select() retournera un résultat dès que l'un des flux soumis est prêt à l'utilisation.
-
tv_usec -
Voyez la description de
tv_sec.
Valeurs de retour
En cas de succès, stream_select() retourne
le nombre de flux qui ont évolué, ce qui peut être 0, si
le délai d'expiration a été atteint avant que les flux n'évoluent.
En cas d'erreur, la fonction retournera FALSE et un
avertissement sera renvoyé (cela peut apparaître si l'appel
système est interrompu par un signal entrant).
Exemples
Exemple #1 Exemple avec stream_select()
Cet exemple surveille si des données arrivent pour être
lues soit dans $stream1, soit dans
$stream2. Si le délai d'expiration
vaut 0, la fonction se termine immédiatement :
<?php
/* Preparation du tableau de flux lecture */
$read = array($stream1, $stream2);
$write = NULL;
$except = NULL;
if (false === ($num_changed_streams = stream_select($read, $write, $except, 0))) {
/* Gestion d'erreur */
} elseif ($num_changed_streams > 0) {
/* Au moins un des flux a évolué */
}
?>
Notes
Note:
À cause d'une limitation du moteur Zend actuel, il n'est pas possible de passer la valeur
NULLdirectement comme paramètre d'une fonction qui s'attend à des paramètres passés par référence. Au lieu de cela, il est recommandé d'utiliser une variable temporaire, ou une expression dont le membre de gauche est une variable temporaire. Comme ceci :<?php
$e = NULL;
stream_select($r, $w, $e, 0);
?>
Note:
Assurez-vous de bien utiliser l'opérateur === lorsque vous recherchez des erreurs. Comme stream_select() peut retourner 0, une comparaison effectuée à l'aide de == l'évaluerait à
TRUE:<?php
$e = NULL;
if (false === stream_select($r, $w, $e, 0)) {
echo "stream_select() failed\n";
}
?>
Note:
Si vous avez écrit ou lu dans un flux qui est retourné dans les tableaux de flux, soyez bien conscient que ces flux n'ont peut être pas écrit ou lu la totalité des données demandées. Soyez en mesure de lire un seul octet.
Note:
Quelques flux (comme zlib) ne peuvent pas être sélectionnés par cette fonction.
Note:
Notes aux utilisateurs de Windows : stream_select() utilisé sur une pipe retournée par proc_open() peut causer la perte de données sous Windows 98.
Utiliser la fonction stream_select() sur un pointeur de fichier retourné par proc_open() échouera et retournera
FALSEsous Windows.
Voir aussi
- stream_set_blocking() - Configure le mode bloquant d'un flux
You can key on file descriptors just fine by casting them to an int or a string, which returns what you would expect.
Note that contrary to what the previous poster said, one is not able to use a stream resource as a key for an array. Rather, if you want to know which socket you are dealing with, consider using code similar to this:
<?php
$sockets = array("sock_1" => $sock1, "sock_2" => $sock2, "sock_3" => $sock_3);
$read = $write = $error = $sockets;
$num = stream_select($read, $write, $error, 10);
if ($n > 0) {
foreach ($read as $r) {
$key = array_search($r, $sockets);
// $key will be "sock_1", "sock_2", "sock_3", etc.
}
}
?>
Hope that helps someone out there!
Note: At least one of the input arrays must be non-empty, or you get an E_WARNING message:
PHP Warning: stream_select(): unable to select [9]: Bad file descriptor (max_fd=0) in
http://bugs.php.net/bug.php?id=42682
This function will not return the number of changed streams under certain systems with older versions, but instead it will return '0'. Be careful.
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.
Simple stream_select wrapper.. Returns the first stream in the array, and sets parameter 2 to the key (So that it is easy to identify what received data):
<?php
function select($array, &$vkey, $timeout=0){
$select = array();
$null = NULL;
foreach($array as $key => $sock){
$x = count($select);
$select[$x] = $sock;
$keys[$x] = $key;
}
if(stream_select($select, $null, $null, $timeout)){
foreach($keys as $key){
if($array[$key] == $select[0]){
$vkey = $key;
return($select[0]);
}
}
}
}
$streams = array("foo" => $stream_one, "bar" => $stream_two); // Create an array of two (already existant) streams.
if($new = select($streams, $key, 60)){ //Sets $new to the resource that next gets new data, and $key to either "foo", or "bar", depending which one it is.
echo $key.":".stream_get_line($new, 2048)."\n";
}
?>
@mbaynton at gmail dot com
A handy syntactic trick:
<?php
$r = Array($stream1, $stream2);
stream_select($r, $w = null, $x = null, 1337);
?>
I've seen it recommended elsewhere in the documentation for clarifying magic arguments so maintainers don't have to go check the function itself, but it also solves your problem here.
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;
}
?>
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.
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;
}
?>
The documentation states that:
You do not need to pass every array to stream_select(). You can leave it out and use an empty array or NULL instead. Also do not forget that those arrays are passed by reference and will be modified after stream_select() returns.
This appears to be incorrect, at least in PHP 5.1.6, because PHP sees these parameters are passed by reference and thus insists on a bona fide variable for the reference to refer to:
Fatal error: Only variables can be passed by reference
is what you get if you pass NULL or anything else literally.
