繁体   English   中英

PHP中的ArrayAccess - 通过引用分配偏移量

[英]ArrayAccess in PHP — assigning to offset by reference

首先,来自ArrayAccess::offsetSet()的ole'手册的引用:

此函数不是通过引用在赋值中调用,也不是通过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的对象,如示例代码段所示。 我之前已经对此进行了调查,我认为这不可能,但由于不确定性而又回到了这一点,我( 重新 )在手册中发现了这一点。 现在我只是困惑。


更新 :当我点击发布你的问题时 ,我意识到这可能只是通过引用另一个变量来引用赋值,例如$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);

至于hasgetsetremove去,他们有支持的方法没有问题ArrayAccess 绑定功能是我不知所措的地方,我开始接受ArrayAccess和PHP的限制只是禁止这一点。

手册所指的是所谓的“间接修改”。 请考虑以下脚本:

$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获取然后修改。 如果你的offsetGet由ref返回,那么这将成功。 如果不是,你会得到一些间接修改错误。


另一方面,您想要的恰恰相反:不通过引用获取值,而是分配它。 理论上这需要offsetSet($key, &$value)的签名,但实际上这是不可能的

顺便说一句,参考文献很难掌握。 您将获得许多非显而易见的行为,对于数组项引用(尤其是那些具有一些特殊规则)尤其如此。 我建议你完全避开它们。

这不适用于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 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM