搜尋
首頁後端開發php教程PHP yield 協程 生成器用法的了解

PHP yield 協程 生成器用法的了解

Jul 02, 2020 pm 06:02 PM
phpyield協程產生器

PHP yield 協程 生成器用法的了解

寫在前面

這篇文章,要跟大家探討的是PHP yield 在生成器用法,不帶foreach for, while 迴圈的那種。就討論 yield 將一個函數變成生成器的用法。

關於yield 特性,是在開發 PHP5 時被提上日程,PHP5.5 版本正式加入。

關於yield的使用,我看到大部分文章都停留在,使用yield如何在foreach#中穿出數據,今天想跟大家講講生成器所有文法。

相關學習推薦:PHP程式設計從入門到精通

官網講解

產生器允許你在foreach 程式碼區塊中寫程式碼來迭代一組資料而不需要在記憶體中創建一個陣列, 那會使你的記憶體達到上限,或者會佔據可觀的處理時間。相反,你可以寫一個生成器函數,就像一個普通的自定義函數一樣, 和普通函數只返回一次不同的是, 生成器可以根據需要yield 多次,以便生成需要迭代的值。

看了下官網對他講解:php.net 生成器語法  . 每個字都認識,但似乎還是體會到它講的內涵。官網我們主要看兩部分內容:

  1. yield 的語法。

  2. 程式碼範例。

先說語法, yield 的左邊是賦值語句,右邊可以是值(也可是表達式) 。而yield 會先執行右邊的表達式,並且把值$value送到生成器外面。當生成器收到值後,會執行yield左邊的語句,賦值給$data.

<?phpfunction func(){
    $data = (yield [$express]);}

語法就這樣,估計大家還是有些懵,那就看看官網下面代碼例子吧,我看裡面例子參差不齊。

注意yield 外麵包的這一層括號,如果是在php5.5,右邊$express的優先權是判斷,可能會比左側$data的賦值語句低的。所以在php5用yield,yield 右邊是可運行表達式,左側需要接受回傳並賦值,那麼這個括號是必要的。在php7不會有這個問題。

透過例子來了解它

不論是學人類語言,電腦語言,都是模仿開始

#對於一個用人類語言來描述,都不那麼明晰時,所以那就透過例子告訴你它能做什麼,不能做什麼。

相關程式碼,我放到gitee了,希望你能複製到你本地運行下,親自運行感受下,有助於了理解接下來的內容。

git clone gitee.com/xupaul/PHP-generator-yie...

可以產生Generator

先定義一個函數,在函數內寫個yield 關鍵字,並將這個函數呼叫賦值給一個變數。一個生成器就產生了。

程式碼 /php-yield-test/yieldFunctions.php 是生成器依照不同語法組合定義了多個生成器。

測試程式碼 /php-yield-test/whatIsGenerator.php,用來檢查哪些函式能構成生成器,哪些不能。運行結果如下

test result

  1. 函數內必須有 yield 關鍵字,函數可以是全劇函數,或是類別的方法。
  2. 即使 yield 肯定不會被執行,也會產生生成器。請參閱:yield_func4
  3. 光禿禿 的 yield 關鍵字就行(不向外送出,不處理外面的輸入)。請參閱: yield_func2
  4. 函數內使用生成器並不能讓自己也成為生成器,請參閱:yield_func5
  5. eval函數中直接執行yield 會報錯, 請參閱:yield_func11

是的,函數內有沒有foreach,while,for 語句都不是關鍵,關鍵是yield. 生成器的型別判斷用$gen instanceof Generator

生成器的函數

Generator 物件是從generators傳回的.

Generator 物件不能透過new 實例化.

  • Generator::current — 返回当前产生的值
  • Generator::key — 返回当前产生的键
  • Generator::next — 生成器继续执行
  • Generator::rewind — 重置迭代器
  • Generator::send — 向生成器中传入一个值
  • Generator::throw — 向生成器中抛入一个异常
  • Generator::valid — 检查迭代器是否被关闭
  • Generator::__wakeup — 序列化回调
  • Gengerator::getReturn - Get the return value of a generator

摘自 php.net generator

看着以上方法,是不想起了Iterator, 他们的确很像。同时注意,官网zh语言版本的文档没有索引方法getReturn,访问也是404。文档以en版为准,ch做参考。

以上就是生成器所有的方法,我们一个个来看。

测试方法代码 /php-yield-test/generatorMothod.php, 这里面对每个方法都有使用举例,运行结果如下。

run result 2

run result 3

好接下来对举例做个一一讲解。

Generator::current

  • 返回当前产生的值
