EDIT: Проблема – это зарегистрированная ошибка php: https://bugs.php.net/bug.php?id=71617 благодаря обнаружению того, что один @Danack
Я просто переношу приложение из PHPH 5.5 на PHP 7 и наткнулся на какое-то странное поведение, когда дело доходит до сериализации объектов.
Я попытался подготовить его к минимальному, полному и проверенному примеру, который можно найти по адресу http://sandbox.onlinephpfunctions.com/code/e926a7398119ea715531cafe4ce6a22c329e53b8
Проблема в том, что если класс расширяет ArrayObject, то все частные свойства, похоже, просто исчезают, если вы serialize()
а затем unserialize()
этот объект:
serialize()
объект unserialize()
результат шага 4 Я попытался свести его к минимальному, полному и проверенному примеру, который можно найти по адресу http://sandbox.onlinephpfunctions.com/code/e926a7398119ea715531cafe4ce6a22c329e53b8, где вы можете протестировать код с разными версиями PHP.
<?php class demoStdObject { public $public = ''; protected $protected = ''; private $private = ''; public function getPublic() { return $this->public; } public function getProtected() { return $this->protected; } public function getPrivate() { return $this->private; } public function setPublic($public) { $this->public = $public; } public function setProtected($protected) { $this->protected = $protected; } public function setPrivate($private) { $this->private = $private; } } class demoArrayObject extends ArrayObject { public $public = ''; protected $protected = ''; private $private = ''; public function getPublic() { return $this->public; } public function getProtected() { return $this->protected; } public function getPrivate() { return $this->private; } public function setPublic($public) { $this->public = $public; } public function setProtected($protected) { $this->protected = $protected; } public function setPrivate($private) { $this->private = $private; } } $arrayObject = new demoArrayObject(); $stdObject = new demoStdObject(); testSerialize($arrayObject); echo str_repeat('-',30) . "\n"; testSerialize($stdObject); function testSerialize($object) { $object->setPublic('public'); $object->setProtected('protected'); $object->setPrivate('private'); $serialized = serialize($object); $unserialized = unserialize($serialized); echo get_class($object) . ":\n"; echo $unserialized->getPublic() . "\n"; echo $unserialized->getProtected() . "\n"; echo $unserialized->getPrivate() . "\n"; }
Выход для PHP 5.6:
demoArrayObject: public protected private ------------------------------ demoStdObject: public protected private
Выход для PHP 7:
demoArrayObject: public protected ------------------------------ demoStdObject: public protected private
Я не смог найти никаких документированных изменений, связанных с serialize()
, unserialize()
или классом ArrayObject
поэтому мне интересно, что происходит. Это ошибка? Недокументированная функция? 😉
Поскольку мы делаем много serialize()
/ unserialize()
в нашем проекте, мне действительно нужно убедиться, что bahavior PHP 7 на 100% совместим с поведением PHP 5.3+.
Вопрос: Как я могу заставить PHP 7 вести себя как PHP 5.3+?
Хотя это исправлено для следующего выпуска PHP, тот факт, что ваш код полагается на недокументированное поведение, является ошибкой, известной как « Программирование путем совпадения ». Из прекрасной статьи:
Как программировать путем совпадения
Предположим, что Fred задано программирование. Фред в каком-то коде, пытается, и, похоже, работает. Фред использует еще один код, пытается его, и он все еще работает. После нескольких недель кодирования таким образом, программа внезапно перестает работать, и после нескольких часов попытки исправить это он все еще не знает, почему. Фред вполне может потратить значительное количество времени на то, чтобы преследовать этот кусок кода, не имея возможности его исправить. Независимо от того, что он делает, он просто никогда не работает правильно.
Несчастные случаи
Несчастные случаи реализации – это вещи, которые происходят просто потому, что именно так написан код. В конечном итоге вы полагаетесь на недокументированную ошибку или граничные условия.
В этом случае нет гарантии, что при расширении массива ArrayObject значения дочернего класса будут корректно неэтериализованы.
Либо использование композиции, а не наследование было бы намного более безопасным, или писать методы сериализации / несериализации по дочернему методу позволяли бы управлять сериализацией / десериализацией. В качестве альтернативы, просто не используя сериализацию / несериализацию и вместо этого используя свой собственный интерфейс, также может быть более предсказуемым, чем «магические» внутренние методы.