PHP(5.3)的特殊行为,静态继承和引用 [英] Peculiar Behaviour with PHP (5.3), static inheritance and references

查看:152
本文介绍了PHP(5.3)的特殊行为,静态继承和引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用PHP 5.3编写一个库,其中大部分是一个具有多个静态属性的类,这些属性从子类扩展到允许子类的零-conf。

I'm writing a library in PHP 5.3, the bulk of which is a class with several static properties that is extended from by subclasses to allow zero-conf for child classes.

无论如何,这里有一个例子来说明我发现的特殊性:

Anyway, here's a sample to illustrate the peculiarity I have found:

<?php

class A {
    protected static $a;
    public static function out() { var_dump(static::$a); }
    public static function setup($v) { static::$a =& $v; }
}
class B extends A {}
class C extends A {}

A::setup('A');
A::out(); // 'A'
B::out(); // null
C::out(); // null

B::setup('B');
A::out(); // 'A'
B::out(); // 'B'
C::out(); // null

C::setup('C');
A::out(); // 'A'
B::out(); // 'B'
C::out(); // 'C'

?>

现在,就我而言,这是静态继承的非常理想的行为,但是,更改 static :: $ a =& $ v; static :: $ a = $ v; (无引用)你得到了我预期的行为,即:

Now, this is pretty much desired behaviour for static inheritance as far as I'm concerned, however, changing static::$a =& $v; to static::$a = $v; (no reference) you get the behaviour I expected, that is:

'A'
'A'
'A'

'B'
'B'
'B'

'C'
'C'
'C'

任何人都能解释为什么会这样吗?我无法理解引用如何以任何方式影响静态继承:/

Can anyone explain why this is? I can't understand how references effect static inheritance in any way :/

更新:

基于 Artefacto的回答,在基类中使用以下方法(在本例中为A)并在类声明之后调用它会产生上面标记为desired的行为,而不需要在setter中通过引用分配,同时保留结果当使用self ::作为上面的预期行为时。

Based on Artefacto's answer, having the following method in the base class (in this instance, A) and calling it after the class declarations produces the behaviour labelled as 'desired' above without the need to assign by reference in setters, whilst leaving the results when using self:: as the 'expected' behaviour above.

/*...*/
public static function break_static_references() {
    $self = new ReflectionClass(get_called_class());
    foreach($self->getStaticProperties() as $var => $val)
        static::$$var =& $val;
}
/*...*/
A::break_static_references();
B::break_static_references();
C::break_static_references();
/*...*/


推荐答案

TL; DR版本



静态属性 $ a 是每个类中的不同符号,但它是实际上在 $ a = 1的意义上是相同的变量; $ b =& $ a; $ a $ b 是相同的变量(即,它们在相同的参考集上)。在进行简单的赋值( $ b = $ v; )时,两个符号的值都会改变;通过引用( $ b =& $ v; )进行作业时,只会影响 $ b

TL;DR version

The static property $a is a different symbol in each one of the classes, but it's actually the same variable in the sense that in $a = 1; $b = &$a;, $a and $b are the same variable (i.e., they're on the same reference set). When making a simple assignment ($b = $v;), the value of both symbols will change; when making an assignment by reference ($b = &$v;), only $b will be affected.

首先,让我们了解静态属性是如何继承的。 zend_do_inheritance 迭代超类静态属性,调用 inherit_static_prop

First thing, let's understand how static properties are 'inherited'. zend_do_inheritance iterates the superclass static properties calling inherit_static_prop:

zend_hash_apply_with_arguments(&parent_ce->default_static_members TSRMLS_CC,
    (apply_func_args_t)inherit_static_prop, 1, &ce->default_static_members);

其定义为:

static int inherit_static_prop(zval **p TSRMLS_DC, int num_args,
    va_list args, const zend_hash_key *key)
{
    HashTable *target = va_arg(args, HashTable*);

    if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) {
        SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
        if (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, p,
                sizeof(zval*), NULL) == SUCCESS) {
            Z_ADDREF_PP(p);
        }
    }
    return ZEND_HASH_APPLY_KEEP;
}

让我们翻译一下。 PHP使用copy on write,这意味着如果它们具有相同的内容,它将尝试共享相同的实际内存表示(zval)。为每个超类静态属性调用 inherit_static_prop ,以便可以将其复制到子类。 inherit_static_prop 的实现确保子类的静态属性将是PHP引用,无论父类的zval是否共享(特别是,如果超类具有引用) ,孩子将分享zval,如果没有,zval将被复制,新的zval将被作为参考;第二种情况在这里我们并不感兴趣)。

Let's translate this. PHP uses copy on write, which means it will try to share the same actual memory representation (zval) of the values if they have the same content. inherit_static_prop is called for each one of the superclass static properties so that can be copied to the subclass. The implementation of inherit_static_prop ensures that the static properties of the subclass will be PHP references, whether or not the zval of the parent is shared (in particular, if the superclass has a reference, the child will share the zval, if it doesn't, the zval will be copied and new zval will be made a reference; the second case doesn't really interest us here).

所以基本上,当形成A,B和C时, $ a 将是每个类的不同符号(即,每个类都有其属性哈希表和每个哈希表都有自己的 $ a 条目,但基础zval将是相同的,它将是一个参考。

So basically, when A, B and C are formed, $a will be a different symbol for each of those classes (i.e., each class has its properties hash table and each hash table has its own entry for $a), BUT the underlying zval will be the same AND it will be a reference.

你有类似的东西:

A::$a -> zval_1 (ref, reference count 3);
B::$a -> zval_1 (ref, reference count 3);
C::$a -> zval_1 (ref, reference count 3);

因此,当您进行正常作业时

Therefore, when you do a normal assignment

static::$a = $v;

因为所有三个变量共享相同的zval及其引用,所有三个变量都将采用值 $ v 。如果你这样做会是一样的:

since all three variables share the same zval and its a reference, all three variables will assume the value $v. It would be the same if you did:

$a = 1;
$b = &$a;
$a = 2; //both $a and $b are now 1

另一方面,当你这样做时

On the other hand, when you do

static::$a =& $v;

您将打破参考集。假设你在A级做到了。你现在有:

you will be breaking the reference set. Let's say you do it in class A. You now have:

//reference count is 2 and ref flag is set, but as soon as
//$v goes out of scope, reference count will be 1 and
//the reference flag will be cleared
A::$a -> zval_2 (ref, reference count 2);

B::$a -> zval_1 (ref, reference count 2);
C::$a -> zval_1 (ref, reference count 2);

类似的是

$a = 1;
$b = &$a;
$v = 3;
$b = &$v; //$a is 1, $b is 3



解决方法



正如Gordon现在删除的答案中所述,三个类的属性之间的引用集也可以通过重新声明每个类中的属性来打破:

Work-around

As featured in Gordon's now deleted answer, the reference set between the properties of the three classes can also be broken by redeclaring the property in each one of the classes:

class B extends A { protected static $a; }
class C extends A { protected static $a; }

这是因为如果重新声明属性,则不会将该属性从超类复制到子类中(请参阅条件 if(!zend_hash_quick_exists(target,key-> arKey,key-> nKeyLength,key-> h)) in inherit_static_prop )。

This is because the property will not be copied to the subclass from the superclass if it's redeclared (see the condition if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) in inherit_static_prop).

这篇关于PHP(5.3)的特殊行为,静态继承和引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