PHP 的特殊行为 (5.3)、静态继承和引用

2023-11-27

我正在 PHP 5.3 中编写一个库,其中大部分是一个具有多个静态属性的类,这些静态属性由子类扩展以允许子类的零配置。

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

<?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; to static::$a = $v;(无参考)你得到了我期望的行为,即:

'A'
'A'
'A'

'B'
'B'
'B'

'C'
'C'
'C'

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

Update:

基于阿特法托的回答,在基类中具有以下方法(在本例中为 A),并在类声明后调用它,会产生上面标记为“所需”的行为,而无需在 setter 中通过引用进行分配,同时在使用 self 时保留结果: :如上面的“预期”行为。

/*...*/
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 and $b是相同的变量(即它们位于相同的参考集上)。进行简单作业时($b = $v;),两个符号的值都会改变;通过引用进行分配时($b = &$v;), only $b会受到影响。

原版

首先,让我们了解静态属性是如何“继承”的。zend_do_inheritance迭代超类静态属性调用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 使用写时复制,这意味着如果值具有相同的内容,它将尝试共享值的相同实际内存表示形式 (zval)。inherit_static_prop为每个超类静态属性调用,以便可以将其复制到子类。实施inherit_static_prop确保子类的静态属性将是 PHP 引用,无论父类的 zval 是否共享(特别是,如果超类有引用,则子类将共享 zval,如果没有,则 zval 将共享被复制并且新的 zval 将被引用;第二种情况我们在这里并不真正感兴趣)。

所以基本上,当 A、B 和 C 形成时,$a对于每个类来说,将是不同的符号(即,每个类都有其属性哈希表,并且每个哈希表都有自己的条目$a),但底层 zval 将是相同的并且它将是一个引用。

你有类似的东西:

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

因此,当你做正常的作业时

static::$a = $v;

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

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

另一方面,当你这样做时

static::$a =& $v;

你将打破参考集。假设您在 A 类中执行此操作。您现在拥有:

//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

解决方法

