PHP 7のジェネレータの新しい特性yield-from&&return-values
6512 ワード
ジェネレータの委任
公式文書の説明を簡単に翻訳します.
PHP 7では、ジェネレータ依頼(yield from)により、他のジェネレータ、反復可能なオブジェクト、配列を外層ジェネレータに依頼することができる.外層のジェネレータは、yieldから委任された値を順次yieldに委任し、yield自体で定義された値を続行します.
yield fromを使用すると、比較的明確なジェネレータネストを記述するのに便利ですが、コードネスト呼び出しは複雑なシステムを記述するために必要です.前の例:
以上、出力されます.
もちろん、yield fromが親子ジェネレータに双方向のチャネルを確立するため、内部ジェネレータは親ジェネレータから送信された情報または異常を受け入れることもできます.あまり言わないで、前の例:
出力は前の例と同じです.
ジェネレータの戻り値
ジェネレータが反復されたり、returnキーに実行されたりすると、このジェネレータに値が返されます.この戻り値を取得するには、次の2つの方法があります.$ret=Generator::getReturn()メソッドを使用します. $ret=yield from Generator()式を使用します.
前の例:
出力結果は貼らないので、皆さんは推測していると思います.
yield fromとreturnを組み合わせることで、yieldの書き方が普段私たちが書いている同期モードのコードに似ていることがわかります.結局、これがPHPジェネレータの特性の原因の一つですね.
ブロックされていないWebサーバ
2015年に戻って、鳥兄のブログに転載された「PHPで協程を使ってマルチタスクスケジューリングを実現する」.PHP 5の反復生成器,コプロセッサを紹介し,単純な非ブロックウェブサーバを実現した.(リンクは文末参照)
PHP 7の2つの新しい特性を利用して、このwebサーバを書き換えるには、100行以上のコードしか必要ありません.
コードは次のとおりです.
参考資料 [1] http://www.php.net/manual/zh/... [2] http://www.laruence.com/2015/... [3] http://blog.csdn.net/u0101613...
公式文書の説明を簡単に翻訳します.
PHP 7では、ジェネレータ依頼(yield from)により、他のジェネレータ、反復可能なオブジェクト、配列を外層ジェネレータに依頼することができる.外層のジェネレータは、yieldから委任された値を順次yieldに委任し、yield自体で定義された値を続行します.
yield fromを使用すると、比較的明確なジェネレータネストを記述するのに便利ですが、コードネスト呼び出しは複雑なシステムを記述するために必要です.前の例:
";
yield;
}
}
function task() {
yield from echoTimes('foo', 10); // print foo ten times
echo "---
";
yield from echoTimes('bar', 5); // print bar five times
}
foreach (task() as $item) {
;
}
以上、出力されます.
foo iteration 1
foo iteration 2
foo iteration 3
foo iteration 4
foo iteration 5
foo iteration 6
foo iteration 7
foo iteration 8
foo iteration 9
foo iteration 10
---
bar iteration 1
bar iteration 2
bar iteration 3
bar iteration 4
bar iteration 5
もちろん、yield fromが親子ジェネレータに双方向のチャネルを確立するため、内部ジェネレータは親ジェネレータから送信された情報または異常を受け入れることもできます.あまり言わないで、前の例:
";
}
}
function task2() {
yield from echoMsg('foo');
echo "---
";
yield from echoMsg('bar');
}
$gen = task2();
foreach (range(1,10) as $num) {
$gen-?>send($num);
}
$gen->send(null);
foreach (range(1,5) as $num) {
$gen->send($num);
}
//$gen->send("hello world"); //try it ,gay
出力は前の例と同じです.
ジェネレータの戻り値
ジェネレータが反復されたり、returnキーに実行されたりすると、このジェネレータに値が返されます.この戻り値を取得するには、次の2つの方法があります.
前の例:
";
yield;
}
return "$msg the end value : $i
";
}
function task() {
$end = yield from echoTimes('foo', 10);
echo $end;
$gen = echoTimes('bar', 5);
yield from $gen;
echo $gen-?>getReturn();
}
foreach (task() as $item) {
;
}
出力結果は貼らないので、皆さんは推測していると思います.
yield fromとreturnを組み合わせることで、yieldの書き方が普段私たちが書いている同期モードのコードに似ていることがわかります.結局、これがPHPジェネレータの特性の原因の一つですね.
ブロックされていないWebサーバ
2015年に戻って、鳥兄のブログに転載された「PHPで協程を使ってマルチタスクスケジューリングを実現する」.PHP 5の反復生成器,コプロセッサを紹介し,単純な非ブロックウェブサーバを実現した.(リンクは文末参照)
PHP 7の2つの新しい特性を利用して、このwebサーバを書き換えるには、100行以上のコードしか必要ありません.
コードは次のとおりです.
socket = $socket;
$this->masterCoSocket = $master ?? $this;
}
public function accept()
{
$isSelect = yield from $this->onRead();
$acceptS = null;
if ($isSelect && $as = stream_socket_accept($this->socket, 0)) {
$acceptS = new CoSocket($as, $this);
}
return $acceptS;
}
public function read($size)
{
yield from $this->onRead();
yield ($data = fread($this->socket, $size));
return $data;
}
public function write($string)
{
yield from $this->onWriter();
yield fwrite($this->socket, $string);
}
public function close()
{
unset($this->masterCoSocket->streamPoolRead[(int)$this->socket]);
unset($this->masterCoSocket->streamPoolWrite[(int)$this->socket]);
yield ($success = @fclose($this->socket));
return $success;
}
public function onRead($timeout = null)
{
$this->masterCoSocket->streamPoolRead[(int)$this->socket] = $this->socket;
$pool = $this->masterCoSocket->streamPoolRead;
$rSocks = [];
$wSocks = $eSocks = null;
foreach ($pool as $item) {
$rSocks[] = $item;
}
yield ($num = stream_select($rSocks, $wSocks, $eSocks, $timeout));
return $num;
}
public function onWriter($timeout = null)
{
$this->masterCoSocket->streamPoolWrite[(int)$this->socket] = $this->socket;
$pool = $this->masterCoSocket->streamPoolRead;
$wSocks = [];
$rSocks = $eSocks = null;
foreach ($pool as $item) {
$wSocks[] = $item;
}
yield ($num = stream_select($rSocks, $wSocks, $eSocks, $timeout));
return $num;
}
public function onRequest()
{
/** @var self $socket */
$socket = yield from $this->accept();
if (empty($socket)) {
return false;
}
$data = yield from $socket->read(8192);
$response = call_user_func($this->handleCallback, $data);
yield from $socket->write($response);
return yield from $socket->close();
}
public static function start($port, callable $callback)
{
echo "Starting server at port $port...
";
$socket = @stream_socket_server("tcp://0.0.0.0:$port", $errNo, $errStr);
if (!$socket) throw new Exception($errStr, $errNo);
stream_set_blocking($socket, 0);
$coSocket = new self($socket);
$coSocket->handleCallback = $callback;
function gen($coSocket)
{
/** @var self $coSocket */
while (true) yield from $coSocket->onRequest();
}
foreach (gen($coSocket) as $item){};
}
}
CoSocket::start(8000, function ($data) {
$response = <<
参考資料