<?phpfunction yield_func(){
    yield 12;
    return &#39;a&#39;;}$gen = yield_func();$re = $gen->current();echo &#39;current return : &#39; . $re;

输出:

current return : 12

看到 php-yield-test/generatorMothod.php  代码。

通过第一个代码事例,可得,对一个generator调用current方法,才算真正开始执行。执行到yield为止。如果不能命中yield,则执行到函数结束。

非generoator会立马执行并得到结果,而非一个生成器对象。

通过例子2,调用current一次,两次呢,第一次可以看到代码执行日志,第二次,只是把上一次的结果返回给我们而已,并不是让该生成器重新执行。

通过例子1,调用该函数还会获取到返回值,返回的内容就是 yield 表达式左边的内容。如果表达式无内容,则是NULL.

Generator::send

  • 向生成器yield点中传入一个值,并返回下一次current值。
<?phpfunction yield_func(){
    $data = yield 12;
    echo &#39;get yield data: &#39; . $data;
    return &#39;a&#39;;}$gen = yield_func();$re = $gen->current();$gen->send(32);

输出:

get yield data: 32

例子3,是一个current,send的常规调用。调用current代码运行yield等到用户send输入参数。接收到输入后,继续运行。current能够接收到yield弹出的值,send返回值为空。

例子4,直接调用send,相当于调用current,send。不过current的返回值,并不会通过send传给用户。

例子21中,可以看到直接调用send(1),会运行生成器,并向第一个yield处输入1,继续运行至下一个yield的返回值value。所以,$gen->send(2),和 $gen->current() 结果都是同一个值。

也就是说:跳过current,直接调用send,会丢失第一次yield的弹出值。

Generator::next

  • 跳过中断,并让生成器继续执行
<?phpfunction yield_func(){
    echo &#39;run to code line: &#39; . __LINE__ . PHP_EOL;
    yield;
    echo &#39;run to code line: &#39; . __LINE__ . PHP_EOL;
    return $result;}$gen = yield_func();$gen->current();echo &#39;current called&#39; . PHP_EOL;$gen->next();

输出:

run to code line: 4current called
run to code line: 6

例子5,这是一个较为常规的调用,调用current代码运行yield等到用户输入,这是调用next跳过,让代码继续运行。

例子6,直接调用next,相当于调用currentnext。而且通过最后打印$result, 我们发现怎么有点像在调用 $gen->send(NULL);

Generator::rewind

  • 重置迭代器
<?phpfunction yield_func(){
    echo &#39;run to code line: &#39; . __LINE__ . PHP_EOL;
    $result = yield 12;
    echo &#39;run to code line: &#39; . __LINE__ . PHP_EOL;}$gen = yield_func();echo &#39;call yield_func rewind &#39; . PHP_EOL;$gen->rewind();

输出:

call yield_func rewind 
run to code line: 4

例子7,8 中,发现调用该方法,会导致隐式调用current

例子9 中,发现在执行过一个yield代码段后,再次调用该方法,会导致报错(哪怕该 生成器已结束)。

Generator::throw

  • 向生成器中抛入一个异常
<?phpfunction yield_func(){
    try {
        $re = yield &#39;exception&#39;;
    } catch (Exception $e) {
        echo &#39;catched exception msg: &#39; .$e->getMessage();
    }}$gen = yield_func();$gen->throw(new \Exception(&#39;new yield  exception&#39;));

输出:

catched exception msg: new yield  exception

通过以上简单的例子可得,throw 就是让yield这行代码产生异常,让外面的try catch 捕获我们生成的那个异常。

例子11中,构造生成器,并调用current方法,运行到yield处,再调用throw,就能捕获到异常。

例子12中,当调用send方法,跳过函数内yield代码时,再调用throw传入异常,就没法捕获了。

Generator::valid

  • 检查迭代器是否被关闭
<?phpfunction yield_func(){
    yield 12;
    return &#39;a&#39;;}$gen = yield_func();$gen->send(1);$check = $gen->valid();echo &#39;the generator valid ? &#39; . intval($check);

输出:

the generator valid ? 0

例子12中,发现current被隐式调用。

例子13中,可得,当生成器运行到yield代码段时,用valid函数检查,都会返回true

所以,别问我是否已运行,问就是运行。该方法用来获取是否关闭状态,不是 是否运行状态!运行到底,运行到return就是 关闭状态。

Generator::key

  • 返回当前产生的键
<?phpfunction yield_func(){
    yield 1 => &#39;abc&#39;;}$gen = yield_func();echo &#39;value is :&#39; . $gen->current() . PHP_EOL;echo &#39;key is: &#39; . $gen->key() . PHP_EOL;

输出:

value is :abc
key is: 1

从以上例子中,可得yield可显示设置返回的key.

例子15 中,发现key的分发规律和PHP数组键值发放策略是差不多的,默认从0开始,未指定则是以上一个数字key+1作为当前的key.

例子16 中,我们又发现current被隐式调用。

Generator::__wakeup

  • Generator::__wakeup — 序列化回调
<?phpfunction yield_func(){
    yield 1 => &#39;abc&#39;;}$gen = yield_func();try {$ser = serialize($gen);} catch (\Exception $e) {
    print_r($e->getMessage());}

输出:

Serialization of &#39;Generator&#39; is not allowed

这是一个魔术方法,见 PHP 魔术方法,也就是说 生成器 不能被序列化成一个字符串。

例子17就不用说了,看下例子18,看样子序列化成功了。也就是说一个生成器做为一个方法可以被序列化,当函数变成生成器时,就不能被序列化了。

Generator::getReturn

<?phpfunction yield_func(){
    yield 1 => &#39;abc&#39;;
    return 32;}$gen = yield_func();$gen->send(0);echo &#39;call yield_func return, and get: &#39; . $gen->getReturn();

输出:

call yield_func return, and get: 32

该函数就是获取生成器最后的返回值。如果没有return语句,或者没有执行到return语句,调用该函数得到的就是NULL。

例子19 可得,getReturn 能够获取到生成器最后的返回值。

例子19、20 可得,当生成器没有执行到return语句,或者没有执行到最后时,调用getReturn是会导致报错。

综上所述

到这里,我们就发现rewind,next__wakeup 这两个函数感觉没啥叼用呢,为啥还存在呢,因为Generator继承Iterator,自然就有了rewind, next方法,PHP 虽然支持方法覆盖,但子类的访问修饰符 不能缩紧,所以Generator只能重写这两个方法。 __wakeup 继承自 stdClass

状态转换

看图:

PHP yield 生命周期图

画了两个状态转换图,上面的要细致,繁复一点。下面的精简版,便于快速理解。

总结

以上就是关于 PHP 生成器的基础内容,希望你看了后对它有更进一步认识。下一讲,我们手把手一起来做一个任务调度器,实战一下。

有问题欢迎提问,谢谢大家!

PHP yield 協程 生成器用法的了解

以上是PHP yield 協程 生成器用法的了解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:learnku。如有侵權,請聯絡admin@php.cn刪除
高流量網站的PHP性能調整高流量網站的PHP性能調整May 14, 2025 am 12:13 AM

TheSecretTokeEpingAphp-PowerEdwebSiterUnningSmoothlyShyunderHeavyLoadInVolvOLVOLVOLDEVERSALKEYSTRATICES:1)emplactopCodeCachingWithOpcachingWithOpCacheToreCescriptexecution Time,2)使用atabasequercachingCachingCachingWithRedataBasEndataBaseLeSendataBaseLoad,3)

PHP中的依賴注入:初學者的代碼示例PHP中的依賴注入:初學者的代碼示例May 14, 2025 am 12:08 AM

你應該關心DependencyInjection(DI),因為它能讓你的代碼更清晰、更易維護。 1)DI通過解耦類,使其更模塊化,2)提高了測試的便捷性和代碼的靈活性,3)使用DI容器可以管理複雜的依賴關係,但要注意性能影響和循環依賴問題,4)最佳實踐是依賴於抽象接口,實現鬆散耦合。

