register_shutdown_function

(PHP 4, PHP 5, PHP 7, PHP 8)

register_shutdown_function注册在关闭时执行的函数

描述

register_shutdown_function(callable $callback, mixed ...$args): void

注册一个 callback,在脚本执行完成或调用 exit() 后执行。

可以多次调用 register_shutdown_function(),每次调用都将按照注册的顺序执行。如果在已注册的关闭函数中调用 exit(),处理将完全停止,并且不会调用任何其他已注册的关闭函数。

关闭函数也可以调用 register_shutdown_function() 来将关闭函数添加到队列的末尾。

参数

callback

要注册的关闭回调。

关闭回调作为请求的一部分执行,因此可以从它们发送输出并访问输出缓冲区。

args

可以通过传递附加参数来将参数传递给关闭函数。

返回值

不返回值。

示例

示例 #1 register_shutdown_function() 示例

<?php
function shutdown()
{
// 这是我们的关闭函数,在这里我们可以执行任何最后的操作
// 在脚本完成之前。

echo '脚本执行成功', PHP_EOL;
}

register_shutdown_function('shutdown');
?>

注释

注意:

在某些 Web 服务器(例如 Apache)下,脚本的工作目录可能会在关闭函数内部发生变化。

注意:

如果进程使用 SIGTERM 或 SIGKILL 信号被杀死,则不会执行关闭函数。虽然无法拦截 SIGKILL,但可以使用 pcntl_signal() 为 SIGTERM 安装一个处理程序,该处理程序使用 exit() 来干净地结束。

注意:

关闭函数独立于 max_execution_time 跟踪的时间运行。这意味着即使进程因运行时间过长而终止,关闭函数仍将被调用。此外,如果 max_execution_time 在关闭函数运行时用完,它不会被终止。

参见

添加注释

用户贡献的注释 14 个注释

260
jules at sitepointAASASZZ dot com
21 年前
如果您的脚本超过最大执行时间并因此终止

致命错误:- 中第 12 行超过了 20 秒的最大执行时间

已注册的关闭函数仍将执行。

我认为明确这一点很重要!
82
http://livejournal.com/~sinde1/
18 年前
如果您想在注册到 register_shutdown_function() 的函数中使用文件,请使用文件的绝对路径而不是相对路径。因为当脚本处理完成后,当前工作目录会更改为 ServerRoot(参见 httpd.conf)
22
emanueledelgrande ad email dot it
13 年前
许多有用的服务可以委托给此有用的触发器。
它非常有效,因为它在脚本结束时但在任何对象销毁之前执行,因此所有实例化仍然存活。

这是一个简单的关闭事件管理器类,它允许管理函数或静态/动态方法,并使用不确定的参数数量,而无需使用任何反射,利用 func_get_args() 和 call_user_func_array() 的内部处理特定函数

<?php
// 管理关闭回调事件:
class shutdownScheduler {
private
$callbacks; // 存储用户回调的数组

public function __construct() {
$this->callbacks = array();
register_shutdown_function(array($this, 'callRegisteredShutdown'));
}
public function
registerShutdownEvent() {
$callback = func_get_args();

if (empty(
$callback)) {
trigger_error('没有将回调传递给 '.__FUNCTION__.' 方法', E_USER_ERROR);
return
false;
}
if (!
is_callable($callback[0])) {
trigger_error('将无效回调传递给 '.__FUNCTION__.' 方法', E_USER_ERROR);
return
false;
}
$this->callbacks[] = $callback;
return
true;
}
public function
callRegisteredShutdown() {
foreach (
$this->callbacks as $arguments) {
$callback = array_shift($arguments);
call_user_func_array($callback, $arguments);
}
}
// 测试方法:
public function dynamicTest() {
echo
'_REQUEST 数组有 '.count($_REQUEST).' 个元素。<br />';
}
public static function
staticTest() {
echo
'_SERVER 数组有 '.count($_SERVER).' 个元素。<br />';
}
}
?>

一个简单的应用程序

<?php
// 一个通用函数
function say($a = '一个通用的问候', $b = '') {
echo
"说 {$a} {$b}<br />";
}

$scheduler = new shutdownScheduler();

// 安排一个全局范围的函数:
$scheduler->registerShutdownEvent('say', '你好!');

