Vulpo One

Про утечку ресурсов в генераторах PHP

Очень интересный и полезный момент сегодня всплыл в комментариях на Хабре. В PHP 5.5, как известно, сделали поддержку функций-генераторов, по типу питоновских. Там раньше были итераторы, но с адовым синтаксисом (как всё в SPL), а теперь ввели оператор ‘yield’ и всё волшебным образом упростилось.

Например, можно написать такой генератор, читающий построчно файл:

<?php
function getLines($file) {
    f = fopen($file, 'r');
    while ($line = fgets($f)) {
        yield $line;
    }
    fclose($f);
}

yield’ означает «вернуть значение и продолжить с этого места при следующем вызове функции». Имея такой генератор, можно сделать вот такую печать файла:

<?php
foreach (getLines("file.txt") as $line) {
      echo $line;
}

Удобно? Очень удобно. Оператор ‘yield’ выдаст все строки файла, а потом, когда файл закончится, произойдёт обычный ‘return’ из функции, который закроет генератор (и закончит цикл).

Но как известно, если всё идёт хорошо, значит, вы чего-то не заметили. Немного изменим наш цикл:

<?php
foreach (getLines("file.txt") as $n => $line) {
    if ($n > 5) break;
    echo $line;
}

Предположим, нас интересуют только первые шесть строк файла, а дальше мы хотим прервать цикл оператором ‘break’. Имеем на то полное право. Но что в этом случае произойдёт внутри генератора? А ничего. Он останется стоять на последнем исполненном yield-е и никогда не дойдёт до строки ‘fclose($f)’. И наш файл останется незакрытым.

Мы получили утечку ресурса (открытого файла). Понятно, что внутри генератора могут быть открыты любые ресурсы и объекты, и их необходимо правильно и предсказуемо закрывать. Но как это сделать, если юзер может в любой момент сделать break? Обычная документация (http://www.php.net/manual/en/language.generators.overview.php) никаких намёков не даёт.

Так вот, оказывается (и за это спасибо юзеру weirdan с Хабра: https://habr.com/ru/post/189796/#comment_6594776), что читать в этом случае надо не документацию, а RFC по генераторам: https://wiki.php.net/rfc/generators#closing_a_generator. А в нём сказано, что при освобождении ссылки на генератор, у него внутри обязаны выполниться все блоки ‘finally’. И тогда мы получаем очень простой, красивый и безопасный код:

<?php
function getLines($file) {
    f = fopen($file, 'r');
    try {
        while ($line = fgets($f)) {
            yield $line;
        }
    } finally {
        fclose($f);
    }
}

В этом случае блок ‘finally’ выполнится и при нормальном выходе из цикла по генератору и при выходе по break-у. Ура.

Так что если вы пишете на PHP — имейте это в виду. Потому что, кажется, больше нигде про это узнать невозможно — ни в одной из читанных мною статей по генераторам не было ни слова об утечке ресурсов.

ru-php.livejournal.com/1566325.html

Comments


Caprica Abides

Текст (возможны неточности, за основу взят вариант из комментов к 1 видео):

Caprica let us celebrate,
Raise our hands despite the weight
And all her joys we extol,
Caprica abides!

Caprica let us persevere,
Blind ourselves to doubt and fear
(From the BSG Wiki: Shield ourselves from doubt and fear)
And to the burdens of the soul
(From the BSG Wiki: And all the sorrows of the soul)
Caprica abides!

Caprica, the humble beast,
Nurtures more than she,
Who gave the twins their noble feast
And saved them from the sea

Caprica let us tolerate,
Differences may separate,
Yet form the bonds that make us whole,
So say we all,
So say we all,
Caprica abides!

Comments









Libravatar

Как бы ответ на мои молитвы и проклятия появился сервис Libravatar, альтернатива так подставившему меня сервису Gravatar.

На самый первый взгляд, те же яйца, только в профиль — если просто заменить www.gravatar.com на cdn.libravatar.org, взлетит. Это уже хорошо, т.к. Gravatar без гадких Automattic мне было бы достаточно, но нет, всё круче — можно поднять свой сервис и сделать его публично доступным! (Подходит, правда, не для конечных пользователей, а для владельцев доменов, т.к. дискавер идет через SRV-записи DNS) + Приятный бонус в аватарах не только по email, но и по URL (чтобы работало на libravatar.org они должны быть проверены через OpenID, на своем — не обязательно)

Казалось бы у нас теперь 3 несовместимых системы — Libravatar, Pavatar, Gravatar, но и тут есть бонус — Libravatar, если не найдет картинку у себя, покажет Gravatar, так что если вспомнить, что Pavatar популярности не сыскал, можно обойтись этой новой штукой.

Ну и, естественно, я уже накатал чекер — https://sunhome.snch.info/static/libravatars.html

Comments