無標題文檔

那些被忽视的 PHP 函数(整理)

真的是不用不知道,其实我们熟悉的 PHP 还有很多好东西没有发掘。 看到这篇文章 ,当时就泪奔了好几回,重点推荐下,顺便我自己也做个整理。

sys_getloadavg()

这个函数 返回当前 系统的负载均值信息 (当然 Windows 下不适用),详细文档可以翻阅 PHP 的相关文档。文档中有段示例代码,基本上也就能看出它的用途了。

<?php
$load = sys_getloadavg();
if ($load[0] > 80) {
    header('HTTP/1.1 503 Too busy, try again later');
    die('Server too busy. Please try again later.');
}

PS,如果「很不幸」得你的 PHP 环境中没有这个函数,可以考虑使用下面这段代码 via

if (!function_exists('sys_getloadavg')) {
    function sys_getloadavg()
    {
        $loadavg_file = '/proc/loadavg';
        if (file_exists($loadavg_file)) {
            return explode(chr(32),file_get_contents($loadavg_file));
        }
        return array(0,0,0);
    }
}

这一特性如果使用得当,能减轻服务器部分压力。

pack()

pack 对应的还有个函数为 unpack ,用于压缩二进制串,文中的作者的示例非常清楚

$pass_hash = pack("H*", md5("my-password"));

如果你使用 PHP5,那么可以直接这样子

$pass_hash = md5("my-password", true); // PHP 5+

这样做的好处之一是能减少串存储空间(能节省多少呢?可能又会是另篇文章了)。

这里还有个示例代码可以 pack 数组 via

<?php
function pack_array($v,$a) {
 return call_user_func_array(pack,array_merge(array($v),(array)$a));
}

cal_days_in_month()

该函数 可以直接返回指定月份中的天数,例如

$days = cal_days_in_month(CAL_GREGORIAN, date("m"), date("Y")); // 31

我敢保证, 你自己实现过类似功能的函数 :^)

_()

呃, 这的确也是个 PHP 函数 (也有可能是最短的 PHP 内置函数)。_() 是它的「小名」,它的大名是 gettext()

写过 Wordpress 皮肤的朋友会了解 __() 以及 _e() 这些函数,其实 PHP 早已经自带了相关的功能。

// Set language to German
setlocale(LC_ALL, 'de_DE');
 
// Specify location of translation tables
bindtextdomain("myPHPApp", "./locale");
 
// Choose domain
textdomain("myPHPApp");
 
echo _("Have a nice day");

利用 gettext 可以编写多语言的应用,现在您感兴趣的可能就是如何编写 locale 文件,这但已经不是此文涉及的重点, 更多信息可以移步到这里

get_browser()

坦白讲,见到 这个函数 我当时就彻底泪奔。有了这个函数,再也不用自己去分析 $_SERVER['HTTP_USER_AGENT'] 这个字符串了。

更多的信息可以参考这里。在使用此函数前, 你可能需要个 browscap.ini 配置文件 ,相信你可以搞定的。

debug_print_backtrace()

以前查看函数调用堆栈,我会使用 xdebug 等的扩展,其实 PHP5 版本以后已经 内置了相关的函数

顺便再分享个「蛋疼」的小技巧 ,如果你记不住这个函数的名字,可以用这段代码同样能达到目的(看起来还是记住那个函数靠谱):

<?php
$e = new Exception();
print_r(str_replace('/path/to/code/', '', $e->getTraceAsString()));

natsort()

这个函数用于 自然排序 ,这个大家可能都要用到。贴下相关的文档链接以及示例代码

$items = array("100 apples", "5 apples", "110 apples", "55 apples");
 
// normal sorting:
sort($items);
print_r($items);
    # Outputs:
    # Array
    # (
    #     [0] => 100 apples
    #     [1] => 110 apples
    #     [2] => 5 apples
    #     [3] => 55 apples
    # )

natsort($items);
print_r($items);
    # Outputs:
    # Array
    # (
    #     [2] => 5 apples
    #     [3] => 55 apples
    #     [0] => 100 apples
    #     [1] => 110 apples
    # )

有关自然排序的算法规则,可以 参考这里的文档

glob()

这个 函数的功能 同样让人感到泪奔,先不说功能直接上示例代码

foreach (glob("*.php") as $file) {
    echo "$file\n";
}

相比你已经了解该函数的用途了,那么我们就可以有更多的「玩法」,例如就显示目录( via ):

$dirs = array_filter(glob($path.'*'), 'is_dir');

当然,文件递归你也可以 考虑使用下 SPL 扩展

补充 by 神仙

glob 有个参数选项 GLOB_ONLYDIR 就可以只列目录

PHP Filter

如果你还在正则验证字符串,那么就真的「Out」了。自 PHP5.2 版本以后, 内置了 PHP Fliter 模块 用于专门验证 电子邮件、URL 等是否合法,示例代码:

var_dump(filter_var('[email protected]', FILTER_VALIDATE_EMAIL));

由于是新生的模块,因此还有很多的陷阱,例如

filter_var('abc', FILTER_VALIDATE_BOOLEAN); // bool(false)
filter_var('0', FILTER_VALIDATE_BOOLEAN);   // bool(false)

但这不影响我们去尝试。有关 PHP Filter 的更多信息,相信能拎出来另外写篇文章了。

- Split -

最后,感叹 PHP 其实是个历久弥新的工具,不小心我们就会悲剧性得重复造了只轮子。因此,时常看看 PHP 文档每次都会有新的收获。

acookie 改进心得

这是工作记录,慢慢的不知不觉就形成篇文章了,就发在这里吧。