// 尝试安排一个动态方法:
$scheduler->registerShutdownEvent(array($scheduler, 'dynamicTest'));
// 使用静态调用尝试:
$scheduler->registerShutdownEvent('scheduler::staticTest');

?>

很容易猜测如何在更复杂的上下文中扩展此示例,其中应根据特定变量的优先级来处理用户定义的函数和方法。

希望它能帮到一些人。
祝您编程愉快!
14
pgl at yoyo dot org
15 年前
在关闭函数被调用后,您绝对需要小心使用相对路径,但当前的工作目录不会(一定)更改为 Web 服务器的 ServerRoot - 我已经在两台不同的服务器上测试过,它们都将自己的 CWD 更改为 '/'(这不是 ServerRoot)。

这演示了行为

<?php
function echocwd() { echo 'cwd: ', getcwd(), "\n"; }

register_shutdown_function('echocwd');
echocwd() and exit;
?>

输出

cwd: /path/to/my/site/docroot/test
cwd: /

注意:CLI 脚本不受影响,并将其 CWD 保持为脚本调用的目录。
25
ravenswd at gmail dot com
14 年前
您可能会想到从关闭函数内部调用 debug_backtrace 或 debug_print_backtrace,以跟踪致命错误发生的位置。不幸的是,这些函数在关闭函数内部将无法工作。
8
RLK
15 年前
与“标题始终发送”的说明和下面的一些评论相反 - 您可以在关闭函数中像在其他任何地方一样使用 header();当 headers_sent() 为 false 时。您可以通过这种方式进行自定义致命处理

<?php
ini_set
('display_errors',0);
register_shutdown_function('shutdown');

$obj = new stdClass();
$obj->method();

function
shutdown()
{
if(!
is_null($e = error_get_last()))
{
header('content-type: text/plain');
print
"this is not html:\n\n". print_r($e,true);
}
}
?>
3
jawsper at aximax dot nl
14 年前
在测试过程中发现了一些东西

ini auto_append_file 将在注册的函数(s) 被调用之前被包含。
9
alexyam at live dot com
12 年前
当使用 php-fpm 时,应该使用 fastcgi_finish_request() 而不是 register_shutdown_function() 和 exit()

例如,在 nginx 和 php-fpm 5.3+ 下,这将使浏览器等待 10 秒才能显示输出

<?php
echo "You have to wait 10 seconds to see this.<br>";
register_shutdown_function('shutdown');
exit;
function
shutdown(){
sleep(10);
echo
"Because exit() doesn't terminate php-fpm calls immediately.<br>";
}
?>

这不是

<?php
echo "You can see this from the browser immediately.<br>";
fastcgi_finish_request();
sleep(10);
echo
"You can't see this form the browser.";
?>
4
dweingart at pobox dot com
18 年前
我发现 PHP 5.0.4 到 PHP 5.1.2 在将关闭函数与输出缓冲回调结合使用时,行为发生变化。

在 PHP 5.0.4(以及我相信的早期版本)中,关闭函数在输出缓冲回调之后被调用。

在 PHP 5.1.2(不确定更改何时发生)中,关闭函数在输出缓冲回调之前被调用。

测试代码
<?php
function ob_callback($buf) {
$buf .= '<li>' . __FUNCTION__ .'</li>';
return
$buf;
}

function
shutdown_func() {
echo
'<li>' . __FUNCTION__ .'</li>';
}

ob_start('ob_callback');
register_shutdown_function('shutdown_func');
echo
'<ol>';
?>

PHP 5.0.4

1. ob_callback
2. shutdown_func

PHP 5.1.2

1. shutdown_func
2. ob_callback
1
Anonymous
5 年前
警告:除了 SIGTERM 和 SIGKILL 之外,关闭函数也不会响应 SIGINT 运行。(在 Windows 7 SP1 x64 + cygwin 上的 php 7.1.16 和 Ubuntu 18.04 上的 php 7.2.15 上观察到)
1
kenneth dot kalmer at gmail dot com
18 年前
我在 register_shutdown_function() 上进行了两个测试,以查看它在什么情况下被调用,以及是否可以从类中调用静态方法。以下是结果

