按 ‘ PHP ’ 标签归档

magic quotes

这篇博文里涉及两个问题。一是Mediawiki显示空白,二是在托管共享服务器上关掉自己PHP环境里的magic quotes。

对于第一个问题,我找到了原因但是没在上面费时间;解决了第二个问题,第一个问题也就N/A了。

 

先说事情的缘起。最近发现我那个自用小Wiki出状况了,所有页面都显示空白正文,只剩标题和Wiki标准界面菜单。

如果点“编辑”,就能在编辑框里看到正文。做一些改动再打开,可以看到词条内容确实被改动过。这是个很好的信号,说明数据库里的内容都还在,而且存取都没问题,只是后续的处理和显示出错了。

大致搜了一下网上内容,有遇到完全相同症状的,说是PHP的PCRE版本升级了,而低版本的Mediawiki代码会出错。方案是降级PCRE或者升级Mediawiki。

降级PCRE是不可能的了,Mediawiki早该升级了。

那就升吧。

 

  1. 下载Mediawiki。
  2. 解压。
  3. 上传到服务器上的一个新路径下。
  4. 把旧Wiki里的images目录拷过去。
  5. 把旧wiki里的LocalSettings.php改一下$wgScriptPath,拷过去。
  6. 浏览器上打开新路径的Wiki链接/mw-config,按指示操作。
  7. 确认新版本运行正常。
  8. 删除旧Wiki目录。
  9. 把新Wiki目录改为旧Wiki曾用名。
  10. 把LocalSettings.php的$wgScriptPath改回来。

 

做到第六步时报错了,说新版Mediawiki不能在magic_quotes_gpc打开的情况下运行,让我关掉。

magic_quotes_gpc是PHP_INI_PERDIR级别的,不能够被程序软关,就是说在LocalSettings.php里调用ini_set没用,必须在类似php.ini这类系统级设置里硬关。

可系统不是我一个人的,我只能局部定制。

 

直接在.htaccess里写

php_flag magic_quotes_gpc Off

刷,产生“500 Internal Server Error”错误。据说如果PHP在Apache上不是以Module运行,则会看到这个错误号。这条路走不通了,再来。

 

写个PHP脚本,只有一句话:phpinfo(); 上传运行。阅读phpinfo()的输出,找到PHP初始设置都用了哪些.ini文件。

再写个PHP脚本,只有一句话readfile($_GET[file]); 上传运行,把.ini文件一个个读出内容来,拼接成一个php.ini,在文件末尾附上

    magic_quotes_gpc = Off

在.htaccess里放一句

    SetEnv PHPRC 新php.ini全路径名

上传,再刷,没关掉……

 

几乎山穷水尽时留意到了phpinfo输出的这一栏:

    user_ini.filename     .user.ini

对啊,PHP_INI_PERDIR的说明里也提到了.user.ini,我早该用这个最省力的方法。

创建一个.user.ini文件,把“magic_quotes_gpc = Off”写到里面,上传到Wiki主目录下。再刷,呼,终于行了。

专业服务器比笔记本还慢?

新到刀片服务器一台,郑重地装到了机房里,总算在公司有点产业了。

周六就放大长假,所以周四忙到半夜,周五早晨接着来,先把组里的文档管理系统从一个破破的笔记本转移到刀片上去。转移完毕,一刷浏览器,每个页面都要延迟两秒才能打开,而过去在笔记本上,文档系统的反应都是迅如疾风啊!专业大刀片和专业机房,怎么比放在邋里邋遢的办公桌上的笔记本还慢啊!!! 😕

文档管理系统是MediaWiki家的,在Apache平台上,使用PHP存取MySQL数据库。

是Apache慢么?由这个Apache管理的其它独立网页都是打开得飞快。
是PHP慢么?把那个独立网页从HTML改成PHP程序输出,还是刷屏快到连闪烁都看不出。
是MySQL慢么?从一个MySQL客户端单独访问,依然是迅雷不及掩耳盗铃。
合在一起之后的MediaWiki,却又是龟速了。

用这台机器的浏览器访问自己,也是同样的现象,这说明网络和防火墙设置不是原因。再说,如果真是网络问题,那么独立网页和MediaWiki就只是“慢”和“更慢”的区别。

后来不知怎的,就留意到MediaWiki本地配置文件LocalSettings.php里的

$wgDBServer = "localhost";

心想,如果把localhost改成绝对的IP地址——127.0.0.1——呢?这么改了一下,MediaWiki就立马变身神行太保了。

