标签 PHP 下的文章

在哪些情况下 HTTP_REFERER 会为空

有时候我们需要获取当前页面的前一个页面信息,在PHP里面,通过$_SERVER['HTTP_REFERER']即可获取相关信息,配合使用parse_url()函数,可以切割网址单独获取各部分信息。但referer并非总是有效的,有几次这个问题困恼了我很久。

第一次碰到这个问题是因为浏览器原因导致,详情可参考我去年写的一篇文章:IE浏览器下HTTP_REFERER失效
第二次是因为给网站设置了https访问,但跳转的链接还是http。

通过查阅相关资料,总结了下面这些情形,HTTP_REFERER 有可能为空(失效):

  • 直接在浏览器地址栏输入网址访问
  • 通过浏览器书签点击访问网站
  • 在一个外部应用程序中点击链接访问
  • 从 https 链接跳转到 http 链接
  • 从 https 链接跳转到 另一个不同的 https 链接
  • 服务器安装了诸如firewall、antivirus等安全软件,将referer过滤掉了
  • 使用了过滤 referer 的代理服务器
  • 通过诸如curl等程序访问网站,但未设置referer头
但需要注意的是,完全信任 HTTP_REFERER 是不推荐的,不仅因为该变量容易失效或为空,更在于该变量很容易被伪造。

PHP设置脚本执行时间

php.ini 中缺省的最长执行时间是 30 秒,这是由 php.ini 中的 max_execution_time 变量指定,如果脚本需要跑很长时间,例如要大量插入数据库,或者分析统计大量数据,服务器会在 30 秒后强行终止正在执行的程序,这自然不是我们希望看到的,所以就要更改php脚本最大执行时间。

PHP设置脚本最大执行时间的三种方法

1、在php.ini里面设置

max_execution_time = 120;

2、通过PHP的ini_set函数设置

ini_set("max_execution_time", "120");

3、通过set_time_limit 函数设置

set_time_limit(120);

以上几个数字设置为0则无限制,脚本会一直执行下去,直到执行结束。

所以,需要长时间执行的脚本,一般在php代码开头处添加如下代码就可以了
set_time_limit(0);

案例:
有文本文件data.txt,共有五万行,现在需要将这批数据按行插入数据库。可以预见,这个脚本执行需要很长时间,所以必须设置最大执行时间,此处设置成无限制。

set_time_limit(0);

$file = fopen('data.txt', 'r');
while (!feof($file)) {
    $time = time();
    $line = fgets($file);
    DB::query("INSERT INTO `code`(code,created_at) VALUES('$line',$time)"); 
}
echo 'successful imported';

IE浏览器下HTTP_REFERER失效

后端语言:PHP。

由于功能需求,后端需要判断链接的http_referer值,PHP保存在 $_SERVER['HTTP_REFERER']中。但经过测试,IE浏览器(包括IE7-IE11)下该值为空,谷歌、火狐等均能正常获取。页面链接是通过window.open(url)的方式打开的。

后得知,IE浏览器只有通过a链接或者form表单提交的方式才能获取到HTTP_REFERER,所以有了下面的解决方案,大致就是先判断浏览器是否是IE浏览器,如果是,就模拟a链接点击跳转到指定页面,如果不是,就使用正常方式跳转。

代码如下:

    // document.all 方法ie11不支持,所以不能简单通过 (document.all) ? true : false 来判断
    function isIE() { //ie?
        if (!!window.ActiveXObject || "ActiveXObject" in window)
            return true;
        else
            return false;
        }

    function referURL(url){
        var isIe=isIE();
        if(isIe) {
           var linka = document.createElement('a');
           linka.href=url;
           linka.target="_blank";
           document.body.appendChild(linka);
           linka.click();
         } else {
             window.open(url);
          }
    }

记一次网站迁移

背景:公司的部分业务是网站建设,现由于公司业务调整,需要将公司部署在电信IDC机房的四千多个网站迁移至百度云。

迁移开始之前,考虑了几个难点:
1、网站数据较大(150G左右),通过公网传输需要耗费很长时间,但其间不能影响网站正常访问;
2、每个站点都配有FTP账号,迁移后要保证原来的账号仍然可用;
3、所有站点都配有二级域名,当初做的是泛域名解析。

第三个难点相对容易解决,只需要调整一下Apache的匹配规则,然后起用全新的泛域名指向即可。

为了保证第二点中提到的FTP可用,第一点中转移数据的时候,不仅要保证数据完整,而且文件、目录的属性也要和老服务器一致。所以,开始转移数据之前,要在新服务器添加所有对应的用户。补充一点,我们用的是SFTP,所以所有账号都是Linux的一个用户,名称和站点目录一致。

所以迁移步骤大致如下:
1、利用Python脚本生成SFTP用户及对应密码,保存到文本文件,每个账号占一行,后面紧跟一个空格加密码;在新服务器中读取该文本文件,并批量添加用户。之所以可以用这种方法,是由于最开始的SFTP账号是根据站点目录名生成的,所以通过脚本比较方便处理。
2、用Rsync同步数据,配置文件中 use chroot = no 来保证所有文件的所属用户和组都正确,另外 -a 参数保证文件读写权限与旧服务器一致。
3、mysqldump方法导出数据库,同样通过rsync同步到新服务器,导入。我们的结构是一个站点一个数据库,所以虽然数据库数量多,但都很小,毕竟都是企业站居多。导入的方法是:python脚本批量解压.gz的数据库文件,然后把需要导入的库写进一个文件source.sql,该文件的内容格式如下:

source xxx.sql;
source xxx2.sql;
...

至此,网站迁移基本完成。

Apache Rewrite 排除某个子目录

在做一个网站的时候,使用了apache Rewrite对URL进行重写,但意外发现了一个问题:Ueditor的一些插件,后缀是.html,本不该被重写,结果导致功能异常。虽然有可能可以通过编写更精确的Rewrite规则来避免这个问题,但直接排除后台管理目录的重写显然更容易实现。所以,找到导致插件重写错误的 RewriteRule,在上面添加 RewriteCond,如下:

RewriteEngine on
RewriteCond %{QUERY_STRING} ^(.*)$
#省略...
#下面这条给紧接着的RewriteRule筛选条件,类似if的功能
RewriteCond %{REQUEST_URI} !^/(admin|admin/.*)$
#下面这条导致了上面描述的问题
RewriteRule ^(.*)/([a-z0-9]{1,}).html$ $1/index.php?act=$2
#省略...

如此,所有 admin 子目录的访问都不会被重写。