Эта тема на forum.dklab.ru


MVH: Выполнение действия перед завершением работы скрипта
Скажите, есть ли способ выполнить некоторое действие перед прекращением работы скрипта. Например, необходимо сделать одну запись в MySQL, если время работы скрипта превышено или пользователь закрыл браузер (как я понимаю в этом случае php скрипт прекращает работу).
Константин Жинько [tIT]:
http://ru2.php.net/manual/en/function.register-shutdown-function.php
http://ru2.php.net/manual/en/features.connection-handling.php
Юрий Насретдинов:
MVH
Только учтите - ничего выводить в браузер Вы не сможете.
MVH:
Один вопрос:
с английским у меня плоховато, поэтому хочу уточнить. Как я понял, функция, зарегистрированная через register_shutdown_function(), будет вызвана и в том случае, если время работы скрипта (TIMEOUT) вышло.
Юрий Насретдинов:
MVH
Не только, просто после завершения работы скрипта, неважно, что было причиной (кроме конечно таких случаев, как аварийное завершение процесса (попросту kill apache ;)))
MVH:
Просто интересна такая ситуация.
Время работы скрипта вышло и вызывается shutdown функция. У неё есть какое-нибудь ограничение по времени работы? А то получится, что можно time limit скрипта, установленный в php.ini можно будет обойти...
Юрий Насретдинов:
MVH
Попробуйте, заодно и нам расскажете.

P.S. Обычно на хостингах еще ставят ограничения на процессорное время, затрачиваемое скриптами, средствами операционной системы
chin:
А можно ли определить, что пользователь именно закрыл браузер? Т.е. ушел с сайта... ?
Дмитрий Котеров:
Нельзя. Это уже миллион раз обсуждалось.
Anonymous:
Есть еще php.ini->auto_append_file
Константин Жинько [tIT]:
Есть еще php.ini->auto_append_file
Это совсем другое -- она просто тупо подставляет в конец каждого файла указанный и исполняет... Соответственно, если программа прерывается, то до append_file никогда очередь не дойдет
MVH:
Попробуйте, заодно и нам расскажете.
Попробовал. Вывод: время работы функции, зарегистрированной через register_shutdown_function(), не может превышать max_execution_time, установленный в php.ini (или ещё где). Т.е. выполняется скрипт, завершилось время выполнения (30 сек., например), запускается shutdown функция - и она может выполняться максимум 30 сек.
Если, кому интересно, то тестил так:
Выполнил этот скрипт:
<?php
set_time_limit(1);
function Shutdown()
{
$i = 0;
while ($i < 1)
{
$i++;
}
}
register_shutdown_function('Shutdown');
while (true)
{
}
?>
Выдалось сообщение:
Fatal error: Maximum execution time of 1 second exceeded in h:\home\url_checker\www\1.php on line 12

затем выполнил этот скрипт:
<?php
set_time_limit(1);
function Shutdown()
{
$i = 0;
while ($i < 100000000000000000000000000000)
{
$i++;
}
}
register_shutdown_function('Shutdown');
while (true)
{
}
?>
Выдалось 2 сообщения:
Fatal error: Maximum execution time of 1 second exceeded in h:\home\url_checker\www\1.php on line 14

Fatal error: Maximum execution time of 1 second exceeded in h:\home\url_checker\www\1.php on line 6
И из второго сообщения видно, что выполнение цикла while в ф-и Shutdown() превысило время выполнения скрипта.
Graymur:
Есть еще php.ini->auto_append_file
Это совсем другое -- она просто тупо подставляет в конец каждого файла указанный и исполняет... Соответственно, если программа прерывается, то до append_file никогда очередь не дойдет
Откуда информация?
Юрий Насретдинов:
Откуда информация
Ну проверьте, опять же
MVH:
Какой-то непонятный глюк при работе register_shutdown_function() и fsockopen()
При выполнении следующего кода выдаётся ошибка (иногда через раз):
<?php
set_time_limit(5);
function Shutdown()
{
touch('c:\\qwe.txt');
}
register_shutdown_function('Shutdown');
while (true)
{
fsockopen('dawjadlhi.ru', 80, $e, $en, 5);
}
?>

Warning: fsockopen(): php_network_getaddresses: gethostbyname failed in h:\home\url_checker\www\1.php on line 10

Warning: fsockopen(): unable to connect to dawjadlhi.ru:80 in h:\home\url_checker\www\1.php on line 10

Warning: fsockopen(): php_network_getaddresses: gethostbyname failed in h:\home\url_checker\www\1.php on line 10

Warning: fsockopen(): unable to connect to dawjadlhi.ru:80 in h:\home\url_checker\www\1.php on line 10

Warning: fsockopen(): php_network_getaddresses: gethostbyname failed in h:\home\url_checker\www\1.php on line 10

Warning: fsockopen(): unable to connect to dawjadlhi.ru:80 in h:\home\url_checker\www\1.php on line 10

Fatal error: Maximum execution time of 5 seconds exceeded in h:\home\url_checker\www\1.php on line 10

Fatal error: Maximum execution time of 5 seconds exceeded in h:\home\url_checker\www\1.php on line 3

