PHP не любит деструкторы

в 20:16, , рубрики: bugs, php, php5, баги, метки: , , ,

Столько воды утекло с тех пор как ПХП провозглосил себя ООП языком. Мне не удалось уточнить когда именно, но «On July 13, 2004, PHP 5 was released, powered by the new Zend Engine II. PHP 5 included new features such as improved support for object-oriented programming».
Т.е теоретически уже тогда появились конструкторы и деструкторы. Сам пользуюсь версией 5.3.2, но диву даюсь что он вытворяет.

Немного о себе. Я программист С++, опыт 4 года. Специализация — компьютерная графика и работа с сетью. А именно создание сетевых игр. Но таким играм нужен сервер, а серверу база игроков. А игроки хотят еще и сайт. «Зачем нанимать веб-программиста, я ведь и сам неплох. Заодно и язык изучу.»
Так я думал пол года назад, но до сих пор не могу понять!

Классическое

Думаю многие кто работал с деструкторами однажды столкнулся:
PHP Fatal error: Exception thrown without a stack frame in Unknown on line 0
Первая реакция — недоумение. Вторая матная. И ведь впихивание exit() везде не дает результата, ведь догадка о том что эксепшен в деструкторе приходит далеко не сразу, а если он происходит, то скорее всего кодовая база значительная.
Ответ c bugs.php.net/bug.php?id=33598
sniper@php.net:
Throwing exceptions in __desctruct() is not allowed.
Should be documented..
vrana@php.net:
Thank you for the report, and for helping us make our documentation better.
"Attempting to throw an exception from a desctructor causes a fatal error."

Весело? Лично мне не очень.
Воспроизводится ошибка с неявным исключением очень просто.
class a
{
// ...
public function __destruct()
{
global $_SESSION;
$_SESSION = "Some information";
}
}
$dummy = new a();

Реверсный порядок очистки

Пусть в некоторой области видимости(глобальной и неочень) у нас следующий код:
$earth = new world();
$vasya = new human($earth);

Соответственно в коде конструктора человека идет его пришпандоривание к миру. (Предполагается что без мира вася существовать не будет, отбросим философию, нам проект скорее закрыть.)
Ну а в коде деструктора вася лежит аля $this->my_world->RemoveHumanFromWorld($this), в котором вызываются RemoveAllMyStuff, CalculateKarma и прочее. (Предположим у нас в мире не хранится ссылка на васю, так как это не требовалось в рамках задачи)
Что делает пхп при выходе из области видимости? Уничтожает мир и падает с ошибкой «Fatal error: Call to a member function RemoveHumanFromWorld() on a non-object in /home/god/my_projects/earth/creatures/reasonable/homo_sapiens.php on line 1956».
(Поэтому кстати мир был написан на С++, ведь богу не нужно что бы он запускался на любой вселенной. Виртуальный космос с сборщиком мусора. Ха Ха.)
Ответ с bugs.php.net/bug.php?id=36759
dmitry@php.net
Fixed in CVS HEAD and PHP_5_2.
Найти бы этого дмитрия и ткнуть носом как в той картинке. Не знаю как там в последних версиях, пока не обновляюсь, но в 5.3 актуально.

Завершение скрипта — исключительная ситуация

Ох, про это столько можно писать. Глобальные переменные (аля сессий) перестают быть валидными, доступ к файловой системе обрубается. И вообще как я понимаю корректная работа скрипта не гарантируется. Так что не дай бог вам выполнять что то в деструкторе глобального обьекта…
Но это опустим. В документации пхп 5.1 и ранее (строфу и стих не найду) сказано: «The destructor method will be called as soon as there are no other references to a particular object», что в принципе логично для языка без строгого требования конструкции delete (лат. unset).
После баг репорта bugs.php.net/bug.php?id=38572
Документация изменилась «The destructor method will be called as soon as there are no other references to a particular object, or in any order during the shutdown sequence»
Удобно? По мне — не очень.

Только прямые ссылки

Пусть согласно логике языка обьект $a должен удалиться раньше $b.
Но пусть в обьекте $b хранится ссылка на поле $a->some_data.
Тогда по логике обьект $b должен удалиться раньше. Увы, в пхп не так. Подобной баги я не нашел, но ситуация специфическая(и не побоюсь слова исключительная). Избегается несильным патчем до ссылки на $a, терпимо и репортить я не стал.

Дедлок в стиле пхп

$a->ref = $b;
$b->ref = $a;
Мне в свое время было интересно, как пхп справится с перекресными ссылками. Зависнет ли? Упадет ли с ошибкой «нельзя покинуть область видимости», и как она будет звучать на английском. Увы, теплица показала — все переменные будут существовать до конца, пока не придет как говорится «or in any order during the shutdown sequence»

Заключение

На данный момент все что я вспомнил. А может и все что я встречал. Но создалось впечатление что деструкторы в ПХП это костыль, так что скорее всего скоро опять наткнусь.
Мне кажется будущее ООП веб-программирование за интерпретатором c++, и возможно он будет готов. Возможно кто то однажды возьмется изменить с++, добавить стандартных конструкций что бы он стал ориентированным под веб. Но пока я альтернатив не нашел.

Ссылки

en.wikipedia.org/wiki/PHP
php.net/manual/en/language.oop5.decon.php
Хотя чего там мелочиться:
[http://].*php.net.*

Автор: Offenso

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js