-- Split --

acookie 就是段统计代码,它的原理就是发送个 GET 请求到统计服务器,并附上本地用户的浏览器类型、分辨率等其他信息。

问题

有人报告 acookie 的代码出现 XSS ,原理是利用某 GET 参数 bypass 未过滤完全的字符。这里是原先的代码摘录:

(function() {
    function akrand(num) {
        return Math.floor(Math.random() * num) + 1
    }
    var P = location.pathname;
    if ((parent === self) || P.indexOf('{...}') != - 1 || P.indexOf('{...}') != - 1) {
        var R = escape(document.referrer);
        var id = "" + akrand(9999999) + akrand(9999999);
        var title = escape(document.title);
                document.write('<img src="http://foo/1.gif?acookie_load_id=' + id + '&title=' + title + '&pre=' + R 
                       + '&param0={...}&param1={...}&param2={...}"  width="0" height="0" style="display:none;" />');
    }
})();

这段代码「年事已高」每次的修修补补都是治标不治本,于是干脆考虑重写这段统计代码。

目标

在开始重写之前,为将要写的代码列了几条目标:

  1. 流量方面的考虑,代码要尽可能的简短
  2. 尽可能的优化性能
  3. 「绿色」,不干扰页面的其他脚本
  4. 脚本尽可能得做到安全

思考

  1. 使用原先的 document.write 会在页面中加入 DOM,直接使用 new Image 更好
  2. 原先的判断条件很冗余,完全可以考虑个函数搞定
  3. 安全方面虽然可以写个简短的过滤函数,但这样代码又会很长

解决

下面是反复修改后的最终代码:

(function(){
    var M = function(n) {
        return Math.floor(Math.random() * n) + 1;
    },
    I = function() {
        for (var i = 0, P = location.pathname, args = arguments, len = args.length; i < len; i++) {
            if (P && P.indexOf(args[i]) !== -1) {
                return 1;
            }
        }
        return 0;
    },
    D = document, R = escape(D.referrer), S = screen, T = escape(D.title);

    if (parent === self || I('{...}') || I('{...}')) {
        try {
            return new Image().src = [
                "http://foo/1.gif?cache=" + M(9999999),
                '&pre='+ R +'&scr='+ S.width +'x'+ S.height + '&title=' + T,
                '&param0=' + '{...}',
                '&param1=' + '{...}',
                '&param2=' + '{...}'
            ].join('');
        } catch (e) {}
    }
})();

本来想使用 ~function() {}; 这样的闭包, 结果发现效率方面还是原先的理想 (当然不会差很多),加之可能以后阅读的同事会有困扰,还是采用原先的吧。

在这个案例中,其实返回 0 和 1 同比返回 false 和 true 是同样的道理,处于节省代码量考虑,直接使用整型值。

安全方面出于简单原则考虑,没有考虑使用转义函数,而是采用数组拼贴的方式,这样就算有注入也会造成语法报错而不能指定此段脚本,前面加 return 也是这样考虑。

后记

考虑以后的代码可维护性很重要,这个例子中代码虽然简单,当然还会有更极端的写法,不过处于以后同事的合作考虑,尽量不要写得过于的「专业」。

采用 document.write 的考虑是想直接使用脚本在页面上输出,因此在本案例中不是很适用,同时才用这一方法在安全的角度上考虑需要过滤的字符太多,因此如果了解需求(比如仅仅是生成个带参数的 URL)还是避免使用它

XSS 和 CSRF 等虽说后台处理是治本的,但如果使用不当前后台考虑不周全就算加入了对应的过滤函数,也有可能造成注入。也从另个角度考虑,有时候完善的代码也能从一定程度上加大注入的难度,尤其是 Javascript 等这种「每个人都可以看见得脚本」。

-- EOF --

Mac 下禁用 CNNIC CA 证书

有关 CNNIC 的警告这里有个 详细的说明 ,不多做解释。对于 Mac 用户,这里有个简单的办法禁用 CNNIC CA 证书。

https://friable.rocks/_/2010_02_01/1265080937.png

打开「钥匙串访问」,搜索 「CNNIC 」即可看到对应的证书。双击该证书

https://friable.rocks/_/2010_02_01/1265080978.png

下拉「信任」选择「永不信任」即可。

根据上述文中的提醒,同时把有「entrust.net」关键字的证书也禁用掉,这样就没有上述文中的风险了。

先不讨论文中有关证书的风险,但 CNNIC 的流氓 已经众人皆知,这的确让人感到很不安。因此,个人对于这件事的态度,也是「宁可信其有,不可信其无」。

个人信息安全这块,有时候往往会忽视。 国内的大环境如此 ,也只能是人人自危。

PS,这里有个投票号召众浏览器厂商移除 CNNIC 证书, 有兴趣可以看下查看结果 )。

-- EOF --

我的照片

嗨!我叫「明城」,八零后、码农、宁波佬,现居杭州。除了这里,同时也欢迎您关注我的 GitHubTwitterInstagram 等。

这个 Blog 原先的名字叫 Gracecode.com 、现在叫 「無標題文檔」 。 要知道作为码农取名是件很难的事情,所以不想在取名这事情上太费心思。

作为八零后,自认为还仅存点点可能不怎么被理解的幽默感,以及对平淡生活的追求和向往。 为了避免不必要的麻烦,声明本站所输出的内容以及观点仅代表个人,不代表自己所服务公司或组织的任何立场。

如果您想联系我,可以发我邮件 `echo bWluZ2NoZW5nQG91dGxvb2suY29tCg== | base64 -d`

分类

搜索

文章