この記事では、ある参考値のあるPHPのマルチタスクコルーチン処理を中心に紹介しますので、皆さんに共有しますので、困っている友達は参考にしてください
それでは、始めましょう!
これがこの記事で説明する内容です。ただし、より単純で親しみのある例から始めましょう。
すべては配列から始まります
簡単なトラバーサルで配列を使用できます:
$array = ["foo", "bar", "baz"]; foreach ($array as $key => $value) { print "item: " . $key . "|" . $value . "\n"; } for ($i = 0; $i <p>これは、日常のコーディングで使用する基本的な実装です。配列を走査することで、各要素のキー名とキー値を取得できます。 </p><p>もちろん、配列がいつ使用できるかを知りたい場合に限ります。 PHP には便利な組み込み関数が用意されています。 </p><pre class="brush:php;toolbar:false">print is_array($array) ? "yes" : "no"; // yes
配列に似た処理
同じ方法で一部のデータを走査する必要がある場合がありますが、それらは配列型ではありません。たとえば、DOMDocument クラスの処理:
$document = new DOMDocument(); $document->loadXML("<p></p>"); $elements = $document->getElementsByTagName("p"); print_r($elements); // DOMNodeList Object ( [length] => 1 )
これは明らかに配列ではありませんが、length 属性があります。配列のように走査することはできるでしょうか?次の特別なインターフェイスを実装しているかどうかを判断できます:
print ($elements instanceof Traversable) ? "yes" : "no"; // yes
これは非常に便利です。走査不可能なデータを反復処理するときにエラーが発生することはありません。処理する前にテストするだけで十分です。
しかし、これにより別の疑問が生じます: カスタム クラスにもこの関数を持たせることはできるでしょうか?答えは「はい」です!最初の実装メソッドは次のようになります:
class MyTraversable implements Traversable { // 在这里编码... }
このクラスを実行すると、エラー メッセージが表示されます:
PHP 致命的なエラー: クラス MyTraversable は、Iterator またはIteratorAggregate
Iterator(Iterator)
Traversable を直接実装することはできませんが、2 番目のオプションを試すことができます:
class MyTraversable implements Iterator { // 在这里编码... }
このインターフェイスには # が必要です。 ##5 メソッド。イテレータを完成させましょう:
class MyTraversable implements Iterator { protected $data; protected $index = 0; public function __construct($data) { $this->data = $data; } public function current() { return $this->data[$this->index]; } public function next() { return $this->data[$this->index++]; } public function key() { return $this->index; } public function rewind() { $this->index = 0; } public function valid() { return $this->index data); } }ここでいくつかのことに注意する必要があります:
- コンストラクターによって渡された
$データを保存する必要がありますメソッド 配列を使用して、後でそこから要素を取得できるようにします。
current または next 要素を追跡するには、内部インデックス (またはポインター) も必要です。
rewind() index プロパティのみをリセットするため、current() および next() 正しく動作します。
- キー名は数値型に限定されません。例を単純にするために、ここでは配列のインデックス付けが使用されています。
$iterator = new MyIterator(["foo", "bar", "baz"]); foreach ($iterator as $key => $value) { print "item: " . $key . "|" . $value . "\n"; }これは手間がかかるように思えるかもしれませんが、配列のように使用することができます
foreach A /for 関数の簡潔な実装。
IteratorAggregate (集約反復子) 2 番目のインターフェイスによってスローされたTraversable 例外をまだ覚えていますか? Iterator インターフェイスを実装するよりも高速な実装を見てみましょう:
class MyIteratorAggregate implements IteratorAggregate { protected $data; public function __construct($data) { $this->data = $data; } public function getIterator() { return new ArrayIterator($this->data); } }ここでは不正行為を行っています。完全な
Iterator を実装するのではなく、ArrayIterator() を通じて修飾します。ただし、完全な Iterator を実装する場合に比べて、コードが大幅に簡素化されます。
$content = file_get_contents(__FILE__); $lines = explode("\n", $content); foreach ($lines as $i => $line) { print $i . ". " . $line . "\n"; }このコードは、ファイル自体を読み取り、各行の行番号とコードを出力します。それなら、発電機を使ってみませんか!
function lines($file) { $handle = fopen($file, 'r'); while (!feof($handle)) { yield trim(fgets($handle)); } fclose($handle); } foreach (lines(__FILE__) as $i => $line) { print $i . ". " . $line . "\n"; }これはもっと複雑だと思います。それはいいのですが、これは
file_get_contents() 関数を使用していないためです。ジェネレーターは関数のように見えますが、yield キーワードを取得するたびに実行を停止します。
ジェネレータはイテレータに少し似ています:print_r(lines(__FILE__)); // Generator Object ( )これはイテレータではありませんが、
Generatorです。内部的にはどのようなメソッドが定義されていますか?
print_r(get_class_methods(lines(__FILE__))); // Array // ( // [0] => rewind // [1] => valid // [2] => current // [3] => key // [4] => next // [5] => send // [6] => throw // [7] => __wakeup // )大きなファイルを読み込んでから
memory_get_peak_usage() を使用すると、ファイルの大きさに関係なく、ジェネレーター コードが固定量のメモリを使用することがわかります。一行ずつ進んでいきます。代わりに、file_get_contents() 関数を使用してファイル全体を読み取ると、より多くのメモリが使用されます。このような処理を繰り返すときにジェネレーターが有利になるのはここです。Send (データ送信)ジェネレーターにデータを送信できます。次のジェネレーターを見てください:
<?php $generator = call_user_func(function() { yield "foo"; }); print $generator->current() . "\n"; // foo
注意这里我们如何在 call_user_func() 函数中封装生成器函数的?这里仅仅是一个简单的函数定义,然后立即调用它获取一个新的生成器实例...
我们已经见过 yield 的用法。我们可以通过扩展这个生成器来接收数据:
$generator = call_user_func(function() { $input = (yield "foo"); print "inside: " . $input . "\n"; }); print $generator->current() . "\n"; $generator->send("bar");
数据通过 yield 关键字传入和返回。首先,执行 current() 代码直到遇到 yield,返回 foo。send() 将输出传入到生成器打印输入的位置。你需要习惯这种用法。
抛出异常(Throw)
由于我们需要同这些函数进行交互,可能希望将异常推送到生成器中。这样这些函数就可以自行处理异常。
看看下面这个示例:
$multiply = function($x, $y) { yield $x * $y; }; print $multiply(5, 6)->current(); // 30
现在让我们将它封装到另一个函数中:
$calculate = function ($op, $x, $y) use ($multiply) { if ($op === 'multiply') { $generator = $multiply($x, $y); return $generator->current(); } }; print $calculate("multiply", 5, 6); // 30
这里我们通过一个普通闭包将乘法生成器封装起来。现在让我们验证无效参数:
$calculate = function ($op, $x, $y) use ($multiply) { if ($op === "multiply") { $generator = $multiply($x, $y); if (!is_numeric($x) || !is_numeric($y)) { throw new InvalidArgumentException(); } return $generator->current(); } }; print $calculate('multiply', 5, 'foo'); // PHP Fatal error...
如果我们希望能够通过生成器处理异常?我们怎样才能将异常传入生成器呢!
$multiply = function ($x, $y) { try { yield $x * $y; } catch (InvalidArgumentException $exception) { print "ERRORS!"; } }; $calculate = function ($op, $x, $y) use ($multiply) { if ($op === "multiply") { $generator = $multiply($x, $y); if (!is_numeric($x) || !is_numeric($y)) { $generator->throw(new InvalidArgumentException()); } return $generator->current(); } }; print $calculate('multiply', 5, 'foo'); // PHP Fatal error...
棒呆了!我们不仅可以像迭代器一样使用生成器。还可以通过它们发送数据并抛出异常。它们是可中断和可恢复的函数。有些语言把这些函数叫做……
我们可以使用协程(coroutines)来构建异步代码。让我们来创建一个简单的任务调度程序。首先我们需要一个 Task 类:
class Task { protected $generator; public function __construct(Generator $generator) { $this->generator = $generator; } public function run() { $this->generator->next(); } public function finished() { return !$this->generator->valid(); } }
Task 是普通生成器的装饰器。我们将生成器赋值给它的成员变量以供后续使用,然后实现一个简单的 run() 和 finished() 方法。run() 方法用于执行任务,finished() 方法用于让调度程序知道何时终止运行。
然后我们需要一个 Scheduler 类:
class Scheduler { protected $queue; public function __construct() { $this->queue = new SplQueue(); } public function enqueue(Task $task) { $this->queue->enqueue($task); } pulic function run() { while (!$this->queue->isEmpty()) { $task = $this->queue->dequeue(); $task->run(); if (!$task->finished()) { $this->queue->enqueue($task); } } } }
Scheduler 用于维护一个待执行的任务队列。run() 会弹出队列中的所有任务并执行它,直到运行完整个队列任务。如果某个任务没有执行完毕,当这个任务本次运行完成后,我们将再次入列。
SplQueue 对于这个示例来讲再合适不过了。它是一种 FIFO(先进先出:fist in first out) 数据结构,能够确保每个任务都能够获取足够的处理时间。
我们可以像这样运行这段代码:
$scheduler = new Scheduler(); $task1 = new Task(call_user_func(function() { for ($i = 0; $i enqueue($task1); $scheduler->enqueue($task2); $scheduler->run();
运行时,我们将看到如下执行结果:
task 1: 0 task 1: 1 task 2: 0 task 2: 1 task 1: 2 task 2: 2 task 2: 3 task 2: 4 task 2: 5
这几乎就是我们想要的执行结果。不过有个问题发生在首次运行每个任务时,它们都执行了两次。我们可以对 Task 类稍作修改来修复这个问题:
class Task { protected $generator; protected $run = false; public function __construct(Generator $generator) { $this->generator = $generator; } public function run() { if ($this->run) { $this->generator->next(); } else { $this->generator->current(); } $this->run = true; } public function finished() { return !$this->generator->valid(); } }
我们需要调整首次 run() 方法调用,从生成器当前有效的指针读取运行。后续调用可以从下一个指针读取运行...
有些人基于这个思路实现了一些超赞的类库。我们来看看其中的两个...
RecoilPHP
RecoilPHP 是一套基于协程的类库,它最令人印象深刻的是用于 ReactPHP 内核。可以将事件循环在 RecoilPHP 和 RecoilPHP 之间进行交换,而你的程序无需架构上的调整。
我们来看一下 ReactPHP 异步 DNS 解决方案:
function resolve($domain, $resolver) { $resolver ->resolve($domain) ->then(function ($ip) use ($domain) { print "domain: " . $domain . "\n"; print "ip: " . $ip . "\n"; }, function ($error) { print $error . "\n"; }) } function run() { $loop = React\EventLoop\Factory::create(); $factory = new React\Dns\Resolver\Factory(); $resolver = $factory->create("8.8.8.8", $loop); resolve("silverstripe.org", $resolver); resolve("wordpress.org", $resolver); resolve("wardrobecms.com", $resolver); resolve("pagekit.com", $resolver); $loop->run(); } run();
resolve() 接收域名和 DNS 解析器,并使用 ReactPHP 执行标准的 DNS 查找。不用太过纠结与 resolve() 函数内部。重要的是这个函数不是生成器,而是一个函数!
run() 创建一个 ReactPHP 事件循环,DNS 解析器(这里是个工厂实例)解析若干域名。同样,这个也不是一个生成器。
想知道 RecoilPHP 到底有何不同?还希望掌握更多细节!
use Recoil\Recoil; function resolve($domain, $resolver) { try { $ip = (yield $resolver->resolve($domain)); print "domain: " . $domain . "\n"; print "ip: " . $ip . "\n"; } catch (Exception $exception) { print $exception->getMessage() . "\n"; } } function run() { $loop = (yield Recoil::eventLoop()); $factory = new React\Dns\Resolver\Factory(); $resolver = $factory->create("8.8.8.8", $loop); yield [ resolve("silverstripe.org", $resolver), resolve("wordpress.org", $resolver), resolve("wardrobecms.com", $resolver), resolve("pagekit.com", $resolver), ]; } Recoil::run("run");
通过将它集成到 ReactPHP 来完成一些令人称奇的工作。每次运行 resolve() 时,RecoilPHP 会管理由 $resoler->resolve() 返回的 promise 对象,然后将数据发送给生成器。此时我们就像在编写同步代码一样。与我们在其他一步模型中使用回调代码不同,这里只有一个指令列表。
RecoilPHP 知道它应该管理一个有执行 run() 函数时返回的 yield 数组。RoceilPHP 还支持基于协程的数据库(PDO)和日志库。
IcicleIO
IcicleIO 为了一全新的方案实现 ReactPHP 一样的目标,而仅仅使用协程功能。相比 ReactPHP 它仅包含极少的组件。但是,核心的异步流、服务器、Socket、事件循环特性一个不落。
让我们看一个 socket 服务器示例:
use Icicle\Coroutine\Coroutine; use Icicle\Loop\Loop; use Icicle\Socket\Client\ClientInterface; use Icicle\Socket\Server\ServerInterface; use Icicle\Socket\Server\ServerFactory; $factory = new ServerFactory(); $coroutine = Coroutine::call(function (ServerInterface $server) { $clients = new SplObjectStorage(); $handler = Coroutine::async( function (ClientInterface $client) use (&$clients) { $clients->attach($client); $host = $client->getRemoteAddress(); $port = $client->getRemotePort(); $name = $host . ":" . $port; try { foreach ($clients as $stream) { if ($client !== $stream) { $stream->write($name . "connected.\n"); } } yield $client->write("Welcome " . $name . "!\n"); while ($client->isReadable()) { $data = trim(yield $client->read()); if ("/exit" === $data) { yield $client->end("Goodbye!\n"); } else { $message = $name . ":" . $data . "\n"; foreach ($clients as $stream) { if ($client !== $stream) { $stream->write($message); } } } } } catch (Exception $exception) { $client->close($exception); } finally { $clients->detach($client); foreach ($clients as $stream) { $stream->write($name . "disconnected.\n"); } } } ); while ($server->isOpen()) { $handler(yield $server->accept()); } }, $factory->create("127.0.0.1", 6000)); Loop::run();
据我所知,这段代码所做的事情如下:
在 127.0.0.1 和 6000 端口创建一个服务器实例,然后将其传入外部生成器.
外部生成器运行,同时服务器等待新连接。当服务器接收一个连接它将其传入内部生成器。
内部生成器写入消息到 socket。当 socket 可读时运行。
每次 socket 向服务器发送消息时,内部生成器检测消息是否是退出标识。如果是,通知其他 socket。否则,其它 socket 发送这个相同的消息。
打开命令行终端输入 nc localhost 6000 查看执行结果!
该示例使用 SplObjectStorage 跟踪 socket 连接。这样我们就可以向所有 socket 发送消息。
这个话题可以包含很多内容。希望您能看到生成器是如何创建的,以及它们如何帮助编写迭代程序和异步代码。
如果你有问题,可以随时问我。
相关推荐:
以上がPHPのマルチタスクコルーチン処理の解析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

PHPは、動的なWeb開発およびサーバー側のアプリケーションに使用されるサーバー側のスクリプト言語です。 1.PHPは、編集を必要とせず、迅速な発展に適した解釈言語です。 2。PHPコードはHTMLに組み込まれているため、Webページの開発が簡単になりました。 3。PHPプロセスサーバー側のロジック、HTML出力を生成し、ユーザーの相互作用とデータ処理をサポートします。 4。PHPは、データベースと対話し、プロセスフォームの送信、サーバー側のタスクを実行できます。

PHPは過去数十年にわたってネットワークを形成しており、Web開発において重要な役割を果たし続けます。 1)PHPは1994年に発信され、MySQLとのシームレスな統合により、開発者にとって最初の選択肢となっています。 2)コア関数には、動的なコンテンツの生成とデータベースとの統合が含まれ、ウェブサイトをリアルタイムで更新し、パーソナライズされた方法で表示できるようにします。 3)PHPの幅広いアプリケーションとエコシステムは、長期的な影響を促進していますが、バージョンの更新とセキュリティの課題にも直面しています。 4)PHP7のリリースなど、近年のパフォーマンスの改善により、現代の言語と競合できるようになりました。 5)将来的には、PHPはコンテナ化やマイクロサービスなどの新しい課題に対処する必要がありますが、その柔軟性とアクティブなコミュニティにより適応性があります。

PHPの中心的な利点には、学習の容易さ、強力なWeb開発サポート、豊富なライブラリとフレームワーク、高性能とスケーラビリティ、クロスプラットフォームの互換性、費用対効果が含まれます。 1)初心者に適した学習と使用が簡単。 2)Webサーバーとの適切な統合および複数のデータベースをサポートします。 3)Laravelなどの強力なフレームワークを持っています。 4)最適化を通じて高性能を達成できます。 5)複数のオペレーティングシステムをサポートします。 6)開発コストを削減するためのオープンソース。

