Изменено поведение (un) serialize ()?

РЕДАКТИРОВАТЬ: Проблема является задокументированной ошибкой php сейчас: https://bugs.php.net/bug.php?id=71617 спасибо, что нашли это @Danack

Я просто перенес приложение из PHPH 5.5 в PHP 7 и наткнулся на какое-то странное поведение, когда дело доходит до сериализации объектов.

Я попытался свести его к минимальному, полному и проверяемому примеру, который можно найти на http://sandbox.onlinephpfunctions.com/code/e926a7398119ea715531cafe4ce6a22c329e53b8

Проблема в том, что если класс расширяется ArrayObject тогда все частные свойства, кажется, просто исчезают, если вы serialize() а потом unserialize() этот объект:

  1. создать класс с частным свойством и метод получения / установки для этого свойства
  2. создать объект этого класса
  3. установить частную собственность через метод установки
  4. serialize() объект
  5. unserialize() результат шага 4
  6. вызов метода получения приватного свойства, результат зависит от вашей версии PHP
    • PHP 5.3 — PHP 5.6: результат — значение, установленное на шаге 3
    • PHP 7: результат равен нулю

Я попытался свести его к минимальному, полному и проверяемому примеру, который можно найти на 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() в нашем проекте мне действительно нужно убедиться, что поведение PHP 7 на 100% совместимо с поведением PHP 5.3+.

Вопрос: Как я могу заставить PHP 7 вести себя как PHP 5.3+ ??

3

Решение

Хотя это исправлено в следующей версии PHP, тот факт, что ваш код использует недокументированное поведение, является ошибкой, известной как «Программирование по совпадению«. Из прекрасной статьи:

Как программировать по совпадению

Предположим, что Фред получил задание на программирование. Фред вводит какой-то код, пробует его и, похоже, работает. Фред вводит еще немного кода, пробует его, и, похоже, все еще работает. После нескольких недель кодирования программа внезапно перестает работать, и после нескольких часов попыток исправить это, он все еще не знает, почему. Фред вполне может потратить значительное количество времени, преследуя этот кусок кода, даже не имея возможности его исправить. Неважно, что он делает, кажется, что он никогда не работает правильно.

Несчастные случаи реализации

Несчастные случаи реализации — это то, что происходит просто потому, что так написан код. В конечном итоге вы полагаетесь на недокументированную ошибку или граничные условия.

В этом случае нет гарантии, что при расширении ArrayObject значения дочернего класса будут правильно не сериализованы.

Либо использование композиции, а не наследования будет намного безопаснее, либо написание методов сериализации / десериализации для дочернего метода позволит вам контролировать сериализацию / десериализацию. Альтернативно, просто не использовать serialize / unserialize и вместо этого использовать ваш собственный интерфейс также может быть более предсказуемым, чем «магические» внутренние методы.

2

Другие решения

Других решений пока нет …