PHP性能:是否可以優化應用程序?PHP性能:是否可以優化應用程序?May 14, 2025 am 12:04 AM

是的,優化papplicationispossibleandessential.1)empartcachingingcachingusedapcutorediucedsatabaseload.2)優化的atabaseswithexing,高效Quereteries,and ConconnectionPooling.3)EnhanceCodeWithBuilt-unctions,避免使用,避免使用ingglobalalairaiables,並避免使用

PHP性能優化:最終指南PHP性能優化:最終指南May 14, 2025 am 12:02 AM

theKeyStrategiestosigantificallyBoostPhpaPplicationPerformenCeare:1)UseOpCodeCachingLikeLikeLikeLikeLikeCacheToreDuceExecutiontime,2)優化AtabaseInteractionswithPreparedStateTementStatementStatementAndProperIndexing,3)配置

PHP依賴注入容器:快速啟動PHP依賴注入容器:快速啟動May 13, 2025 am 12:11 AM

aphpdepentioncontiveContainerIsatoolThatManagesClassDeptions,增強codemodocultion,可驗證性和Maintainability.itactsasaceCentralHubForeatingingIndections,因此reducingTightCightTightCoupOulplingIndeSingantInting。

PHP中的依賴注入與服務定位器PHP中的依賴注入與服務定位器May 13, 2025 am 12:10 AM

選擇DependencyInjection(DI)用於大型應用,ServiceLocator適合小型項目或原型。 1)DI通過構造函數注入依賴,提高代碼的測試性和模塊化。 2)ServiceLocator通過中心註冊獲取服務,方便但可能導致代碼耦合度增加。

PHP性能優化策略。PHP性能優化策略。May 13, 2025 am 12:06 AM

phpapplicationscanbeoptimizedForsPeedAndeffificeby:1)啟用cacheInphp.ini,2)使用preparedStatatementSwithPdoforDatabasequesies,3)3)替換loopswitharray_filtaray_filteraray_maparray_mapfordataprocrocessing,4)conformentnginxasaseproxy,5)

PHP電子郵件驗證:確保正確發送電子郵件PHP電子郵件驗證:確保正確發送電子郵件May 13, 2025 am 12:06 AM

phpemailvalidation invoLvesthreesteps:1)格式化進行regulareXpressecthemailFormat; 2)dnsvalidationtoshethedomainhasavalidmxrecord; 3)

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中