PHPは死んでいません。 1)PHPコミュニティは、パフォーマンスとセキュリティの問題を積極的に解決し、PHP7.xはパフォーマンスを向上させます。 2)PHPは最新のWeb開発に適しており、大規模なWebサイトで広く使用されています。 3)PHPは学習しやすく、サーバーはうまく機能しますが、タイプシステムは静的言語ほど厳格ではありません。 4)PHPは、コンテンツ管理とeコマースの分野で依然として重要であり、エコシステムは進化し続けています。 5)OpcacheとAPCを介してパフォーマンスを最適化し、OOPと設計パターンを使用してコードの品質を向上させます。

PHPとPythonには独自の利点と短所があり、選択はプロジェクトの要件に依存します。 1)PHPは、Web開発に適しており、学習しやすく、豊富なコミュニティリソースですが、構文は十分に近代的ではなく、パフォーマンスとセキュリティに注意を払う必要があります。 2)Pythonは、簡潔な構文と学習が簡単なデータサイエンスと機械学習に適していますが、実行速度とメモリ管理にはボトルネックがあります。

PHPは動的なWebサイトを構築するために使用され、そのコア関数には次のものが含まれます。1。データベースに接続することにより、動的コンテンツを生成し、リアルタイムでWebページを生成します。 2。ユーザーのインタラクションを処理し、提出をフォームし、入力を確認し、操作に応答します。 3.セッションとユーザー認証を管理して、パーソナライズされたエクスペリエンスを提供します。 4.パフォーマンスを最適化し、ベストプラクティスに従って、ウェブサイトの効率とセキュリティを改善します。

