我有一个WordPress的网站,今天突然停止工作。 当我看到我看到的日志和错误:
[错误] [客户端50.78.108.177] PHP致命错误:strtotime():时区数据库已损坏 – 这应该永远不会发生!
在google上看到一个人说他们在/ usr / share / zoneinfo中发现了一个权限问题。 我试图将权限更改为777,775,770,我仍然继续得到相同的错误。 我在Ubuntu 10.04.3 LTS上运行php PHP 5.3.2。 任何build议或build议将是有益的。如果一切都失败了,我会尝试降级到早期版本的PHP,但我想在做这些之前尝试其他的东西。
谢谢,Timnit
更新
以防万一它有帮助:错误指向下面的函数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 ); }
更新#2:
现在我已经通过简单地把上面的函数return false;
解决了这个问题return false;
没有做任何事情。 但是我还没有弄清楚问题的根源。
更新3:
var_dump($dateformatstring)
string(5)“dmy”string(1)“m”string(5)“dmy”string(1)“m”string(5)“dmy”string(1)“m”
var_dump($mysqlstring)
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
更新#5:
做一个php -i | fgrep -i date
php -i | fgrep -i date
返回
Build Date => Dec 13 2011 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
然后我编辑php.ini文件,将时区设置为“美国/洛杉矶”,并得到这个输出
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
然后我重新启动apache2。 我仍然得到错误
在chroot模式下使用php-fpm时也会发生这个问题,在这种情况下,解决方案是在你的chroot目录下创建类似于/ usr / share / zoneinfo / Europe的东西,然后将你的TZ文件拷贝到它,例如伦敦
还造成了 :打开的文件太多了。
我今天在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
发生什么事
当PHP“访问zoneinfo数据库”时,它实际上会尝试打开一个目录和一些文件。 如果其中一些操作失败,将出现“zoneinfo corrupt”消息,但这只是表示PHP进程无法打开这些文件:
fopen
操作暂时不工作 我的情况是最后一个: 真正的问题是脚本打开了太多的临时文件 ,并在运行时将其打开。 可以同时打开多少个文件是有限制的,zoneinfo文件是最好的最后一根稻草。 快速修复暂时解决了这个问题,同时我把“太多的文件”问题退回给负责的开发人员。
其实我怀疑这也指向PHP不断打开和关闭zoneinfo数据库,而不是缓存它,但这是一个调查的另一天。
间歇性错误 “打开的文件数量”是每个进程 ,而不是每个PHP脚本 。 所以有两个(至少)情况可能会导致难以诊断的,可能是间歇性的/不可重现的错误:
一个正确或错误的PHP脚本分配800个文件可以正常工作,直到遇到另一个分配了224个文件的子进程。 达到了每个进程1024个打开文件的限制, 在这种情况下 ,进程失败并带有一个神秘的错误(在这个问题上,这只是指一个长链并发原因的最后一个症状)。
运行mod_php5
Apache将导致PHP访问的文件被Apache进程打开。 但是Apache进程也保持打开日志文件 ,每个进程都有一个处理每个日志文件的句柄。
所以如果你有200个网站,每个网站都有一个独立的access_log,比如说/var/www/somesite/logs/access_log
,每个进程将会有大约210个句柄开始处理,留给800个免费的PHP使用。
如果脚本需要一次分配900个临时文件,则可能会导致开发服务器(使用一个站点)工作,而生产服务器(安装有200个站点)则不会。
脏的诊断(在Unix / Linux上) : glob
/proc/self/fd
和count()
结果。 作为罪恶丑陋,但它提供了一个大概的文件描述符实际打开的数字。
快速和肮脏的修复(在Unix / Linux上) :增加每个进程打开文件的fdlimit,使其达到1024(当然你需要是root)。 服务器故障更重要。
问题是文件权限。 我给apache2用户读取和执行访问usr / share / zoneinfo和etc / localtime。 以前,我没有把当地时间的父母设定为正确的权限。 即我只更改localtime和zoneinfo的权限,而不更改其父目录的权限。 如此愚蠢! 从问题中退出并回到问题总是有用的。
你提到“降级”,你最近升级了吗? 在PHP 5.3.x中,您不得不在php.ini文件中设置date.timezone
的有效值。
如果您最近没有升级,请尝试通过重新安装tzdata
软件包来解决问题。 我专门与CentOS合作,所以我不确定Ubuntu的软件包管理器的名字是什么,但是我非常确定tzdata
是发行版的标准。
$ -> yum reinstall tzdata # switch 'yum' for Ubuntu package manager $ -> rm -f /etc/localtime $ -> ln -sf /usr/share/zoneinfo/UTC /etc/localtime # 'UTC' can be replaced with what you prefer $ -> date # check to see that it stuck
此后,您可能需要重新启动您的httpd,以确保获取时区信息。
– 编辑
看起来像罪魁祸首是你的date_i18n()函数,它总是被调用,除非调用代码明确地传递了第三个“伪”参数。 我通过$ translate设置为false的一些测试数据运行你的代码,并运行良好。
function mysql2date( $dateformatstring, $mysqlstring, $translate = true ) { $translate = false; ... if ( $translate ) return 'date_i18n would have been called'; //return date_i18n( $dateformatstring, $i ); ... } $testPatterns = array( array( 'dateformatstring' => 'dmy', 'mysqlstring' => '2011-10-20 05:35:01' ), array( 'dateformatstring' => 'm', 'mysqlstring' => '2011-10-20 05:35:01' ), array( 'dateformatstring' => 'dmy', 'mysqlstring' => '2011-10-20 05:25:22' ) ); foreach ($testPatterns as $testPattern) { // Not passing arg to over-ride $translate, forces call to date_i18n() var_dump(mysql2date($testPattern['dateformatstring'], $testPattern['mysqlstring'])); // Forcing $translate to false, makes date() call which works fine var_dump(mysql2date($testPattern['dateformatstring'], $testPattern['mysqlstring'], false)); }
可能是这个可以帮助你的PHP – 设置时区