阿里云虚拟主机禁用 sleep 函数?fatal error 报错解决方法

我的好几个 WordPress 插件都因为这个函数的缺失导致 fatal error。

Fatal error: Uncaught Error: Call to undefined function sleep() in /usr/home/htdocs/wp-content/plugins/wp-super-cache/partials/easy.php:79 Stack trace: #0 /usr/home/htdocs/wp-content/plugins/wp-super-cache/wp-cache.php(4423): include() #1 /usr/home/htdocs/wp-content/plugins/wp-super-cache/wp-cache.php(1311): wpsc_render_partial('partials/easy.p...', Array) #2 /usr/home/htdocs/wp-includes/class-wp-hook.php(341): wp_cache_manager('') #3 /usr/home/htdocs/wp-includes/class-wp-hook.php(365): WP_Hook->apply_filters('', Array) #4 /usr/home/htdocs/wp-includes/plugin.php(522): WP_Hook->do_action(Array) #5 /usr/home/htdocs/wp-admin/admin.php(264): do_action('settings_page_w...') #6 /usr/home/htdocs/wp-admin/options-general.php(10): require_once('/usr/home/wh-aa...') #7 {main} thrown in /usr/home/htdocs/wp-content/plugins/wp-super-cache/partials/easy.php on line 79Code language: Access log (accesslog)

看了 phpinfo() 发现 sleep 函数被禁用了,我没想到这个无害的函数会被禁用,估计插件作者也不会想到这个函数会被禁用吧。

phpinfo() 显示的 disable_functions

从图中可以看出,阿里云虚拟主机禁用的 PHP 函数如下:

exec,system,passthru,shell_exec,escapeshellarg,escapeshellcmd,proc_close,proc_open,ini_alter,dl,popen,pcntl_exec,socket_accept,socket_bind,socket_clear_error,socket_close,socket_connect,socket_create_listen,socket_create_pair,socket_create,socket_get_option,socket_getpeername,socket_getsockname,socket_last_error,socket_listen,socket_read,socket_recv,socket_recvfrom,socket_select,socket_send,socket_sendto,socket_set_block,socket_set_nonblock,socket_set_option,socket_shutdown,socket_strerror,socket_write,stream_socket_client,stream_socket_server,pfsockopen,disk_total_space,disk_free_space,chown,diskfreespace,getrusage,get_current_user,getmyuid,getmypid,dl,leak,listen,chgrp,link,symlink,dlopen,proc_nice,proc_get_stats,proc_terminate,shell_exec,sh2_exec,posix_getpwuid,posix_getgrgid,posix_kill,ini_restore,mkfifo,dbmopen,dbase_open,filepro,filepro_rowcount,posix_mkfifo,putenv,sleepCode language: plaintext (plaintext)

这里大部分函数被禁用是出于安全考虑,比如文件操作、系统命令执行、进程控制、网络操作等函数被禁用,这是是可以理解的,但是 sleep 函数也被禁用实在让人想不通。

我首先想到的应对措施是能不能用 .user.ini 文件重新启用 sleep 函数。但是根据 PHP 官方文档的说明,disable_functions 只能在全局 php.ini 文件中设置,.user.ini 文件中无效。

不过文档也说:As of PHP 8.0.0, disabling a function removes its definition, allowing userland to redefine it.

也就是说在 PHP 8.0 及以上版本中,禁用的函数可以在用户代码中重新定义。所以我自己模拟了一个 sleep 函数,代码如下:

<?php
if (!function_exists('sleep')) {
    function sleep($seconds) {
        $start = time();
        while (time() - $start < $seconds);
        return 0;
    }
}Code language: PHP (php)

将这段代码保存为 sleep.php,然后在 .user.ini 文件中添加 auto_prepend_file 配置指向这个 sleep.php 文件路径,比如:

auto_prepend_file = "/usr/home/sleep.php"Code language: plaintext (plaintext)

这样每次PHP脚本运行时,都会先包含这个 sleep.php 文件,从而定义 sleep 函数,避免 fatal error。

阿里云这种做法真是让人摸不着头脑,按理说禁用函数是为了安全性,把不安全的函数禁用掉是可以理解的,但一个 sleep 函数能造成什么安全隐患呢?这是最安全的函数之一了吧,毕竟它的功能就是等待一段时间,什么都不执行。

我猜测,阿里云这么做是出于性能/经济考虑,禁用这个函数试图减少虚拟主机用户占用的资源,但这种做法对谁都没好处。对用户来说,用户如果要用到这个函数,只能自己模拟,不然就等着程序 fatal error。对阿里云来说,模拟函数的开销肯定比原生函数的开销大,本来想着“降本增效”,最后反倒可能浪费更多资源。

参考资料:

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注