PHPはMySQLIおよびPDO拡張機能を使用して、データベース操作とサーバー側のロジック処理で対話し、セッション管理などの関数を介してサーバー側のロジックを処理します。 1)MySQLIまたはPDOを使用してデータベースに接続し、SQLクエリを実行します。 2)セッション管理およびその他の機能を通じて、HTTPリクエストとユーザーステータスを処理します。 3)トランザクションを使用して、データベース操作の原子性を確保します。 4)SQLインジェクションを防ぎ、例外処理とデバッグの閉鎖接続を使用します。 5)インデックスとキャッシュを通じてパフォーマンスを最適化し、読みやすいコードを書き、エラー処理を実行します。

PHPで前処理ステートメントとPDOを使用すると、SQL注入攻撃を効果的に防ぐことができます。 1)PDOを使用してデータベースに接続し、エラーモードを設定します。 2)準備方法を使用して前処理ステートメントを作成し、プレースホルダーを使用してデータを渡し、メソッドを実行します。 3)結果のクエリを処理し、コードのセキュリティとパフォーマンスを確保します。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

mPDF
mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

AtomエディタMac版ダウンロード
最も人気のあるオープンソースエディター

EditPlus 中国語クラック版
サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

WebStorm Mac版
便利なJavaScript開発ツール
