62-パラメータreturn value

8969 ワード

62-パラメータreturn value
PHP言語での関数の戻り値はreturnによって達成され、次のプログラムのようになります.
<?php
function sample_long() {
  return 42;
}
$bar = sample_long();
?>

C言語もreturnキーワードを使用しています.
int sample_long(void) {
    return 42;
}
int main(void) {
    int bar = sample_long();
    return 1;
}

では、拡張で作成したPHP関数は、ユーザー側の関数呼び出し者に戻り値をどのようにフィードバックしますか?ほら、ここでは単にreturn~ではなくフィードバックを指します.
拡張で定義された関数は、以下のようにreturnキーワードで直接値を返すべきだと思います.たとえば、zvalを生成して返す必要があります.
ZEND_FUNCTION(sample_long_wrong)
{
    zval *retval;

    MAKE_STD_ZVAL(retval);
    ZVAL_LONG(retval, 42);

    return retval;
}

しかし、上記の書き方は無効です!拡張開発者が毎回zvalを初期化してreturnするよりも、zendエンジンはとっくにより良い方法を用意しています.各zif関数宣言にはreturn_というzval*タイプのパラメータが追加されています.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
    function sample_long()
    {
        return 42;
    }
    /*
                .
        $a = sample_long();
           $a    42 ,           。
    */
    ?>
    

    次に、拡張を記述する際の実装を示します.
    ZEND_FUNCTION(sample_long)
    {
        ZVAL_LONG(return_value, 42);
        return;
    }
    

    注意が必要なのは、ZEND_FUNCTION自体はreturnキーワードで価値のあるものを返していません.実行時にreturnを変更したにすぎません.valueポインタが指す変数の値にすぎず、カーネルはreturn_をvalueが指す変数は、この関数をユーザ側で呼び出した後の戻り値として使用されます.思い出してZVAL_LONG()マクロは1種類の操作に対するパッケージであり、展開後は以下のようになるべきである.
    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プロパティ、この2つのプロパティの値はPHPカーネルによって自動的に管理されます.
    5章で得た拡張フレームワークに追加し、この関数名を関数エントリ配列に登録します.次のようにします.
    static zend_function_entry walu_functions[] = {
        ZEND_FE(walu_hello,        NULL)
        PHP_FE(sample_long, NULL)
        { NULL, NULL, NULL }
    };
    

    拡張機能をコンパイルすると、sampleをユーザー側で呼び出すことができます.long関数は、整数の戻り値を得るために使用されます.
    <?php var_dump(sample_long());?>
    

    とreturn_value関連マクロ
    return_valueはこのように重要で、カーネルはとっくにそれのために大量のマクロを用意して、私たちの操作を簡素化して、プログラムの品質を高めます.前章で触れたマクロの多くはZVALでした冒頭、次にご紹介するマクロの名前は、RETVALです.上の例に戻り、RETVALで書き直します.
    PHP_FUNCTION(sample_long)
    {
        RETVAL_LONG(42);
        //      ZVAL_LONG(return_value, 42);
        return;
    }
    

    ほとんどの場合、returnを処理しています.valueの後にしたことはreturn文で私たちの関数の実行を終了し、最後まで手伝って、仏を西に送って、私たちの仕事量を減らすために、カーネルにはRETURN_*も提供されています.シリーズマクロは私たちのためにreturnを自動的に補完します.次のようになります.
    PHP_FUNCTION(sample_long) { RETURN_LONG(42); //#define RETURN_LONG(l) { RETVAL_LONG(l); return; } php_printf("I will never be reached.
    "); // 。 }

    次に、現在のすべてのRETVAL_***を示します.マクロとRETURN_***マクロは、みんなが閲覧して使用するために使用されます.
    //       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 
    function sample_array_range() {
        $ret = array();
        for($i = 0; $i < 1000; $i++) {
            $ret[] = $i;
        }
        return $ret;
    }
    sample_array_range();
    ?>
    

    sample_array_range()は実行しただけで,関数の戻り値は使用されなかった.関数の戻り値$retは初期化され、呼び出し元に返された後も機能しなかったが、1000個の要素を格納するために多くのメモリを浪費した.この例は極端ですが、戻り値が使用されていない場合、関数で事前に性能に有利な操作を知っておく方法はありませんか?
    このアイデアはPHPスクリプト言語ではまったく奇想天外で、実現できないに違いない.しかし、私たちが置かれている環境がカーネル、すなわちzifであれば、この願いを簡単に実現することができます.私たちがしなければならないのはreturn_を十分に利用することです.value_usedこのパラメータ:
    ZEND_FUNCTION(sample_array_range)
    {
        if (return_value_used) {
            int i;
    
            //          PHP      
            array_init(return_value);
            for(i = 0; i < 1000; i++)
            {
                // retrun_value         ,  i
                add_next_index_long(return_value, i);
            }
            return;
        }
        else
        {
            //    E_NOTICE   
            php_error_docref(NULL TSRMLS_CC, E_NOTICE,"     ,             !");
            RETURN_NULL();
        }
    }
    

    参照として値を返す
    関数の戻り値を参照して返す技術をマニュアルで見たことがあるに違いありません.しかし、いくつかの歴史的な理由から、拡張のために関数を記述する際に、php 5では、php 5のため、値を参照して返すには慎重にしなければならない.1以前は、この機能を本当に実現することはできませんでした.lookは次のコードをクリックします.
    <?php
    //  PHP             ,   PHP  。
    $a = 'china';
    
    function &return_by_ref()
    {
        global $a;
        return $a;
    }
    
    $b = &return_by_ref();
    $b = "php";
    echo $a;
    //      php
    ?>
    

    上記のコードでは、bは実はaの参照であり、最後の行のコードが実行されると、aとbは「bar」という文字列に対応するzvalを探し始め、カーネルの角度でこれらを再観察しましょう.
    #if (PHP_MAJOR_VERSION > 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変数は,ある非参照形式の変数と1つのzvalを共有すると分離する.
    残念なことに、上記のコードをコンパイルすると、使用中にエラーが発生します.正常に動作するためには、ソースファイルに何かを追加する必要があります.
    #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がなければ、私たちのプログラムはphp 4の下でコンパイルすることができなくて、php 5.0では予測できないエラーもアクティブになります.