正如戈登现已删除的答案中所指出的那样,三个类的属性之间的引用集也可以通过重新声明每个类中的属性来破坏:

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).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PHP 的特殊行为 (5.3)、静态继承和引用 的相关文章

  • PHP7.1上读取会话数据失败

    分享一个我遇到的问题 现已解决 在我的开发机器上 我使用 PHP 运行 IIS 我升级到 PHP7 突然我的代码不再工作 返回此错误 session start 读取会话数据失败 用户 路径 C WINDOWS temp 看起来像是权限问题
  • 何时使用 C++ 私有继承而不是组合?

    你能给我一个具体的例子吗 什么时候使用私有继承优于组合 就我个人而言 我将使用组合而不是私有继承 但在某些情况下 使用私有继承可能是特定问题的最佳解决方案 正在阅读C faq http www parashift com c faq lit
  • Laravel 5:在控制台内核的 Schedule() 函数中使用 Cache:: 或 DB::

    我正在尝试以不同的用户配置的时间间隔在 Laravel 5 中运行 Artisan 控制台命令 我已经构建了控制台命令 并拥有一个包含 运行频率 配置值的数据库 具有 Eloquent 模型 内schedule 的函数App Console
  • 动态重新定义 PHP 类函数?

    我试图弄清楚如何动态导入大量 PHP 类函数 例如 class Entity public function construct type require once type functions php person new Entity
  • C# 反序列化过程中创建指向父对象的指针

    我有这样的课程 Serializable public class child public Parent parent Serializable public class Parent public List
  • CodeIgniter加入选择为

    我的数据库中有 2 个表需要加入 一张表是 artikelen 表 另一张表是 Collections 表 我目前有 this gt db gt select this gt db gt from collecties this gt db
  • 如何在 Yii2 应用程序中显示多个选择下拉列表中的选定值?

    我正在研究 Yii2 我正在使用这样的自定义数组创建多个选择下拉菜单 在控制器文件中 all groups Groups find gt where group created by id gt orwhere new Expression
  • 计算帖子中使用 WordPress 短代码的次数

    我有以下 WordPress 短代码功能 function wp shortcode static i 1 return i i return return add shortcode shortcode wp shortcode 这很好用
  • 如何从文件中获取整个函数

    好的 我现在正在逐行阅读一个文件 我知道文件中的每个函数名称 因为它是在 XML 文档中的其他位置定义的 应该是这样的 function function name 其中 function name 是函数的名称 我从 XML 文档中获取所
  • 多维数组中的数组排列保留键 PHP

    这两天我一直在疯狂地尝试完成这个任务 也许你可以启发我 这是针对赛马投注排列的 每次用户玩游戏时 我都会得到一个多维数组 2 个级别 第一级包含比赛 ID 第二级包含用户为该比赛选择的马匹 它看起来像这样 play array 4 gt a
  • 如何通过开始索引和结束索引提取子字符串?

    str HelloWorld sub substr str 3 5 echo sub prints loWor 我知道 substr 采用第一个参数 第二个参数是开始索引 而第三个参数是要提取的子字符串长度 我需要的是通过提取子字符串起始索
  • 一次用 \r\n & \n & \r 分解字符串? [复制]

    这个问题在这里已经有答案了 我想按行分割字符串 但我希望它基于所有主要使用的换行符 n r n r 并返回一个包含每一行的数组 您可以使用正则表达式和preg split http php net preg split反而 lines pr
  • PHP 使用主键和辅助键对多维数组进行排序[重复]

    这个问题在这里已经有答案了 如何按主键和辅助键对多维数组进行排序 例如 假设有以下数组 result array result 0 prio 1 result 0 date 2010 02 28 result 0 post February
  • 如何使用 PHP 将字符串按大写字母分解?

    我有一个字符串 CamelCaseString 我想对大写字母进行explode split 或一些更好的方法来将该字符串分解为单个单词 最简单的方法是什么 解决方案更新 此链接指向一个略有不同的问题 但我认为答案通常比本页当前问题的答案更
  • Laravel 5 命名约定

    我对 Laravel 约定有点困惑 因为我是这个框架的新手 我正在关注 Jeffrey Way 他使用的 Laracasts 视频Plural对于控制器名称 E g 页面控制器 卡片控制器 帖子控制器 但如果我参考官方文档Laravel g
  • Laravel 4.2 Composer 安装错误:“无法扫描类”

    我想通过 Composer 在新的 Laravel 4 2 安装上安装一些软件包 但是 我遇到了例外 这是我的作曲家文件 name laravel laravel description The Laravel Framework keyw
  • WordPress 子主题包括包含文件

    我在一家WordPress使用 AMPPS 作为本地主机在我的本地计算机上进行设置 使用 Delta 主题 我创建了一个子主题 delta2 child 初始设置效果很好 但是 我需要更改包含文件夹中名为 home slider php 的
  • 通过 IP 地址限制 Laravel 错误日志

    When debug被设定为true在 Laravel 的app config php有什么方法可以限制结果Whoops包含对某些 IP 地址的堆栈跟踪的错误页面 并且不在该列表中的 IP 显示特定视图 Thanks 没有内置 但是你可能可
  • 为什么 0.5 mod 0.1 在不同的编程语言中结果不同?

    我有一个关于模数的问题 模运算求一个数除以另一个数的余数 我原本期望 0 5 0 1 0 的结果 但是当我在 PHP 或 net 中运行它时 我得到 0 1 我运行的 php 代码是 var dump fmod 0 5 0 1 在 net中
  • 通过 Facebook 图 api 点赞帖子

    你好 我对 facebook PHP SDK 没有什么问题 我想通过 facebook PHP SDK 点赞帖子或其他内容 我正在执行此代码 我认为它应该是正确的 但显然它不起作用 给定的错误代码是的 PHP SDK不知道这种POST请求

随机推荐