Duplicate array keys (Notice: member variable "a" returned from __sleep() multiple times)

I couldn't find a report for the bug in the question but interestingly it seems this commit addresses the same thing:

If we are in a scope where the shadowed private property is visible, the shadowing public property should not be visible.

The test code is well-written, with a simple change we could have it here:

class Test
{
    private $prop = "Test";

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

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

$props = (new Test2)->run();

Calling var_dump() on $props shows:

array(2) {
  ["prop"]=>
  string(5) "Test2"
  ["prop"]=>
  string(4) "Test"
}

Back to your question:

How could it be, that PHP delivers an array with two completely identical keys? Who is able to explain what happens here internally because in plain PHP I'm not able to generate an array with two completely identical keys?

Yes, you are not able to have an array with two identical keys:

var_dump(array_flip(array_flip($props)));

results in:

array(1) {
  ["prop"]=>
  string(4) "Test"
}

but let me not agree with you on two completely identical keys as these two elements with identical key names aren't stored with identical keys internally within a hashtable. That is, those are stored as unique integers except on potential collisions and as this has been occurring internally the restriction on user inputs was ignored.


After messing with this a bit, it looks like this doesn't depend on __sleep().

Apparently this was always the case in earlier versions of PHP 7 (but apparently not in PHP 5). This smaller example shows the same behavior.

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

    public function showProperties() { return get_object_vars($this); }
}

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

$b = new B;
var_dump($b->showProperties());

Output from PHP 7.0 - 7.3

array(2) {
  ["a"]=>
  string(17) "This is $a from B"
  ["a"]=>
  string(17) "This is $a from A"
}

I think the private $a in the parent is a different property than the public $a in the child. When you change the visibility in B you're not changing the visibility of the $a in A, you're really making a new property with the same name. If you var_dump the object itself you can see both properties.

It shouldn't have much effect though, since you wouldn't be able to access the private property from the parent class in the child class, even though you can see it exists in those earlier PHP 7 versions.