Добрый день!
На днях я столкнулся с поведением finally в php версии 5.5 которого не ожидал и, думаю, многие о нем не знают.
Суть ошибки в следующем. У нас есть блок try-finally. В try бросается Exception. Далее в finally выполняется код бросающий в процессе свой Exception и ловящий его. При этом первый Exception, который должен вылететь из try, забывается.
Под катом рафинированный пример ошибки и пара выводов о возможности использования finally в 5.5.
try {
try {
echo "1n";
throw new Exception("E1");
} finally {
try {
echo "2n";
throw new Exception("E2");
} catch (Exception $e) {
echo "3n";
}
}
echo "4-n";
} catch (Exception $e) {
echo "4+".$e->getMessage()."n";
}
echo "5n";
В 5.6 результат ожидаемый: 1 2 3 4+E1 5.
В 5.5 же к моему удивлению я получил 1 2 3 5
(результаты выполнения можно посмотреть на 3v4l.org)
Ошибку я накануне завел в bugs.php.net и уже успел получить следующий ответ:
the problem is because, after finally supporting was merged into 5.5. we did a refactor to it for performance……
and in the new implementation, we need a extra field in EG to fix this issue, which will break ABI compitiable, thus is not allowed to merge into 5.5
Т.е. в моем вольном переводе получается что проблема связана с тем что после того как finally был добавлен в 5.5 его отрефакторили для улучшения производительности и в новой реализации необходимо дополнительное поле EG что бы исправить ошибку, но это ломает API что для уже запатченной 18-ть раз 5.5 сделать нельзя.
Следующие выводы можно сделать из сложившихся обстоятельств:
- в finally спокойной можно использовать совсем простые действия в которых гарантированно нет механизмов с ловлей Exception'ов
- в finally можно использовать проверенный код о работе которого всё известно и в котором нет механизма ловли Exception'ов вообще
- в случае если ваш autoloader сам использует внутри Exception'ы, то использование finally с кодом создающим новые объекты или использующий статические методы, переменные или константы (т.е. код где неявно возможно задействование автолоадера) может повлечь за собой потерю Exception'а
- отказаться от использования finally до перехода на версию 5.6
UPD если про эту ошибку сейчас забудут что более чем вероятно, то она скорее всего всплывет когда фреймворки перестанут поддерживать версию 5.4, но ещё будут поддерживать 5.5 и начнут активано использовать долгожданный finally.
Сам я эту её поймал когда у меня кусок кода работал по разному в зависимости от того использовал ли я в finally logger или нет. Моему удивлению не было предела и я перебрал весь Monolog пока не нашел что дело в самописному адаптере пишущем логи в базу и ловящем ошибки при записи. И даже в этом случае я не сразу стал верить что дело в php и область поиска бага постепенно сужал пока не свёл к коду, написанному в этой заметке выше :)
Автор: AlexeyDsov