2013-07-24 3 views
8

私は、オブジェクトメソッドが配列zvalを返す必要があるPHP拡張を開発しています。コードが正常に動作し、期待されることを行いPHPエクステンションからメモリにコピーすることなく配列を返す方法は?

ZEND_METHOD(myObject, myMethod) 
{ 
    zval **myArrayProperty; 
    if (zend_hash_find(Z_OBJPROP_P(getThis()), "myArrayProperty", sizeof("myArrayProperty"), (void **) &myArrayProperty) == FAILURE) { 
     RETURN_FALSE; 
    } 
    RETURN_ZVAL(*myArrayProperty, 1, 0); 
} 

- それは、オブジェクトのmyArrayPropertyを返しますよう

方法が見えます。しかし、私はプロセスを最適化したいと思います。

myArrayPropertyは非常に大きい配列を格納します。そしてRETURN_ZVAL()マクロは値を返すために配列を複製します。複製プロセスでは、メモリを取得してすべての配列値をコピーするのに時間がかかります。同時に、返された配列は通常、読み取り専用操作に使用されます。だから最適な最適化は、参照カウントでPHPのメカニズムを使用することであり、myArrayPropertyの内容を複製しません。むしろ、私はrefcountmyArrayPropertyに増やして、それにポインタを戻します。これは、PHP拡張モジュールで変数を扱うときに通常使用されるのと同じ方法です。

しかし、それを行う方法はないようです。PHP拡張機能から返すために値を複製する必要があります。関数のシグネチャを参照によって返すように変更することは、プロパティと戻り値をリンクするため、つまり戻り値を後で変更するとプロパティを変更するため、オプションではありません。それは容認できる行動ではありません。

function myMethod() { 
{ 
    return $this->myArrayProperty; 
} 

参照カウント機構によって最適化される:PHPで同じコードをので

参照カウントを係合することができないことは、奇妙に見えます。だからこそ私はStackOverflowでこの質問をしています。

したがって、メモリに配列をコピーせずに、PHP拡張モジュールの関数から配列を返す方法はありますか?

答えて

-1

私はこのような何かをコード化されたので、それは、しばらくの間をされている...

だから、私は以下のコードで何をすべきか:1)。明示的にリフカウンターを増やす2)。

RETURN_ZVAL_FAST(*myArrayProperty); 

あなたの機能により、参照が返された場合:これはマクロRETURN_ZVAL_FASTを使用してPHP 5.6(現在のマスター)のようのみ可能であることにより、値あなたの関数が戻る場合、それを

ZEND_METHOD(myObject, myMethod) 
{ 
    zval **myArrayProperty; 

    if (zend_hash_find(Z_OBJPROP_P(getThis()), "myArrayProperty", sizeof("myArrayProperty"), (void **) &myArrayProperty) == FAILURE) { 
     RETURN_FALSE; 
    } 

    Z_ADDREF_PP(myArrayProperty); 
    RETURN_ZVAL(*myArrayProperty, 0, 0); 
} 
+0

しかし、メモリリークやセグメンテーション(いずれか先に来る方)につながることはありませんか?メモリリークは、プロパティへのすべての参照がクリアされたときに発生しますが、refvalが1のままであるため、zvalコンテナが占有するメモリは解放できません。返された値が破棄されるとSegfaultが発生し、コンテナは配列(共有されたHashTable、そのコンテナとプロパティzvalコンテナの両方から参照されます)と一緒にクリアされるため、後でプロパティを使用すると、予測できない間違いがあります。 –

+0

プロパティ参照zval - refcount = 1です。このコードは、zvalが返されたときにrefcountを増加させ、オブジェクトと呼び出し元の両方によって参照されます。オブジェクトがプロパティを必要としない場合、refcountが減少するので、呼び出し元だけが所有します。だから、このコードは私には正気に見えます。しかし、再び、これは厳密には理論的なものです - 私はPHPのソースを今でも開かれていません。 – JimiDini

+0

残念ながら、予測通り、コードは動作しません - 実際には確認済みです:http://pastebin.com/FRfaJZvL。問題は上記の通りです:オブジェクトと呼び出し元は異なるメモリ位置を参照します。 –

7

をコピーせずにzvalを返しますあなたの関数は、値による返し、あなたがPHP 5.5または古いにしている場合は

zval_ptr_dtor(&return_value); 
SEPARATE_ZVAL_TO_MAKE_IS_REF(myArrayProperty); 
Z_ADDREF_PP(myArrayProperty); 
*return_value_ptr = *myArrayProperty; 

:(arginfoでreturn_reference=1)は、次のコードを使用して返すことができます

if (Z_REFCOUNT_PP(myArrayProperty) == 1) { 
    RETVAL_ZVAL(*myArrayProperty, 0, 1); 
    Z_ADDREF_P(return_value); 
    *myArrayProperty = return_value; 
} else { 
    RETVAL_ZVAL(*myArrayProperty, 1, 0); 
} 
+0

まあ、説明で言われているように、関数は参照によって値を返しません。残念ながら解決策ではありません。 –

+0

申し訳ありませんが、私はそれを逃した。この場合、あなたが望むものは不可能です。 – NikiC

+0

私はreturn_value_ptrをACC_RETURN_REFERENCEなしで渡すことができなかった理由はわかりませんが(これは、非ref関数からis_ref = 1 zvalを返すことを可能にします)。これについて内部的に@を尋ねたいかもしれません。 – NikiC

0

私はPHP < 5.6へのアクセスを持っていないが、私はこの問題は、値のみがコピーされていることだと思う:えーあなたはまだrefcount=1ケースを最適化することができます。問題の定義をコードで検索してください。

zval *arr; 
MAKE_STD_ZVAL(arr); 
array_init(arr); 
// Do things to the array. 
RETVAL_ZVAL(arr, 0, 0); 
efree(arr); 

愚かに使用した場合これは危険である:あなたが試すことができるかもしれない意味

。あなた自身の一時的な容器と共に使用する場合、私は何の問題も知らない。

戻り値を直接操作することもできますが、これはより良いアプローチかもしれません。あなたはそれを初期化して、ポインタを最初に渡します。

このように返品結果をラッピングすることができます。参照を試すこともできます。