<?php
/**
* Tests the shutdown function being able to call a static methods
*/
class Shutdown
{
public static function
Method ($mixed = 0)
{
// we need absolute
$ap = dirname (__FILE__);
$mixed = time () . " - $mixed\n";
file_put_contents ("$ap/shutdown.log", $mixed, FILE_APPEND);
}
}
// 3. Throw an exception
register_shutdown_function (array ('Shutdown', 'Method'), 'throw');
throw new
Exception ('bla bla');

// 2. Use the exit command
//register_shutdown_function (array ('Shutdown', 'Method'), 'exit');
//exit ('exiting here...')

// 1. Exit normally
//register_shutdown_function (array ('Shutdown', 'Method'));
?>

要测试,只需将三行测试代码中的一行取消注释并执行。自下而上执行产生以下结果

1138382480 - 0
1138382503 - exit
1138382564 - throw

希望对您有所帮助
1
sts at mail dot xubion dot hu
20 年前
如果您需要 register_shutdown_function 的旧(<4.1)行为,您可以使用“Connection: close”和“Content-Length: xxxx”标题来实现相同的目的,前提是您知道发送数据的确切大小(这很容易用输出缓冲来捕获)。
一个例子
<?php
header
("Connection: close");
ob_start();
phpinfo();
$size=ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
sleep(13);
error_log("do something in the background");
?>

同样适用于注册的函数。
根据 http 规范,当浏览器接收到 Content-Length 标题中指定的数据量时,浏览器应该关闭连接。至少它在我的 IE6 和 Opera7 中运行良好。
1
josh at joshstrange dot com
9 个月前
> 关闭函数独立于 max_execution_time 跟踪的时间运行。这意味着即使进程因运行时间过长而终止,关闭函数仍将被调用。此外,如果 max_execution_time 在关闭函数运行时耗尽,它将不会被终止。

这在我们测试中似乎并不正确,特别是“此外,如果 max_execution_time 在关闭函数运行时耗尽,它将不会被终止”。例如

<?php
set_time_limit
(5);

register_shutdown_function(function() {
$start = time();
for(
$i = 0; $i < 40; $i++) {
echo
"Run 1: $i\n";
while(
1) {
$elapsed = time() - $start;
if(
$elapsed > $i) {
break;
}
}
}
});

?>

这将打印出

Run 1: 0
Run 1: 1
Run 1: 2
Run 1: 3
Run 1: 4
Run 1: 5

然后它将以以下错误结束

执行时间超过 5 秒,已发送 SIGTERM 并终止

如果您注册了多个关闭函数,而较早的函数超过了执行时间,则较晚的函数将_不会_运行。

如果您需要您的关闭函数拥有无限时间(就像文档中所述那样工作),一种解决方案可能是在您的代码中尽早注册一个像这样的关闭函数

<?php
register_shutdown_function
(function() {
set_time_limit(0);
});
?>

这样您就可以在随后的关闭函数中给自己无限时间。

请参阅此处的示例:https://www.tehplayground.com/wJLtMi3Z5c1sTi9Y(注意,5 秒是您可以在此网站上设置的最大值。如果您删除第一个关闭函数,它将在 3 秒后被杀死)。
2
raat1979 at gmail dot com
7 年前
我想通过使用类属性来设置 CLI 应用程序的退出代码。

不幸的是(根据手册)“如果您在一个注册的关闭函数中调用 exit(),处理将完全停止,并且不会调用任何其他注册的关闭函数。”

因此,如果我在关闭函数中调用 exit,我会破坏其他关闭函数(例如,将致命错误记录到 syslog 的函数)

但是! (根据手册)“可以多次调用 register_shutdown_function(),并且每次调用都将按照注册顺序执行。”

幸运的是,你也可以在关闭函数中注册关闭函数(至少在 PHP 7.0.15 和 5.6.30 中)。

换句话说,如果你在关闭函数中注册一个关闭函数,它将被追加到关闭函数队列中。

<?php
class SomeApplication{
private
$exitCode = null;

public function
__construct(){
register_shutdown_function(function(){
echo
"some registered shutdown function";
register_shutdown_function(function(){
echo
"last registered shutdown function";
// 如果预计其他关闭函数也会执行相同的操作,可以考虑在另一个关闭函数中注册关闭函数...
exit($this->exitCode === null ? 0 : $this->exitCode);
});
});
}
}
?>
To Top