难道系统认出localhost就是127.0.0.1,还需要等两秒种?检查一下\windows\system32\drivers\etc\hosts,居然

# 127.0.0.1  localhost

真的被注释掉了!注释的前头还加了一行注释:“兹事体大,着DNS同学办理。”难怪,时间都被DNS耗费掉了! 😯

速度问题解决之后,忽然又想起一个推论:如果MySQL和Apache分装在这台刀片的两个VM上,那么,MySQL服务器的IP地址就会生写在Apache那边(俺们的系统还没复杂到要用DNS来处理),然后,当Apache来访问MySQL时,就会出现“两台机器互动”比“一台机器自己动”还快的诡异现象。 😉

简单的验证码图片生成器

下面的PHP程序可以生成如  这样的简单验证码。把它存成一个PHP文件,例如captcha.php,使用<img src=’captcha.php’ />显示它,然后,你在服务器端把用户的输入和$_SESSION[‘captcha’]做比较即可:

<?php
    session_start();

    $charset = 'ABCDEFGHJKLMNPQRSTUVWXY3456789';
    $maxpos = strlen($charset) - 1;
    $captcha = '';
    $im = imagecreate(60, 24);
    $white = imagecolorallocate($im, 0xff, 0xff, 0xff);
    for ($i = 0; $i < 4; $i++) {
        $imchar = imagecreate(15, 24);
        $white = imagecolorallocate($imchar,mt_rand(0xe0, 0xff), mt_rand(0xe0, 0xff), mt_rand(0xe0, 0xff));
        $color = imagecolorallocate($imchar, mt_rand(0x00, 0x70), mt_rand(0x00, 0x70), mt_rand(0x00, 0x70));
        $captcha .= substr($charset, mt_rand(0, $maxpos), 1);
        imagestring($imchar, 5, 0, 0, $captcha[$i], $color);
        $imchar = imagerotate($imchar, mt_rand(-10, 10), $white);
        imagecopymerge($im, $imchar, mt_rand(1, 5) + $i * 15, mt_rand(0, 8), 0, 0, 15, 24, 100);
    }
    for ($i = 0; $i < 15; $i++) {
        $x1 = mt_rand(0, 59);
        $y1 = mt_rand(0, 23);
        $x2 = $x1 + mt_rand(-2, 2);
        $y2 = $y1 + mt_rand(-1, 1);
        imageline($im, $x1, $y1, $x2, $y2, mt_rand(0x000000, 0xffffff));
    }
    $_SESSION['captcha'] = $captcha;
    header('Content-type: image/gif');
    imagegif($im);
?>

使用的字符集里没有I、O、1、0、Z、2,因为担心它们互相混淆。思考:S和5是不是也挺像的?纠结。

为了让验证码有“看不清,换一个”的功能,现实中的<img>写得要稍微复杂些,下面代码里的’captcha.php?’+now.getTime()是为了欺骗浏览器,让它以为是一张新的图片进来了,强迫它刷新captcha.php产生的图片,而不是从缓存里获得:

<img src="captcha.php" title="click for a new one"
onclick="now=new Date();src='captcha.php?'+now.getTime();" />

http_response_code或Status code的重置问题

在写一个产生短链接的程序。短链接本身对应的文件当然不是直接存在域名根目录下的,而是通过.htaccess的404 ErrorHandler转交给检索程序,由检索程序把真正的文件输出出来。这里需要把404号Status code覆盖掉。

在家里机器上写的PHP代码是:

header("Content-type: $mime", FALSE, 200);

其中200是代表OK的状态码。

这句话在家里的机器上工作得挺好,但上传到公共网站上之后,程序工作方式就不是那么回事了。404号状态码并未被覆盖掉,如果输出文件是图像,还能勉强显示。但如果是个下载附件,就看到了404号“网页不存在”的错误信息。

经过反复调试,找到解决方法如下:

header("Status: 200 OK");    // Reset the status to OK (200).
header("Content-type: $mime", FALSE, 200);

这两句话缺一不可,如果少第一句,则公共网站上的程序不干活。如果少第一句,则家里的程序不干活。

让IE可以打开web服务器输出的附件

要想让IE可以打开web服务器输出给它的附件,需要在session_start前加这么一段代码:

if(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE)
session_cache_limiter("public");

否则,不属于本session的IE窗口(如他人的机器或者自己另开的窗口)无法打开链接所输出的附件。