php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #74586 Arrays and objects with duplicate property/key names (serialization bug)
Submitted: 2017-05-13 11:53 UTC Modified: 2017-11-23 23:16 UTC
Votes:9
Avg. Score:4.8 ± 0.4
Reproduced:7 of 8 (87.5%)
Same Version:7 (100.0%)
Same OS:2 (28.6%)
From: daniil at daniil dot it Assigned:
Status: Duplicate Package: *Programming Data Structures
PHP Version: Irrelevant OS:
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: daniil at daniil dot it
New email:
PHP Version: OS:

 

 [2017-05-13 11:53 UTC] daniil at daniil dot it
Description:
------------
Deserializing a class that has attributes with access modifiers different from the ones used during deserialization creates attributes with duplicate attribute names, and converting such objects to arrays using get_object_vars creates arrays with duplicate keys.

This can also be considered a memory leak, since the attributes with the old access modifiers cannot be unset or modified.

Test script:
---------------
// a.php
<?php
class a {
    public $a = 'public';
    public function dump() { return get_object_vars($this); }
}
$a = new a();
file_put_contents('test', serialize($a));
var_dump($a, $a->dump());

// b.php
<?php
class a {
    protected $a = 'protected';
    public function dump() { return get_object_vars($this); }
}
$a = unserialize(file_get_contents('test'));
file_put_contents('test', serialize($a));
var_dump($a, $a->dump());

// c.php
<?php

class a {
    private $a = 'private';
    public function dump() { return get_object_vars($this); }
    public function adump() { return (array)$this; }
    public function vdump() { foreach ($this as $key => $value) { var_dump($key.' => '.$value); } }
    public function unset() { var_dump('UNSETTING $this->a'); unset($this->a); }
}
$a = unserialize(file_get_contents('test'));
file_put_contents('test', serialize($a));
$array = $a->dump();
var_dump($a, $array, $array['a']);

var_dump('UNSETTING $array["a"]');
unset($array['a']);
var_dump($array);

var_dump('UNSETTING $array["a"]');
unset($array['a']);
var_dump($array);

var_dump('UNSETTING $array["a"]');
unset($array['a']);
var_dump($array);


var_dump((object)$a->adump());

$a->vdump();
$a->unset(); // The private property is being unset
$a->vdump();

$a->unset(); // Nothing is being unset
$a->vdump();



Expected result:
----------------
// php a.php && php b.php && php c.php

// a.php
object(a)#1 (1) {
  ["a"]=>
  string(6) "public"
}
array(1) {
  ["a"]=>
  string(6) "public"
}

// b.php
object(a)#1 (2) {
  ["a":protected]=>
  string(9) "protected"
  ["a"]=>
  string(6) "public"
}
array(2) { // NICE
  ["a"]=>
  string(9) "protected"
  ["a"]=>
  string(6) "public"
}

// c.php
object(a)#1 (3) {
  ["a":"a":private]=>
  string(7) "private"
  ["a":protected]=>
  string(9) "protected"
  ["a"]=>
  string(6) "public"
}
array(3) {
  ["a"]=>
  string(7) "private"
  ["a"]=>
  string(9) "protected"
  ["a"]=>
  string(6) "public"
}

string(6) "public" // The last element is always fetched/unset/modified

string(21) "UNSETTING $array["a"]"
array(2) {
  ["a"]=>
  string(7) "private"
  ["a"]=>
  string(9) "protected"
}
string(21) "UNSETTING $array["a"]"
array(1) {
  ["a"]=>
  string(7) "private"
}
string(21) "UNSETTING $array["a"]"
array(0) {
}
object(stdClass)#2 (3) { // Since the array generated using the array cast contains the access modifier indicators (private variables have the class name prepended to the variable name; protected variables have a '*' prepended to the variable name.), casting that array back to an object works (so the behaviour can be replicated manually without serialize)
  ["a":"a":private]=>
  string(7) "private"
  ["a":protected]=>
  string(9) "protected"
  ["a"]=>
  string(6) "public"
}
string(12) "a => private"
string(14) "a => protected"
string(11) "a => public"
string(18) "UNSETTING $this->a"
string(14) "a => protected"
string(11) "a => public"
string(18) "UNSETTING $this->a"
string(14) "a => protected"
string(11) "a => public"



Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-11-23 23:16 UTC] pmmaga@php.net
-Status: Open +Status: Duplicate
 [2017-11-23 23:16 UTC] pmmaga@php.net
This is a duplicate of #49649 and was fixed by this commit:

http://git.php.net/?p=php-src.git;a=commit;h=7cb5bdf64a95bd70623d33d6ea122c13b01113bd
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Nov 30 12:01:29 2024 UTC