PHP: деструктор против функции register_shutdown_function - PullRequest
12 голосов
/ 25 октября 2008

У меня есть класс PHP, который создает изображение PNG на лету и отправляет его в браузер. В руководстве по PHP написано, что мне нужно убедиться, что в конце вызывается функция imagedestroy , чтобы освободить память. Теперь, если бы я не использовал класс, у меня был бы такой код:

function shutdown_func() 
{
    global $img;
    if ($img)
        imagedestroy($img);
}
register_shutdown_function("shutdown_func");

Однако я считаю, что подходящим местом для моего класса было бы сделать вызов imagedestroy в деструкторе класса.

Мне не удалось выяснить, вызывается ли деструктор так же, как функции выключения? Например, если выполнение останавливается, когда пользователь нажимает кнопку СТОП в браузере.

Примечание: что бы вы ни написали в своем ответе, укажите на страницу статьи или руководства (URL), которая его поддерживает.

Ответы [ 4 ]

13 голосов
/ 25 октября 2008

Я только что протестировал с Apache, PHP используется как модуль Apache. Я создал бесконечный цикл, как это:

<?php
class X
{
    function __destruct()
    {
        $fp = fopen("/var/www/htdocs/dtor.txt", "w+");
        fputs($fp, "Destroyed\n");
        fclose($fp);
    }
};

$obj = new X();
while (true) {
    // do nothing
}
?>

Вот что я узнал:

  • нажатие кнопки STOP в Firefox не останавливает этот скрипт
  • Если я выключу Apache, деструктор не будет вызван
  • Останавливается, когда достигает PHP max_execution_time, а destuctor не вызывается

Однако, делая это:

<?php
function shutdown_func() {
    $fp = fopen("/var/www/htdocs/dtor.txt", "w+");
    fputs($fp, "Destroyed2\n");
    fclose($fp);
}
register_shutdown_function("shutdown_func");

while (true) {
    // do nothing
}
?>

shutdown_func вызывается. Это значит, что деструктор класса не так хорош, как функции выключения.

2 голосов
/ 26 октября 2008

Исходя из принципа, что вы должны завершить начатое , я бы сказал, что деструктор - правильное место для бесплатного звонка.

Деструктор будет вызываться при удалении объекта, тогда как функция отключения не будет вызываться до тех пор, пока не завершится выполнение скрипта. Как отметил Вольф, это не обязательно произойдет, если вы принудительно остановите сервер или скрипт, но в это время память, выделенная PHP, все равно будет освобождена.

Также отметил Вольф, PHP освободит ресурсы скрипта, когда скрипт закроется, поэтому, если вы создаете экземпляр только одного из этих объектов, то вы, вероятно, не заметите существенной разницы. Тем не менее, если позже вы в конечном итоге создадите эти экземпляры или сделаете это в цикле, то вам, вероятно, не нужно беспокоиться о внезапном скачке использования памяти, поэтому ради будущего здравого смысла я вернусь к оригинальная рекомендация; положить его в деструктор.

1 голос
/ 29 июля 2015

У меня недавно были проблемы с этим, поскольку я пытался обработать уничтожение специально для случая, когда сервер испытывает таймаут, и я хотел включить данные класса в журнал ошибок. Я получаю сообщение об ошибке при ссылке & $ this (хотя я видел это в нескольких примерах, возможно, проблема с версией или побочный эффект Symfony), и решение, которое я нашел, было довольно чистым:

class MyClass
{
    protected $myVar;

    /**
     * constructor, registers shutdown handling
     */
    public function __construct()
    {
        $this->myVar = array();

        // workaround: set $self because $this fails
        $self = $this;
        // register for error logging in case of timeout
        $shutdown = function () use (&$self) {
            $self->shutdown();
        };
        register_shutdown_function($shutdown);
    }

    /**
     * handle shutdown events
     */
    public function shutdown()
    {
        $error = error_get_last();
        // if shutdown in error
        if ($error['type'] === E_ERROR) {
            // write contents to error log
            error_log('MyClass->myVar on shutdown' . json_encode($this->myVar), 0);
        }
    }

    ...

Надеюсь, это кому-нибудь поможет!

0 голосов
/ 25 октября 2008

Я думаю, что одна большая вещь, которую вы упустили, это то, что вся память, выделенная PHP во время выполнения скрипта, освобождается после его завершения. Даже если пользователь нажимает кнопку остановки, PHP обрабатывает сценарий до его завершения, возвращает его демону HTTP для обслуживания посетителю (или нет, в зависимости от того, насколько умным является демон).

Итак, явное освобождение памяти в конце выполнения скрипта немного избыточно. Некоторые могут возразить, что это было бы хорошо, но это все еще излишне.

Но, что касается деструкторов классов, они вызываются всякий раз, когда объект уничтожается, либо явно с помощью unset(), либо при завершении / завершении скрипта.

Рекомендация разработчика о явном освобождении памяти, используемой при манипулировании изображениями, просто гарантирует, что утечка памяти будет абсолютно невозможна, поскольку растровые изображения могут напрягать стороны памяти (высота * ширина * разрядность * 3 (+ 1, если у вас есть альфа-канал))

Чтобы удовлетворить ваши потребности в Википедии:

...