Shutdown функция соответственно не выполняется. Такое ощущение, что выполнение fsockopen() забирает время выполнения у shutdown ф-и... В чём может быть дело???
MVH:
Проблема решена.
Сначала думал, что это баг php4. Постивал php5 (под XP), но баг остался... Потом попробовал этот скрипт на FreeBSD (на php4) и о чудо - всё прекравно работает.
Всё дело было в видне - это она глючиная.
Дмитрий Котеров:
Не винда глючная, а PHP под винду. Разные вещи.
К тому же Вы наоткрывали тучу сокетов, и не закрыли их - что ж Вы хотите-то... Закрывать надо!
MVH:
К тому же Вы наоткрывали тучу сокетов, и не закрыли их - что ж Вы хотите-то...
Даже если закрывать - всё равно глюк.
благ:
а будет ли работать это в такой ситуации:
во всех скриптах подключается класс db.php

<?
function shutdown()
{
global $db;
mysql_close($db->con);
}

register_shutdown_function('shutdown');

class db {
// тело класса
}

собсна закрытие соединения будет срабатывать в любом случае?
Юрий Насретдинов:
благ
Закрытие соединения будет срабатывать даже в том случае, если Вы не указали mysql_close() нигде
благ:
yUAC
ну да, только обращение к такому скрипту тысачу раз за секунду ставит хостера в позу по отношению ко мне, т.к. появляется куча зависших запросов и бд просто зависает по причине не закрытия соединений. Я даже и не догадывался, что существует подобного рода функция, пришлось просто вручную везде прописывать закрытие и, по словам хостера, все стабилизировалось. Поэтому и спрашиваю. А то, что соединение рано или поздно закрывается это и козе понятно, дело во времени и правильности действий, выполняемых скриптом и базой.
благ:
yUAC
вопрос в другом: правильно-ли я описал конструкцию в классе? все, что находится до объявления класса выполнится? Ведь по-сути че получается:
подключаю файл с классом, в нем парсер видит функцию shutdown(), видит, что она является функцией по-завешению скрипта, видит класс. В shutdown используется глобальная переменная db, которая и является наследованием этого класса, соответственно на момент парсинга этой функции пхп еще не знает класс db, т.к. этот файл просто пока инклудится основным скриптом, а уже следующей строкой он объявляется. Другими словами на момент инклуда функция shutdown не является рабочей, т.к. класса db не существует, хотя уже на следующем шаге в основном скрипте объявляется этот класс. Если я как-то сложно излагаю мысль (г-н Кóтеров почему-то таких сразу отсылает читать философский постулат на тему вечности, хотя далеко не всегда проблему можно однозначно обозначить. форумы для этого и существуют, ну да ладно, в чужой монастырь как говорится ...) вот рабочий пример:
есть index.php

include("./classes/db/class.php");
$db = new bd;
$db->connect();
// далее остальной код

а вот содержимое class.php

<?
function shutdown()
{
global $db;
mysql_close($db->con);
}

register_shutdown_function('shutdown');

class db {
// тело класса
}
?>

Navigator:
Если вывод странички буферизован, то можно вызывать функции до их объявления. Проверено. Но сам я так никогда не делаю ;)
Евгений Галашин:
Если вывод странички буферизован, то можно вызывать функции до их объявления. Проверено.
А теперь подумайте ещё раз над тем, что Вы сказали. А также о компиляции в байт-код и написании сложных программ, где две функции могут вызывать друг друга.

Подсказка: порядок объявления функций в скрипте не важен. И буферизация тут абсолютно ни при чём.

благ, на Вашем месте я бы сделал так:

<?

class db {

function shutdown()
{
mysql_close($this->con);
}

function db() {
// в конструктор добавляем:
register_shutdown_function(array(&$this, 'shutdown'));

}
// тело класса
}
?>

Так мы избавляемся от лишней глобальной функции, но и предложенный Вами вариант тоже будет работать. PHP "задумается" о типе переменной $db только тогда, когда будет выполнять строчку, где она упоминается.
И Вам я советую почитать ман по include/require, а также подумать над компиляцией и алгоритмом работы Zend Engine. (-;
благ:
Евгений Галашин
огромное спасибо, я с самого начала так и хотел, просто не знал как в register_shutdown_function отдать классовую функцию ($this->shutdown), поэтому пришлось извращаться :)
Иван Шумков:
Вот еще вариант. У господина Котерова вычитал.

# Устанавливаем обработчик выходного потока скрипта.
ob_start('ob_handler');
# Печатаем что-нибудь.
echo "Something";
# Вызываем неопределенную функцию внутри eval!
eval('undefinedFunc();');
# Печатаем еще что-то (к этому моменту скрипт уже мертв!).
echo "Other";
# Функция-обработчик просто добавляет некоторый "хвост"
# к тексту, выведенному скриптом ранее.
function ob_handler($text)
{
return "$text<hr>Hello from handler!";
}

Markus:
Всем добрый день.
Столкунулся с проблемой при использовании register_shutdown_function() под виндой.
Денвер PHP Version 4.4.4
class SITE extends MySQL{

function SITE() {
register_shutdown_function(array($this, '_shutdown'));
$this->_connect_mysql();
}

function _shutdown() {
$this->writeLog("ERROR:Shutdown");
}

}
При вызове метода _connect_mysql() и при неудачном подключении к базе, в лог пишется строка с ошибкой и вызывается die().
То есть должна сработать зарегестрированный в register_shutdown_function() метод класса SITE который вызывает метод записи в лог.
В итоге в лог должно писаться две строки. Но под виндой пишется только одна строка с ошибкой о подключении к базе.
На хостинге же пишется как и положено обе строки.
Получается что до метода _shutdown в винде очередь не доходит. Это глюк PHP под винду или как?

Эта тема на forum.dklab.ru