ジェネレーターは PHP 5.5 で導入された新機能ですが、実際には非常に便利な機能です。
ジェネレータとイテレータは似ていますが、標準の PHP イテレータとは異なり、PHP ジェネレータは Iterator インターフェイスを実装するクラスを必要としないため、クラスのオーバーヘッドと負担が軽減されます。ジェネレーターは、要件に従って毎回反復する必要がある値を計算して出力します。これは、アプリケーションのパフォーマンスに大きな影響を与えます。標準的な PHP イテレーターがメモリー内で反復操作を頻繁に実行する場合を想像してください。これには、事前の処理が必要です。 - データセットの計算パフォーマンスが低下します。Excel テーブルデータの操作など、特定の方法を使用して大量のデータを計算する場合、パフォーマンスへの影響はさらに大きくなります。現時点では、ジェネレーターを使用すると、貴重なメモリ領域を占有することなく、後続の値を即座に計算して生成できます。
ジェネレーターは PHP 関数であるため、ジェネレーターを作成する方法は非常に簡単ですが、関数内で yield キーワードを 1 回以上使用する必要があります。通常の PHP 関数とは異なり、ジェネレーターは値を返すことはなく、値を生成するだけです。これは簡単なジェネレーターの実装です:
function getLaravelAcademy() { yield 'http://LaravelAcademy.org'; yield 'Laravel学院'; yield 'Laravel Academy';}
とてもシンプルです!このジェネレーター関数が呼び出されると、PHP は Generator クラスに属するオブジェクトを返します。このオブジェクトは、反復ごとに、Generator インスタンスに計算して、反復する次の値を提供するように要求します。ジェネレーターの優れた点は、値が生成されるたびにジェネレーターの内部状態が一時停止され、ジェネレーターから次の値が要求されると内部状態が復元されることです。ジェネレーターの内部状態は、関数定義の終わりに到達するか、空の return ステートメントに遭遇するまで、ストールと再開の間で切り替わります。次のコードを使用して、上記で定義したジェネレーターを呼び出して反復できます:
foreach(getLaravelAcademy() as $yieldedValue) { echo $yieldedValue, PHP_EOL;}
上記のコードの出力は次のとおりです:
http://LaravelAcademy.orgLaravel学院Laravel Academy
以下では、値の範囲を生成する単純な関数を実装します。これは、ジェネレーターがどのようにメモリを節約するかを説明します。まずイテレータを介して実装します:
function makeRange($length) { $dataSet = []; for ($i=0; $i<$length; $i++) { $dataSet[] = $i; } return $dataSet;}$customRange = makeRange(1000000);foreach ($customRange as $i) { echo $i . PHP_EOL;}
この時点で、実行中にエラーが報告され、単一の PHP プロセスのメモリ制限を超えていることが通知されます (100 万個の数値に対してメモリ スペースを提供する必要があります):
実装計画を改善し、ジェネレーターを使用してみましょう。 実装は次のとおりです:
function makeRange($length) { for ($i=0; $i<$length; $i++) { yield $i; }}foreach (makeRange(1000000) as $i) { echo $i . PHP_EOL;}
もう一度実行すると、ジェネレーターは毎回 1 つの整数にメモリを割り当てるだけで済むため、何のストレスもなく結果を出力できます。
また、一般的な使用例は、ジェネレーターを使用してストリーミング リソース (ファイル、オーディオなど) を反復処理することです。サイズが 4 GB の CSV ファイルを反復処理する必要があるとします。仮想プライベート サーバー (VPS) では PHP が使用できるメモリは 1 GB のみであるため、ファイル全体をメモリにロードすることはできません。次のコードは、操作:
function getRows($file) { $handle = fopen($file, 'rb'); if ($handle == FALSE) { throw new Exception(); } while (feof($handle) === FALSE) { yield fgetcsv($handle); } fclose($handle);}foreach ($getRows($file) as $row) { print_r($row);}
上記の例では、4GB の CSV ファイル全体をメモリに読み取るのではなく、一度に CSV ファイルの 1 行にのみメモリを割り当てます。
ジェネレーターは、機能の多様性と単純さの間の妥協点です。つまり、ジェネレーターは、データセット内で逆方向、早送り、または検索操作を実行することはできません。ジェネレーターは次の値を計算して生成します。ジェネレーターは、使用するシステム メモリの量が最小限であるため、大規模なデータ セットまたはシーケンスを反復処理する場合に最適です。ジェネレーターは、イテレーターが実行できるのと同じ単純なタスクを、より少ないコードで実行することもできます。
全体として、ジェネレーターは PHP に新しい機能を追加するものではありませんが、ジェネレーターを使用すると、特定のタスクが大幅に簡素化され、データ セットでの巻き戻し、早送り、検索機能の実行など、より多くの機能が必要な場合に使用するメモリが減ります。 Iterator インターフェイスを実装するクラスを自分で記述するか、PHP 標準ライブラリ (SPL) (http://php.net/manual/spl.iterators.php) のネイティブ イテレータを使用するのが最善です。