PHP时区数据库是损坏的错误

Tim*_*bru 11 php wordpress ubuntu timezone

我有一个wordpress网站,今天突然停止工作.当我查看日志时,我看到并出现错误:

[错误] [客户端50.78.108.177] PHP致命错误:strtotime():时区数据库已损坏 - 这绝不会发生!

在谷歌上阅读后,有人说他们在/ usr/share/zoneinfo中发现了权限问题.我尝试将权限更改为777,775,770,我仍然继续得到相同的错误.我在Ubuntu 10.04.3 LTS上运行php PHP 5.3.2.任何建议或建议都会有所帮助.如果其他所有方法都失败了,我将尝试降级到早期版本的php,但我想在尝试之前尝试其他方法.

谢谢,蒂姆尼特

更新
以防万一有帮助:错误指向strtotime下面的函数

function mysql2date( $dateformatstring, $mysqlstring, $translate = true ) {
    $m = $mysqlstring;
    if ( empty( $m ) )
            return false;

    if ( 'G' == $dateformatstring )
            return strtotime( $m . ' +0000' );

    $i = strtotime( $m );

    if ( 'U' == $dateformatstring )
            return $i;

    if ( $translate )
            return date_i18n( $dateformatstring, $i );
    else
            return date( $dateformatstring, $i );
}
Run Code Online (Sandbox Code Playgroud)

更新#2:
现在我通过简单地使用上述功能return false;而不执行任何操作来解决问题.但是,我仍然没有找出问题的根本原因.

更新3:

var_dump($dateformatstring)
Run Code Online (Sandbox Code Playgroud)

string(5)"dmy"string(1)"m"string(5)"dmy"string(1)"m"string(5)"dmy"string(1)"m"

var_dump($mysqlstring)
Run Code Online (Sandbox Code Playgroud)

string(19)"2011-10-20 05:35:01"string(19)"2011-10-20 05:35:01"string(19)"2011-10-20 05:25:22"string( 19)"2011-10-20 05:25:22"string(19)"2011-10-19 05:10:06"string(19)"2011-10-19 05:10:06"

更新#4:
还有另一个代码片段,它生成下面的错误日志:

PHP致命错误:date():时区数据库已损坏 - 这 绝不会发生!在第346行的 /srv/www/motionthink.com/public_html/wp-admin/includes/class-wp-filesystem-direct.php中,参考文献:wp_root_directory/wp-admin/plugins.php?plugin_status = upgrade

