16

まず、 の ole' マニュアルからの引用ArrayAccess::offsetSet():

この関数は、参照による割り当てでは呼び出されず、ArrayAccessでオーバーロードされた配列次元への間接的な変更(次元を直接変更するのではなく、サブ次元またはサブプロパティを変更するか、配列次元を割り当てることによって行われるという意味で間接的)別の変数への参照によって)。代わりに、ArrayAccess::offsetGet()が呼び出されます。そのメソッドが参照によって返される場合にのみ操作が成功します。これは PHP 5.3.4 以降でのみ可能です。

私はこれに少し混乱しています。これは、( 5.3.4 の時点でoffsetGet())実装クラスで参照によって返すように定義できることを示唆しているようです。したがって、参照による割り当てを処理します。

だから、今テストスニペット:

(検証とisset()チェックがないことは無視してください)

class Test implements ArrayAccess
{
    protected $data = array();

    public function &offsetGet($key)
    {
        return $this->data[$key];
    }

    public function offsetSet($key, $value)
    {
        $this->data[$key] = $value;
    }

    public function offsetExists($key) { /* ... */ }

    public function offsetUnset($key) { /* ... */ }

}

$test = new Test();

$test['foo'] = 'bar';
$test['foo'] = &$bar; // Fatal error: Cannot assign by reference to
                      // overloaded object in

var_dump($test, $bar);    

わかりました、それはうまくいきません。では、このマニュアル ノートは何を指しているのでしょうか。

理由スニペットの例が示すように
、配列演算子を介して を実装するオブジェクトへの参照による割り当てを許可したい。ArrayAccess私はこれを以前に調査したことがあり、それが可能だとは思いませんでしたが、不確実性のためにこれに戻ってきて、マニュアルでこの言及を(再)発見しました。今、私はただ混乱しています。


更新: をヒットしとき、Post Your Questionこれは. その場合は、お詫び申し上げます。ただし、可能であれば、オーバーロードされたオブジェクトへの参照によって割り当てる方法を知っていると便利です。$bar = &$test['foo'];


さらに詳しく: 結局のところ、次のメソッドエイリアスが必要です。

isset($obj[$key]);       // $obj->has_data($key);

$value = $obj[$key];     // $obj->get_data($key);

$obj[$key] = $value;     // $obj->set_data($key, $value);

$obj[$key] = &$variable; // $obj->bind_data($key, $variable);

// also, flipping the operands is a syntactic alternative
$variable = &$obj[$key]; // $obj->bind_data($key, $variable);

unset($obj[$key]);       // $obj->remove_data($key);

hasgetset、およびremove行く限り、サポートされている のメソッドに問題はありませんArrayAccess。バインド機能は私が途方に暮れている場所であり、ArrayAccess と PHP の制限がこれを単に禁止していることを受け入れ始めています。

4

2 に答える 2

9

マニュアルが言及しているのは、いわゆる「間接的な変更」です。次のスクリプトを検討してください。

$array = new ArrayObject;
$array['foo'] = array();
$array['foo']['bar'] = 'foobar';

上記のスクリプト$array['foo'] = array();では、offsetSet('foo', array()). $array['foo']['bar'] = 'foobar';一方、 をトリガーしoffsetGet('foo')ます。なんでそうなの?最後の行は、ボンネットの下で大まかに次のように評価されます。

$tmp =& $array['foo'];
$tmp['bar'] = 'foobar';

したがって$array['foo']、最初に ref によってフェッチされ、次に変更されます。ref によって返された場合、offsetGetこれは成功します。そうしないと、間接的な変更エラーが発生します。


一方、あなたが望むのは正反対です:参照によって値を取得するのではなく、それを割り当てます。これには理論的には の署名が必要ですoffsetSet($key, &$value)が、実際にはこれは不可能です。

ところで、参照は把握するのが難しいです。多くの非自明な動作が発生します。これは特に、配列項目の参照に当てはまります (これらにはいくつかの特別な規則があります)。それらを完全に避けることをお勧めします。

于 2011-12-31T00:08:16.257 に答える
4

これは では機能しませんArrayAccess。オフセットへの参照を設定できるパブリック関数を自分で追加できます (確かに、これは配列構文を使用する場合とは異なるように見えるため、実際には十分な答えではありません)。

class Test implements ArrayAccess{

    protected $_data = array();

    public function &offsetGet($key){
        return $this->_data[$key];
    }

    ... 

    public function offsetSetReference($key, &$value)
    {
        $this->_data[$key] = &$value;
    }
}

$test = new Test();
$test['foo'] = $var = 'bar';
$test->offsetSetReference('bar', $var);    
$var = 'foo';    
echo $test['bar']; # foo    
$alias = &$test['bar'];    
$alias = 'hello :)';    
echo $var; # hello :)

おそらくそのような機能は、ArrayAccess最初に実装されたときに忘れられていました。

編集:「参照割り当て」として渡します:

class ArrayAccessReferenceAssignment
{
    private $reference;
    public function __construct(&$reference)
    {
        $this->reference = &$reference;
    }
    public function &getReference()
    {
        $reference = &$this->reference;
        return $reference;
    }
}


class Test implements ArrayAccess{
    ...
    public function offsetSet($key, $value){
        if ($value instanceof ArrayAccessReferenceAssignment)
        {
           $this->offsetSetReference($key, $value->getReference());
        }
        else
        {
           $this->_data[$key] = $value;
        }
    }

あなたがそれを実装したので、これは問題なく動作します。これはおそらく、上記のより明示的なoffsetSetReferenceバリアントよりもうまくインターフェイスしています。

$test = new Test();
$test['foo'] = $var = 'bar';
$test['bar'] = new ArrayAccessReferenceAssignment($var);

$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)
于 2011-12-31T00:12:31.203 に答える