1

I've got two scripts and their outputs:

1st script

class A {
  public $view = "foo";

  public function getView()
  {
      return $this->view;
  }
}

$a = new A();
$b = serialize($a);
file_put_contents("/tmp/test.tmp",$b);
var_dump($b);

and it's output:

object(A)[1]
  public 'view' => string 'foo' (length=3)

string 'O:1:"A":1:{s:4:"view";s:3:"foo";}' (length=33)

than I run:

2nd script

class A {
   private $view = "bar";

   public function getView()
   {
       return $this->view;
   }
}

$a = unserialize(file_get_contents("/tmp/test.tmp"));

var_dump($a, $a->getView());

and it displays:

object(A)[1]
  private 'view' => string 'bar' (length=3)
  public 'view' => string 'foo' (length=3)

string 'bar' (length=3)

As you can see the only change is that public $view became private.

My coleague Peter found this, mind blown :)

Edit:

I believe it could be really problematic if you serialize some objects (thru Doctrine for example) to DB, than update your codebase without updating data stored in DB (which will became - as I assume - parsing objects serialized to text and update them with some migration scripts) and then unserialize data and work on it. It's not so uncommon I think and behaviour of that could be uncontrolled. Would love to see PHP throwing an error/exception etc. if unserialized object class definition differs from actual one.

Uirapuru
  • 119
  • 1
  • 8
  • 8
    I wouldn't exactly call it a bug. It seems more like a programmer error, since you're unserialising onto a class with a mismatched definition than the one you unserialised from, which is basically undefined. – slugonamission May 22 '14 at 21:44
  • I'm not sure what the question is here. – vascowhite May 22 '14 at 21:47
  • 4
    @vascowhite the question is stated in big bold letters at the top of the page and in the browser title: **Is that a PHP bug or a feature?** – 000 May 22 '14 at 21:49
  • You could use `$b = json_encode( (array)$a ); ` instead of serialize and unserialize :) – Hackerman May 22 '14 at 21:49
  • 2
    Not a new feature, it is just the way it is. When you unserialise the string, PHP try convert it back to a object, but it needs the object definition. You've change the definition, therefore, PHP use the new definition. – David Lin May 22 '14 at 21:52
  • Thing that interests me the most is why there could be two members with the same name? And if I remember, at the begining, PHP objects where "enhanced" arrays, which disallow to have two same keys. Other thing - I think it's easy to detect collision. It is not a good moment to throw some error? – Uirapuru May 22 '14 at 21:58
  • 1
    @Uirapuru: They don't have the same name. The difference in their name is most likely not visible to you, compare with: [Array to Object and Object to Array in PHP - interesting behaviour](http://stackoverflow.com/q/6325447/367456) – hakre May 22 '14 at 22:02
  • @RobertRozas: json_encode() only encapsulates data, it doesn't encode the object type. – Marc B May 22 '14 at 22:04
  • @MarcB, i see that you miss the cast `(array)$a` – Hackerman May 22 '14 at 22:06

2 Answers2

4

It's neither. Using unserialize with serialized data representing an object of a class whose definition is different from the class definition known to unserialize's calling environment is simply not defined in the PHP documentation. Therefore it's not an official feature. But since the behavior and the outcome is not defined the result can also not considered to be a a bug.

But if you really want to put it into one of the two categories "bug" or "feature", you might call it an undocumented feature since it doesn't seem to make PHP move into a state where the PHP script execution is "broken". (I wouldn't rely on this behavior for future versions though - but that's a different story.)

Hauke P.
  • 2,695
  • 1
  • 20
  • 43
  • Thanks for your answer. Now I'm curious - is there a practical case to use that in some bad-for-production-good-for-hacking code :) – Uirapuru May 22 '14 at 22:11
  • That depends on your definition of "bad-for-production-good-for-hacking code". – Hauke P. May 22 '14 at 22:13
1

You could get weird result faster(demo):

var_dump(unserialize("O:8:\"stdClass\":3:{s:4:\"\0*\0p\";N;s:11:\"\0stdClass\0p\";N;s:1:\"p\";N;}"));

//class stdClass#1 (3) {
//  protected $p =>
//  NULL
//  private $p =>
//  NULL
//  public $p =>
//  NULL
//}

\0 -- 0 byte character

It's not a bug: https://bugs.php.net/bug.php?id=51173

Fixing this creates more issues, like performance drop, than it solves.

Documentation could contain some words about it, but it does not.

sectus
  • 15,605
  • 5
  • 55
  • 97