А типа в плюсах бросание исключений в деструкторе к чему-то хорошему приводит. Как минимум потеря памяти. Если такое предусматривается, то явно ошибка архитектуры
Я привел пример когда эксепшен бросается неявно.
Аналог в плюсах — обращение к нулевой памяти в деструкторе. Мы кидали исключение? нет. Но мы можем его поймать и увидеть, что тут где то указатель занулился. А не «Unknown:0»
я и не пытался. я просто решаю задачи в ООП стаил. деструкторы по мне это удобная штука управления обьектами. так же как и конструкторы.
я не возмущаюсь что там нет копирования, типизации, перегрузок и прочих радостей жизни.
но дядя сказал «поддерживаю», значит должен поддерживать. а внезапный конфуз «поддерживаю, но не так как велит разум» смущает.
(Если вы только про Unknown:0, меня бы вполне устроило «exceptions in destructors not allowed (object BlaBla)»)
К примеру меня убивают строки «Destructor order is not guaranteed to be deterministic.»
Последовательность деструкторов в одной области видимости действительно неявна.
Если вы с плюсов слазите в пхп, то вас ещё очень долго будет раздражать недопиленность последнего. Я сам пишу на обоих одновременно и как-то свыкся с областью задач каждого, и с возможностями.
это меня не задевает. меня выводит из равновесие нежелание разработчиков признать свои ошибки и исправить код.
а то получается как в том анекдоте. «у вас не работает такая то кнопка если открыть фаил с таким расширением.» в следующем патче приходит ответ «поддержка вот такой функции для файлов такого типа приостановлена»
Деструкторы к ООП имеют довольно относительное отношение.
Вообще, деструктор как явление относится к языкам с ручным управлением памятью. В языках с автоматическим управлением памятью (сборщиком мусора) деструкторы отсутствуют как класс — вместо них при необходимости применяются финализаторы.
Если бы вы писали на любом языке со сборщиком мусора (C# или Java), то знали бы — выбрасывание исключений в финализаторе ни к чему хорошему не приводят. В C# такое телодвижение вызывает брутальный краш программы, так как фактически исключение выкидывается в поток сборщика мусора.
Не надо перекладывать ваш 4-летний опыт программирования на С++ на другие языки.
последнее время читал блог пхп, и поражался насколько простые вещи пишут. что то вроде «использование классов», «наследование». подумал почему бы нет. к тому же подобной информации нигде не найти, дак может кому то пригодится. ведь поведение движка совершенно неадекватное и нелогичное.
и самое обидное исправления затрагивают только документацию.
Насколько помню поведение с перекрестными ссылками исправили в последних версиях PHP. Сам не пробовал проверять, не было необходимости, но разработчики уверяют что да.
Что касается деструкторов — у меня вот ни разу такой эксепшен не вываливался. Единственное что приходит на ум — закрытие хэндлеров на запись в файл, или каких других ресурсов. Больше мне не приходилось сталкиваться с необходимостью их использования. Для чистки памяти они не целесообразны. Для отрабатывания какого-либо кода по завершению исполнения, как это иногда любят делать, тоже. Для этого есть register_shutdown_function. Словом, проблема возможно и есть, но не критичная.
ИМХО очень субьективно. Мне, например, было интересно. Сам пару лет назад натыкался на странное поведение деструктора в PHP, но забил и решил задачу иначе, не было времени копать. Теперь стало понятнее.
Про
$earth = new world();
$vasya = new human($earth);
— напишите минимальный код, воспроизводящий проблему. Деструктор $earth ну никак не должен вызываться раньше деструктора $vasya, т.к. счетчик ссылок на объект не становится равным нулю до смерти $vasya. Скорее всего, где-то в доугом месте у вас проблема.
Что касается остального, то это не столько проблемы php, сколько проблемы самой идеологии синхронных деструкторов (увы). Если вы в c++ начнете активно использовать shared_ptr (а лучше бы начать), будет почти то же самое.
Смотрите. Есть обьект SCREEN. Это подсистема страниц экранов (аля дос). Отдельно рисуется лог (который дампится в фаил), отдельно то что видет юзер, отдельно скажем каллстек и отдельно команды js библиотеке, которые в конце переводятся в json и дампятся в какой нибудь див. Надеюсь назначение ясно.
Все поля protected, все паблик методы — static. Экземпляры создаются (детьми), для какой то определенной цели. В коде деструктора ничего не зануляется, он пустой.
— Пусть есть некоторый обьект $DUMMY. В деструкторе он хочет сделать что то типа SCREEN::AddToErrorLog($text);
— $ScreenErrorLog был создан раньше $Dummy.
Таким образом ссылок на экран нет. Но он был создан раньше, и нигде не удалялся. Значит логично предположить что он существует. Увы, нет. Скрипт выдает что то типа «ребенок уже был завершен, все что должен был вывел куда нужно, и теперь только в режиме рид онли».
Может быть немного коряво, но суть, что обьекты должны удаляться в порядке обратном созданию, до тех пор пока ссылки не требуют обратного. А в пхп они удаляются в прямом порядке.
уничтожать объекты по моему логично только когда исключается возможность работы с ними. это происходит только во время покидания области видимости.
я объект не уничтожал, и хочу с ним работать. обьект пропал. схчегобы?
насколько я помню механизм работы сборщика мусора, он не мониторит постоянно изменения. Он делает это только тогда, когда надо выделить еще памяти, а свободной уже мало при данном объеме. В итоге гарантировать что из кучи выберут именно тот или иной объект в таком-то порядке нельзя. Выберут тот объект, на который больше не ссылается ни одна переменная, и конечно же, тот, что не вызовет большой фрагментации. Хотя насчет последнего я могу быть не прав, дано дело было.
Вы бы почитали вначале про алгоритм сбора мусора со счетчиком ссылок (а еще лучше — конкретно про php-шный), а то вам все время что-то неправильное кажется.
Это все интересно, конечно. Но вы правда не знаете, что такое «минимальный код, воспроизводящий проблему»? Если не знаете, то это самый главный баг, с него надо начинать, а там и с деструкторами все станет в порядке, уверяю.
Да, и про
$a->ref = $b;
$b->ref = $a;
— не до конца это будет существовать в 5.3, а до момента, когда запустится цикл сборки мусора. Он запустится. Когда именно — зависит от настроек, плюс есть статья на английском, где подробно расписано, как в 5.3 работает сборщик мусора (работает он, кстати, довольно интересно — я такого комбинированного метода ни в одном другом языке не видел).
Сборщик по умолчанию отключен. В концепции скриптов живущих миллисекунды убирать мусор сразу себе дороже, проще отработав убрать всё, по началу к этому сложно привыкнуть.
я уже признал свою ошибку. я не думал что там есть сборщик мусора времени исполнения.
думал что переменные очищаются согласно логике локальных переменных в спп, обьекты когда теряются все ссылки и прочее.
а если смотреть на пхп с этой стороны, то деструкторы не такая плохая вещь.
В PHP нет деструкторов. Несмотря на то, что они так называются — фактически это финализаторы. Их следует использовать только в случае работы с неуправляемыми ресурсами, такими как файлы, сокеты и их аналоги, для того чтобы гарантировать их освобождение. Использование финализаторов для RAII чревато.
Я не говорю, что RAII не работает. Я говорю, что использование финализаторов для RAII в языках с автоматическим управлением памятью чревато проблемами. Суть проблем описана чуть выше.
И я. У меня вся работа с транзакциями построена на raii — создается объект-транзакция и, если вдруг он внезапно уничтожается, транзакция роллбэчится. Незаменимая вещь, когда приходится работать с кодом, который может в любой момент выкинуть исключение.
> Глобальные переменные (аля сессий) перестают быть валидными, доступ к файловой системе обрубается.
В PHP есть чудесная функция register_shutdown_function(). В ней доступ к файловой системе есть.
Ваша проблема, во-первых, в том, что вы переносите подходы к программированию из одного языка в другой, во-вторых, используете ненадежные конструкции.
Не используйте деструкторы для выполнения какой-либо логики (это не только к PHP относится). Если вам надо отсоединить Васю от мира, обнулить ему карму, и прочее, сделайте явный вызов например $world->detach($vasyaId) или $vasya->detach();
Я не представляю ситуации, где деструкторы *жизненно* неоходимы. Единственное, что я бы в них оставил — это вещи типа assert() (например, убедиться что все важные данные сохранены в БД перед уничтожением объекта).
Вам надо сохранить какой-нибудь кеш на диск перед завершением работы скрипта? Сделайте например вызов App::addShutdownHandler(array($his, 'saveData')) и все будет работать.
Явное всегда лучше неявного. Я сталкивался с логикой в конструкторах, и ничего, кроме негативных эмоций, это не вызывает. Это треш, который невозможно отлаживать.
В Си++, который вы упоминаете, деструкторы тоже предназначены исключительно для освобождения памяти дочерних объектов, а люди, которые туда пихают логику, напрашиваются на канделябр от коллеги-разарботчика.
Ну глупость же.
Зачем мне всегда явно васю от мира откреплять. Пусть это инкапсулируется, а я сделаю просто delete vasya.
Многое повидал, но что бы «деструкторы тоже предназначены исключительно для освобождения памяти дочерних объектов», первый раз.
К примеру дерево где для каждого узла считается количество узлов ниже.
Что мне теперь нельзя в деструкторе сделать parent->ChildDetached(this);?
Обязательно это делать явно? Такой подход мне непонятен.
> В PHP есть чудесная функция register_shutdown_function(). В ней доступ к файловой системе есть.
Ясно что есть возможность извернуться в коде так что бы не наступать на это. Но пахнуть не перестанет.
Я в упор не могу понять что в принципе мне может запрещать писать в файлы, нарушается какая то логика, прадигма, или что?
Я просто хочу сбросить дамп, нельзя, канделябр? Круто.
> Ваша проблема, во-первых, в том, что вы переносите подходы к программированию из одного языка в другой, во-вторых, используете ненадежные конструкции.
К сожалению изза вики «Испытал влияние: Perl, C, C++, Java». Вот и подумалось.
Я ни в коем случае не говорю «мир, подстраивайся под меня». Я надеюсь что те кто пройдет путь подобный мне будет предупрежден, а те кто проходили вспомнят былое вместе.
1. Вы, это, когда баги постите, то используйте версии не двухгодичной давности, а последние.
2. Когда читаете сообщения о багах, то читайте до конца, иногда их открывают заново.
3. Если всё же постите баг, то приводите в пример код, который его воспроизводит, не надо пытаться объяснять «на пальцах».
4. Когда видите баг, возможно кто-то его заметил ранее и стоит его поискать.
P.S. global $_SESSION; — использование такой конструкции это тревожный сигнал.
У вас пять пунктов. Ссылки даны только для 3, а не для всех. И ещё. Первый баг, по мнению его создателся, относился к документации, соответственно изменения внесли в документацию. Вот ссылка на сам баг, где он закрыт: https://bugs.php.net/bug.php?id=47143 .
Друзья, у меня вопрос, а что если в деструкторе вызываются методы, которые могут вызвать исключительную ситуацию? Такого не должно быть и они должны быть вызваны явно?
лучше так не делать.
там не должно быть исключений уровня логики. допускаются только фатальные эксепшены, которые делают не более чем запись лог/отправка данных и выключают скрипт.
PHP не любит деструкторы