並行処理を書きやすくする機構。
ドキュメントによると
Libraries will generally build further abstractions around Fibers, so there’s no need to interact with them directly.
https://www.php.net/releases/8.1/en.php#fibers
と書かれていて、ライブラリを作る人以外は直接使うことはないとのこと。
(逆に言えばCoroutineやPromiseを使うようなライブラリを作る人は使う機会ありそう)
Fibersを使ってListenするとなると次のようなコードになるかなと思われます。
<?php
$address = '0.0.0.0';
$port = 3080;
if (($sock = stream_socket_server("tcp://{$address}:{$port}", $errno, $errstr)) === false) {
echo "stream_socket_server() failed: reason: {$errstr} {$errno}" . PHP_EOL;
exit(255);
}
define('QUIT_MSG', 'QUIT');
class socket_manager{
public static $connections = 0;
static function create_fiber($conn){
stream_set_blocking($conn, false);
$fiber = new Fiber(function () use ($conn): void {
while(true){
$resume_msg = Fiber::suspend('fiber');// resume()後この次の行から実行される
echo "Value used to resume fiber: ", $resume_msg, PHP_EOL;
if($resume_msg === QUIT_MSG){
disconnect_client($conn);
return;
}
$tmp_conn_list = [$conn];
$write_conn = null;
$expect = null;
$is_recv = stream_select($tmp_conn_list, $write_conn, $expect, 0, 0);
if($is_recv === false){
echo 'stream_select error'.PHP_EOL;
disconnect_client($conn);
return;
}
$recv = stream_get_contents($conn);
if($recv === false ){
echo 'stream_get_contents error'.PHP_EOL;
return;
}
/*if($recv === ''){
echo 'no data'.PHP_EOL;
continue;
}*/
echo "received {$recv}".PHP_EOL;
$res = fwrite($conn, $recv."\r\n");// echo response
if($res === false){
static::disconnect_client($conn);
return;
}
}
});
$value = $fiber->start();
echo "Value from fiber suspending: ", $value, PHP_EOL;
return $fiber;
}
static function disconnect_client($conn){
fclose($conn);
echo "$conn disconnected.".PHP_EOL;
}
}
while(true){
if (($conn = stream_socket_accept($sock)) === false) {
socket_close($sock);
exit(255);
}
echo "connected. current connections: {socket_manager::$connections}".PHP_EOL;
$fiber = socket_manager::create_fiber($conn);
while(true){
usleep(200000);// 200msごとにfiberをresumeする
echo 'main loop start'.PHP_EOL;
if(!$fiber->isSuspended()){
break;
}
$fiber->resume(date('Y-m-d\TH:i:s\Z'));// Fiber::suspend()した次の行から実行
}
echo 'main loop end'.PHP_EOL;
unset($fiber);
}
socket_close($sock);
Fibersといえども下記の図のようにシングルスレッドなのであまり実用的ではないですが、threadやforkと組み合わせれば使いどころがありそうです。