309         function dirlist($path, $include_hidden = true, $recursive = false) {
  310                 if ( $this->is_file($path) ) {
  311                         $limit_file = basename($path);
  312                         $path = dirname($path);
  313                 } else {
  314                         $limit_file = false;
  315                 }
  316 
  317                 if ( ! $this->is_dir($path) )
  318                         return false;
  319 
  320                 $dir = @dir($path);
  321                 if ( ! $dir )
  322                         return false;
  323 
  324                 $ret = array();
  325 
  326                 while (false !== ($entry = $dir->read()) ) {
  327                         $struc = array();
  328                         $struc['name'] = $entry;
  329 
  330                         if ( '.' == $struc['name'] || '..' == $struc['name'] )
  331                                 continue;
  332 
  333                         if ( ! $include_hidden && '.' == $struc['name'][0] )
  334                                 continue;
  335 
  336                         if ( $limit_file && $struc['name'] != $limit_file)
  337                                 continue;
  338 
  339                         $struc['perms']         = $this->gethchmod($path.'/'.$entry);
  340                         $struc['permsn']  = $this->getnumchmodfromh($struc['perms']);
  341                         $struc['number']        = false;
  342                         $struc['owner']         = $this->owner($path.'/'.$entry);
  343                         $struc['group']         = $this->group($path.'/'.$entry);
  344                         $struc['size']          = $this->size($path.'/'.$entry);
  345                         $struc['lastmodunix']= $this->mtime($path.'/'.$entry);
  346                         $struc['lastmod']   = date('M j',$struc['lastmodunix']);
  347                         $struc['time']          = date('h:i:s',$struc['lastmodunix']);
  348                  $struc['type']          = $this->is_dir($path.'/'.$entry) ?   'd:'f';
  349 
Run Code Online (Sandbox Code Playgroud)

更新#5:
做一个php -i | fgrep -i date回报

建造日期=> 2011年12月13日18:43:02

date
date/time support => enabled
date.default_latitude => 31.7667 => 31.7667
date.default_longitude => 35.2333 => 35.2333
date.sunrise_zenith => 90.583333 => 90.583333
date.sunset_zenith => 90.583333 => 90.583333
date.timezone => no value => no value
Run Code Online (Sandbox Code Playgroud)

然后我编辑了php.ini文件,将时区设置为"America/Los Angeles"并得到了这个输出

date/time support => enabled
date.default_latitude => 31.7667 => 31.7667
date.default_longitude => 35.2333 => 35.2333
date.sunrise_zenith => 90.583333 => 90.583333
date.sunset_zenith => 90.583333 => 90.583333
date.timezone => America/Los_Angeles => America/Los_Angeles
Run Code Online (Sandbox Code Playgroud)

然后我重新启动了apache2.我仍然得到错误

Cod*_*ris 21

在chroot模式下使用php-fpm时也会出现此问题,在这种情况下,解决方案是在chroot目录中创建类似/ usr/share/zoneinfo/Europe的内容然后将您的TZ文件复制到其中,例如伦敦


LSe*_*rni 6

根本原因:无法打开其中一个zoneinfo文件.

还造成:打开文件太多.

我今天在Ubuntu 14.04.01-LTS"Trusty Tahr"上遇到了同样的问题,并尝试了其他答案而没有任何好处.权限很好,文件在那里,内容是按预期的.

最后我决定从命令行线束中运行脚本,以便我可以尝试strace.这就是结果:

openat(AT_FDCWD, "/usr/share/zoneinfo/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = -1 EMFILE (Too many open files)
open("/usr/share/zoneinfo/zone.tab", O_RDONLY) = -1 EMFILE (Too many open files)
stat("/usr/share/zoneinfo/Europe/Rome", {st_mode=S_IFREG|0644, st_size=2652, ...}) = 0
open("/usr/share/zoneinfo/Europe/Rome", O_RDONLY) = -1 EMFILE (Too many open files)
write(1, "\nFatal error: Unknown: Timezone "..., 104) = 104
Run Code Online (Sandbox Code Playgroud)

怎么了

当PHP"访问zoneinfo数据库"时,它实际上尝试打开目录和一些文件.如果其中一些操作失败,则会显示"zoneinfo corrupt"消息,但这只表示PHP进程无法打开这些文件:

  • 他们不在那里(chroot jail,zoneinfo安装错误)
  • 他们不存在,也不应该是:"欧洲/丝瓜"不是一个有效的时区,但一个错字.
  • 他们在那里,但权限错误.
  • 他们在那里,但这个过程没有被授权(SELinux,AppArmor,...)
  • 他们在那里,但fopen手术暂时不起作用

我的情况是最后一个:真正的问题是脚本打开了太多临时文件,并在运行时保持打开状态.可以同时打开多少文件是有限制的,而zoneinfo文件是众所周知的最后一根稻草.当我向负责的开发人员反弹"太多文件"问题时,快速解决问题暂时解决了问题.

实际上我怀疑这指向PHP不断打开和关闭zoneinfo数据库而不是缓存它,但它是另一天的调查.

间歇性错误 "打开文件的数量"是每个进程,而不是每个PHP脚本.因此,有两个(至少)方案可能导致难以诊断,可能是间歇性/不可重现的错误:

  • 一些长时间运行的流程导致资源泄漏缓慢,例如在Racket下.
  • 由在同一进程中运行的另一个脚本或子例程占用的资源,甚至可能根本与PHP无关.

一个PHP脚本,无论是对还是错,分配800个文件都可以正常工作,直到遇到另一个分配了224个文件的子进程.达到了每个进程1024个打开文件的限制,在这种情况下,进程失败并出现一个神秘的错误(仅在此隐含地指向长链并发原因中的最后一个症状).

Apache:网站太多了.

运行的Apache mod_php5将导致PHP访问的文件被Apache进程打开.但Apache进程也会保持其日志文件处于打开状态,并且每个进程都有一个处理每个日志文件的句柄.

因此,如果您有200个网站,每个网站都有一个独立的access_log,那么/var/www/somesite/logs/access_log每个进程将从已经用于管理的210个句柄开始,剩下800个免费供PHP使用.

如果脚本需要一次分配900个临时文件,这可能导致开发服务器(具有一个站点)工作,生产服务器(安装了200个站点)不工作的情况.

脏诊断(在Unix/Linux上):glob /proc/self/fdcount()结果.丑陋的罪恶,但它给出了关于实际打开多少文件描述符的大概数字.

快速而肮脏的修复(在Unix/Linux上):增加每个进程打开文件的fdlimit,使其达到1024(当然你需要是root用户).这更像是服务器故障的问题.


Tim*_*bru 3

问题是文件权限。我授予 apache2 用户对 usr/share/zoneinfo 和 etc/localtime 的读取和执行权限。之前,我也没有为当地时间的父母设置正确的权限。即我只更改了localtime和zoneinfo的权限,而没有更改其父目录的权限。如此愚蠢!离开问题并回到问题总是有用的。