重复的数组键(注意:多次从__sleep()返回成员变量“ a”)

Ben*_*aap 7 php arrays associative-array php-internals

标题似乎有些愚蠢,但是我对此非常认真。今天在工作中,我遇到了无法解释的怪异的PHP行为。幸运的是,此行为已在PHP 7.4中修复,因此似乎也有人偶然发现了该行为。

我举了一个小例子来说明出了什么问题:

<?php

class A {
    private $a = 'This is $a from A';

    public $b = 'This is $b from A';

    public function __sleep(): array
    {
        var_dump(array_keys(get_object_vars($this)));

        return [];
    }
}

class B extends A
{
    public $a = 'This is $a from B';
}

$b = new B;

serialize($b);
Run Code Online (Sandbox Code Playgroud)

在此处运行此代码:https : //3v4l.org/DBt3o

这里是这里发生的事情的一点解释。我们必须同时共享一个属性的A类和B类$a。细心的读者注意到,该物业$a具有两种不同的可见性(公共,私人)。到目前为止还算不上什么。魔术发生在实例中__sleep被神奇调用的方法中serialize。我们希望拥有所有获得的对象变量,将其get_object_vars减少为仅包含键,array_keys并使用输出所有内容var_dump

我期望这样(从PHP 7.4开始发生,这是我的期望输出):

array(2) {
  [0]=>
  string(1) "b"
  [1]=>
  string(1) "a"
}
Run Code Online (Sandbox Code Playgroud)

但是我得到的是:

array(3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "a"
}
Run Code Online (Sandbox Code Playgroud)

PHP如何提供带有两个完全相同的键的数组?谁能解释内部发生的事情,因为在普通的PHP中我无法生成具有两个完全相同的键的数组?还是我想念这里明显的东西?

我的同事起初不想让我相信,但是在了解了这里发生的事情之后,他们都没有一个很好的解释。

我真的很想看到一个很好的解释。

rev*_*evo 6

我找不到问题中的错误报告,但有趣的是,这个提交似乎解决了同样的问题:

如果我们处于隐藏私有属性可见的范围内,则隐藏公共属性应该不可见。

测试代码写得很好,我们可以在此处进行简单的更改:

class Test
{
    private $prop = "Test";

    function run()
    {
        return get_object_vars($this);
    }
}

class Test2 extends Test
{
    public $prop = "Test2";
}

$props = (new Test2)->run();
Run Code Online (Sandbox Code Playgroud)

调用var_dump()$props显示:

array(2) {
  ["prop"]=>
  string(5) "Test2"
  ["prop"]=>
  string(4) "Test"
}
Run Code Online (Sandbox Code Playgroud)

回到你的问题:

PHP 怎么会提供一个带有两个完全相同的键的数组?谁能解释这里内部发生​​的事情,因为在普通的 PHP 中我无法生成具有两个完全相同键的数组?

是的,您不能拥有具有两个相同键的数组:

var_dump(array_flip(array_flip($props)));
Run Code Online (Sandbox Code Playgroud)

结果是:

array(1) {
  ["prop"]=>
  string(4) "Test"
}
Run Code Online (Sandbox Code Playgroud)

但让我不同意你的观点,two completely identical keys因为这两个具有相同键名的元素在哈希表内部没有使用相同的键存储。也就是说,除了潜在的冲突之外,这些都被存储为唯一的整数,并且由于这种情况在内部发生,因此忽略了对用户输入的限制。