Для чего используется finally в PHP

Ответ

Блок finally в конструкции try-catch я использую для гарантированного выполнения cleanup-операций независимо от того, как завершился блок try или catch. Это критически важно для предотвращения утечек ресурсов.

Реальный пример работы с транзакциями БД:

$connection = null;
try {
    $connection = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $connection->beginTransaction();

    // Бизнес-логика
    $stmt = $connection->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
    $stmt->execute([100, 1]);

    $stmt = $connection->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
    $stmt->execute([100, 2]);

    $connection->commit();

} catch (PDOException $e) {
    // Логируем ошибку
    error_log("Transaction failed: " . $e->getMessage());

    if ($connection) {
        $connection->rollBack();
    }

    throw new ApplicationException("Transfer failed", 0, $e);

} finally {
    // Гарантированно закрываем соединение
    if ($connection) {
        $connection = null;
    }

    // Освобождаем другие ресурсы
    gc_collect_cycles();
}

Ключевое поведение:

  1. finally выполняется всегда — после успешного try, после catch, даже если в catch есть return или throw
  2. Порядок выполнения: try → (catch если есть исключение) → finally → (возврат/проброс если были в catch)
  3. Если в finally есть свой return, он переопределит возвращаемое значение из try/catch

Типичные use cases:

  • Закрытие файловых дескрипторов (fclose())
  • Освобождение сокетов и сетевых соединений
  • Снятие блокировок (локи файлов, мьютексы)
  • Сброс флагов и временных состояний

Ответ 18+ 🔞

А, ну вот, опять про этот ваш finally! Ёпта, слушай, это же как золотое правило, блядь: что бы ни случилось — убрать за собой надо. Представь, ты в гостях нажрался как свинья, тарелки по всей квартире, а потом просто свалил. Хозяева потом охуеют, да? Вот и здесь так же.

Смотри, в жизни бывает всякое: код выполнился удачно — красота. Или нихуя не выполнился, вылетело исключение — тоже, в общем-то, ок, мы его поймали. Но ресурсы-то, сука, остались! Соединение к базе висит, файл открыт, память не освобождена. Это ж пиздец просто, утечка будет, сервер рухнет. А finally — это как тот самый адекватный гость, который, даже если его тошнило в хозяйской раковине, всё равно придёт и отмоет всё до блеска. Он выполнится в любом случае. Всегда. Это железно.

Вот смотри на примере, тут всё понятно станет. Допустим, мы бабло переводим со счёта на счёт:

$connection = null;
try {
    $connection = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $connection->beginTransaction();

    // Снимаем сотку с первого лоха
    $stmt = $connection->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
    $stmt->execute([100, 1]);

    // И кидаем её второму
    $stmt = $connection->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
    $stmt->execute([100, 2]);

    $connection->commit(); // Всё чики-пуки, коммитим

} catch (PDOException $e) {
    // Ой, всё пошло по пизде! Логируем это безобразие
    error_log("Transaction failed: " . $e->getMessage());

    // И откатываем всё, что натворили, чтобы база не ебнулась
    if ($connection) {
        $connection->rollBack();
    }

    // И выбрасываем ошибку дальше, пусть начальство разбирается
    throw new ApplicationException("Transfer failed", 0, $e);

} finally {
    // А ВОТ ЭТА ЧАСТЬ, БЛЯДЬ, ВЫПОЛНИТСЯ ВСЕГДА!
    // Неважно, улетел commit или нас в catch выкинуло.
    // Соединение надо прибить. Гарантированно.
    if ($connection) {
        $connection = null; // Всё, нахуй, закрыли.
    }

    // И заодно мусор собрать, чисто для души
    gc_collect_cycles();
}

Понимаешь, в чём прикол? Порядок-то какой:

  1. Пытаемся сделать дело в try. Получилось — молодца. Не получилось — летим в catch.
  2. В catch мы можем орать, логировать, даже новую ошибку выкинуть — это всё лирика.
  3. А потом, внезапно, наступает finally. И он, этот ублюдок, выполняется при любом раскладе. Даже если в catch у тебя стоит return или throw — он сначала зайдёт в finally, сделает свои грязные делишки, а уж потом вернёт или выбросит твоё значение. Хитрая жопа, да?

Хуй с ним, с порядком. Главное — где это применять? Да везде, где есть что закрыть, отключить или почистить!

  • Файлы открыл — fclose() в finally.
  • С сокетом поигрался — убивай его там.
  • Временную блокировку на файл повесил (flock()) — обязательно сними в finally, а то потом весь скрипт встанет колом, и будешь искать, кто же эту хуйню не отпустил. Доверия к таким кодам — ноль ебать.

Одно только предупреждение, ёпта: если ты в самом finally напишешь return, то он перебьёт всё. И то, что вернул try, и то, что собрался вернуть catch. Так что не выёбывайся, используй его только для уборки, а не для бизнес-логики.

Короче, finally — это твой страховочный крюк, гарант порядка. Без него код — распиздяй и смерд пердящий, который после себя говно не убирает.