Как обойти отсутствие блока finally в PHP?
Заголовок: Отсутствие блока finally в PHP до версии 5.5
Описание проблемы:
В PHP до версии 5.5 отсутствует блок finally. В большинстве других языков программирования можно использовать конструкцию:
try {
// выполнить какие-то действия
} catch(Exception $ex) {
// обработка ошибки
} finally {
// очистка ресурсов
}
Однако в PHP нет аналогичного механизма. Это создает определенные трудности для разработчиков, которым необходимо обеспечить корректное завершение выполнения кода и освобождение ресурсов.
Есть ли у кого-нибудь опыт решения этой довольно раздражающей проблемы в языке? Любые советы и обходные пути будут полезны!
5 ответ(ов)
Решение, нет. Раздражающая, громоздкая замена – да:
$stored_exc = null;
try {
// Выполняем код
} catch (Exception $exc) {
$stored_exc = $exc;
// Обработка ошибки
}
// "Наконец" здесь, убираем за собой
if ($stored_exc) {
throw($stored_exc);
}
Не идеально, но должно сработать.
Обратите внимание: В PHP 5.5 наконец-то (извините за каламбур) был добавлен блок finally: https://wiki.php.net/rfc/finally (и на это понадобилось несколько лет... доступен в 5.5 RC почти через четыре года после того, как я опубликовал этот ответ...)
Вот моё решение проблемы отсутствия блока finally в PHP. Оно не только предоставляет обходной путь для реализации finally, но также расширяет try/catch, чтобы обрабатывать ошибки PHP (включая фатальные ошибки). Моё решение выглядит следующим образом (PHP 5.3):
_try(
// некоторый код, который будет нашим блоком try
function() {
// этот код ожидается как генерирующий исключение или ошибку PHP
},
// некоторый (опциональный) код, который будет нашим блоком catch
function($exception) {
// здесь будет поймано исключение
// ошибки PHP также будут обработаны здесь как ErrorException
},
// некоторый (опциональный) код, который будет нашим блоком finally
function() {
// этот код выполнится после блока catch и даже после фатальных ошибок
}
);
Вы можете загрузить решение с документацией и примерами с GitHub - https://github.com/Perennials/travelsdk-core-php/tree/master/src/sys
Поскольку это конструкция языка, вы не найдете простого решения этой проблемы. Вы можете написать функцию и вызывать её в последней строке блока try
, а также в последней строке перед повторным выбрасыванием исключения в секции try
.
Хорошие книги утверждают, что использование блоков finally
следует ограничивать только освобождением ресурсов, так как вы не можете быть уверены, что они выполнятся, если произойдёт нечто неприятное. Называть это "раздражающей дырой" — это довольно преувеличенно. Поверьте, много исключительного кода написано на языках без блока finally
. 😃
Суть блока finally
заключается в том, что он выполняется независимо от того, был ли блок try
успешным или нет.
Вы можете использовать следующую функцию _try
для обработки исключений в PHP. Эта функция принимает три параметра: $try
, $catch
и $finally
. Использование замыканий (closures) позволяет вам передавать любые анонимные функции для выполнения в этих параметрах. $catch
является необязательным.
Вот сама функция:
function _try(callable $try, callable $catch, callable $finally = null)
{
if (is_null($finally))
{
$finally = $catch;
$catch = null;
}
try
{
$return = $try();
}
catch (Exception $rethrow)
{
if (isset($catch))
{
try
{
$catch($rethrow);
$rethrow = null;
}
catch (Exception $rethrow) { }
}
}
$finally();
if (isset($rethrow))
{
throw $rethrow;
}
return $return;
}
Примеры использования:
_try(function ()
{
// Код, который нужно выполнить (try)
}, function ($ex)
{
// Код обработки исключения (catch)
}, function ()
{
// Код, который выполнится в любом случае (finally)
});
_try(function ()
{
// Код, который нужно выполнить (try)
}, function ()
{
// Код, который выполнится в любом случае (finally)
});
Данная функция правильно обрабатывает исключения различных типов:
- Когда происходит исключение в
$try
: оно будет передано в$catch
. Сначала выполнится$catch
, затем$finally
. Если$catch
не указан, исключение будет снова брошено после выполнения$finally
. - Если исключение происходит в
$catch
:$finally
выполнится немедленно, а исключение будет снова брошено после завершения работы$finally
. - Если исключение происходит в
$finally
: это приведет к прерыванию стека вызовов, и любые другие исключения, запланированные на повторное выбрасывание, будут проигнорированы. - Если исключений не было: функция вернет значение, полученное из
$try
.
Таким образом, вы можете аккуратно обрабатывать исключения в вашем коде, минимизируя дублирование и сохраняя читабельность.
Я только что закончил разработку более элегантного класса Try Catch Finally, который может быть полезен вам. У него есть некоторые недостатки, но с ними можно справиться. Вы можете ознакомиться с кодом по ссылке: gist.
Зачем нужен код исключений PHP? Примеры использования?
PHP: Как проверить тип выброшенного исключения?
Функции startsWith() и endsWith() в PHP
Как получить расширение файла в PHP?
Как читать большой файл построчно?