<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 6.1 函數返回值 # 6.1 函數返回值 你也許會認為擴展中定義的函數應該直接通過return關鍵字來返回一個值,比如由你自己來生成一個zval并返回,就像下面這樣: ``` ZEND_FUNCTION(sample_long_wrong) { zval *retval; MAKE_STD_ZVAL(retval); ZVAL_LONG(retval, 42); return retval; } ``` 但是,上面的寫法是無效的!與其讓擴展開發員每次都初始化一個zval并return之,zend引擎早就準備好了一個更好的方法。它在每個zif函數聲明里加了一個zval\*類型的形參,名為return\_value,專門來解決返回值這個問題。在前面我們已經知道了ZEND\_FUNCTION宏展開后是void name(INTERNAL\_FUNCTION\_PARAMETERS)的形式,現在是我們展開代表參數聲明的INTERNAL\_FUNCTION\_PARAMETERS宏的時候了。 ``` #define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC ``` - int ht - zval \*return\_value,我們在函數內部修改這個指針,函數執行完成后,內核將把這個指針指向的zval返回給用戶端的函數調用者。 - zval \*\*return\_value\_ptr, - zval \*this\_ptr,如果此函數是一個類的方法,那么這個指針的含義和PHP語言中$this變量差不多。 - int return\_value\_used,代表用戶端在調用此函數時有沒有使用到它的返回值。 下面讓我們先試驗一個非常簡單的例子,我先給出PHP語言中的實現,然后給出我們在擴展中用C語言完成相同功能的代碼。 ````php ```` 下面是我們在編寫擴展時的實現。 ````c ZEND\_FUNCTION(sample\_long) { ZVAL\_LONG(return\_value, 42); return; } ```` 需要注意的是,ZEND\_FUNCTION本身并沒有通過return關鍵字返回任何有價值的東西,它只不過是在運行時修改了return\_value指針所指向的變量的值而已,而內核則會把return\_value指向的變量作為用戶端調用此函數后的得到的返回值。回想一下,ZVAL\_LONG()宏是對一類操作的封裝,展開后應該就是下面這樣: ````c Z\_TYPE\_P(return\_value) = IS\_LONG; Z\_LVAL\_P(return\_value) = 42; //更徹底的講,應該是這樣的: return\_value->type = IS\_LONG; return\_value->value.lval = 42; ```` 我們千萬不要自己去修改return\_value的is\_ref\_\_gc和refcount\_\_gc屬性,這兩個屬性的值會由PHP內核自動管理。 現在我們把它加到我們在第五章得到的那個擴展框架里,并把這個函數名稱注冊到函數入口數組里,就像下面這樣: ````c static zend\_function\_entry walu\_functions\[\] = { ZEND\_FE(walu\_hello, NULL) PHP\_FE(sample\_long, NULL) { NULL, NULL, NULL } }; ```` 現在我們編譯我們的擴展,便可以在用戶端通過調用sample\_long函數來得到一個整型的返回值了: ````php ```` ### 與return\_value有關的宏 return\_value如此重要,內核肯定早已經為它準備了大量的宏,來簡化我們的操作,提高程序的質量。 在前幾章我們接觸的宏大多都是以ZVAL\_開頭的,而接下來我們要介紹的宏的名字是:RETVAL。 再回到上面的那個例子,我們用RETVAL來重寫一下: ````c PHP\_FUNCTION(sample\_long) { RETVAL\_LONG(42); //展開后相當與ZVAL\_LONG(return\_value, 42); return; } ```` 大多數情況下,我們在處理完return\_value后所做的便是用return語句結束我們的函數執行,幫人幫到底,送佛送到西,為了減少我們的工作量,內核中還提供了RETURN\_\*系列宏來為我們自動補上return;如: ````c PHP\_FUNCTION(sample\_long) { RETURN\_LONG(42); //#define RETURN\_LONG(l) { RETVAL\_LONG(l); return; } php\_printf("I will never be reached.\\n"); //這一行代碼永遠不會被執行。 } ```` 下面,我們給出目前所有的RETVAL\_\*\*\*宏和RETURN\_\*\*\*宏,供大家查閱使用。 ````c //這些宏都定義在Zend/zend\_API.h文件里 #define RETVAL\_RESOURCE(l) ZVAL\_RESOURCE(return\_value, l) #define RETVAL\_BOOL(b) ZVAL\_BOOL(return\_value, b) #define RETVAL\_NULL() ZVAL\_NULL(return\_value) #define RETVAL\_LONG(l) ZVAL\_LONG(return\_value, l) #define RETVAL\_DOUBLE(d) ZVAL\_DOUBLE(return\_value, d) #define RETVAL\_STRING(s, duplicate) ZVAL\_STRING(return\_value, s, duplicate) #define RETVAL\_STRINGL(s, l, duplicate) ZVAL\_STRINGL(return\_value, s, l, duplicate) #define RETVAL\_EMPTY\_STRING() ZVAL\_EMPTY\_STRING(return\_value) #define RETVAL\_ZVAL(zv, copy, dtor) ZVAL\_ZVAL(return\_value, zv, copy, dtor) #define RETVAL\_FALSE ZVAL\_BOOL(return\_value, 0) #define RETVAL\_TRUE ZVAL\_BOOL(return\_value, 1) #define RETURN\_RESOURCE(l) { RETVAL\_RESOURCE(l); return; } #define RETURN\_BOOL(b) { RETVAL\_BOOL(b); return; } #define RETURN\_NULL() { RETVAL\_NULL(); return;} #define RETURN\_LONG(l) { RETVAL\_LONG(l); return; } #define RETURN\_DOUBLE(d) { RETVAL\_DOUBLE(d); return; } #define RETURN\_STRING(s, duplicate) { RETVAL\_STRING(s, duplicate); return; } #define RETURN\_STRINGL(s, l, duplicate) { RETVAL\_STRINGL(s, l, duplicate); return; } #define RETURN\_EMPTY\_STRING() { RETVAL\_EMPTY\_STRING(); return; } #define RETURN\_ZVAL(zv, copy, dtor) { RETVAL\_ZVAL(zv, copy, dtor); return; } #define RETURN\_FALSE { RETVAL\_FALSE; return; } #define RETURN\_TRUE { RETVAL\_TRUE; return; } ```` 其實,除了這些標量類型,還有很多php語言中的復合類型我們需要在函數中返回,如數組和對象,我們可以通過RETVAL\_ZVAL與RETURN\_ZVAL來操作它們,有關它們的詳細介紹我們將在后續章節中敘述。 ### 不返回值可以么? 其實,zend internal function的形參中還有一個比較常用的名為return\_value\_used的參數,它是干嘛使的呢?它用來標志這個函數的返回值在用戶端有沒有用到。看下面的代碼: ````php 5) || (PHP\_MAJOR\_VERSION == 5 && PHP\_MINOR\_VERSION > 0) ZEND\_FUNCTION(return\_by\_ref) { zval \*\*a\_ptr; zval \*a; //檢查全局作用域中是否有$a這個變量,如果沒有則添加一個 //在內核中真的是可以胡作非為啊,:-) if(zend\_hash\_find(&EG(symbol\_table) , "a",sizeof("a"),(void \*\*)&a\_ptr ) == SUCCESS ) { a = \*a\_ptr; } else { ALLOC\_INIT\_ZVAL(a); zend\_hash\_add(&EG(symbol\_table), "a", sizeof("a"), &a,sizeof(zval\*), NULL); } //廢棄return\_value,使用return\_value\_ptr來接替它的工作 zval\_ptr\_dtor(return\_value\_ptr); if( !a->is\_ref\_\_gc && a->refcount\_\_gc > 1 ) { zval \*tmp; MAKE\_STD\_ZVAL(tmp); \*tmp = \*a; zval\_copy\_ctor(tmp); tmp->is\_ref\_\_gc = 0; tmp->refcount\_\_gc = 1; zend\_hash\_update(&EG(symbol\_table), "a", sizeof("a"), &tmp,sizeof(zval\*), NULL); a = tmp; } a->is\_ref\_\_gc = 1; a->refcount\_\_gc++; \*return\_value\_ptr = a; } #endif /\* PHP >= 5.1.0 \*/ ```` return\_value\_ptr是定義zend internal function時的另外一個重要參數,他是一個zval\*\*類型的指針,并且指向函數的返回值。我們調用zval\_ptr\_dtor()函數后,默認的return\_value便被廢棄了。這里的$a變量如果是與某個非引用形式的變量共用一個zval的話,便要進行分離。 不幸的是,如果你編譯上面的代碼,使用的時候便會得到一個段錯誤。為了使它能夠正常的工作,需要在源文件中加一些東西: ````c #if (PHP\_MAJOR\_VERSION > 5) || (PHP\_MAJOR\_VERSION == 5 && PHP\_MINOR\_VERSION > 0) ZEND\_BEGIN\_ARG\_INFO\_EX(return\_by\_ref\_arginfo, 0, 1, 0) ZEND\_END\_ARG\_INFO () #endif /\* PHP >= 5.1.0 \*/ 然后使用下面的代碼來申明我們的定義的函數: #if (PHP\_MAJOR\_VERSION > 5) || (PHP\_MAJOR\_VERSION == 5 && PHP\_MINOR\_VERSION > 0) ZEND\_FE(return\_by\_ref, return\_by\_ref\_arginfo) #endif /\* PHP >= 5.1.0 \*/ ```` arginfo是一種特殊的結構體,用來提前向內核告知此函數具有的一些特定的性質,如本例,其將告訴內核本函數需要引用形式的返回值,所以內核不再通過return\_value來獲取執行結果,而是通過return\_value\_ptr。如果沒有arginfo,那內核會預先把return\_value\_ptr置為NULL,當我們對其調用zval\_ptr\_dtor()函數時便會使程序崩潰。 這一些代碼都包含在了一個宏里面,只有在php版本大于等于5.1的時候才會被啟用。如果沒有這些if、endif,那我們的程序將無法在php4下通過編譯,在php5.0上也會激活一些無法預測的錯誤。 - - - - - - - zone(zqx10104#163.com)于2011-10-20提供了一個Bug,:-) ## links - 6 [函數返回值](6.html) - 6.2 [引用與函數的執行結果](6.2.html)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看