编写PHP扩展实践

之前写过一篇关于在Windows下搭建PHP编译环境的文章http://blog.eaxi.com/compiling-php-on-windows/,现在接着介绍下如何编写一个属于自己的PHP扩展。
从PHP5开始,PHP自带了一个ext_skel的脚本,帮助开发者快速的创建一个扩展,现在我们就从这里开始。
首先我们切换到源码目录。

cd ext/
./ext_skel –extname=new_ext

值得注意的是,需要先切换到ext/目录再执行./ext_skel,否则会有些资源文件找不到。
现在脚本就已经帮我们创建好了扩展目录,可以开始编写程序了。

vi new_ext/config.m4

编辑如下几行,以启用这个扩展:

cd ..
./buildconf –force

./buildconf的命令我们加了–force,是因为我们选择基于正式版来编写扩展。从svn或者snaps.php.net下载的开发版则无需加此参数。

./configure –help | grep new_ext

现在,我们就已经可以在编译时使用上新的参数了,在帮助输出中已经有所体现。加上 –enable-new_ext 就可以启用我们的新扩展!

./configure –disable-all –enable-cli –enable-new_ext
make
./sapi/cli/php ext/new_ext/new_ext.php

为了加快编译,我们先用–disable-all禁用默认的扩展,只启用cli,和我们的新扩展。
最后一句是执行默认的测试程序,确认扩展已经成功编译进到PHP。
如果编译无错,就可以继续往下了。接下来我们修改下源文件,实现一个自己的小功能:实现一个函数,它接受一个用户名参数,返回这个用户在系统中的信息,用户不存在则返回NULL。

PHP_FUNCTION(getpwnam);

在php_new_ext.h中,加上这一句,声明我们的新函数名。加入的位置参考例子程序。

zend_function_entry new_ext_functions[] = {
PHP_FE(getpwnam, NULL) /* */
{NULL, NULL, NULL} /* Must be the last line in new_ext_functions[] */
};

在new_ext.c中,先在这个数组中加上函数名,以便系统知道我们的新函数。

PHP_FUNCTION(getpwnam)
{
char *arg = NULL;
int arg_len, len;
struct passwd *pw;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s”, &arg, &arg_len) == FAILURE) {
RETURN_NULL();
}

if (NULL == arg || arg_len == 0) RETURN_NULL();

pw = getpwnam(arg);
if (NULL == pw) RETURN_NULL();

array_init(return_value);
add_assoc_stringl(return_value, “name”, arg, arg_len, 1);
add_assoc_string(return_value, “gecos”, pw->pw_gecos, 1);
add_assoc_string(return_value, “home”, pw->pw_dir, 1);
add_assoc_string(return_value, “shell”, pw->pw_shell, 1);
add_assoc_long(return_value, “uid”, pw->pw_uid);
add_assoc_long(return_value, “gid”, pw->pw_gid);
}

加上这段实现的代码,其他的代码保留原样。其中,return_value是PHP_FUNCTION()宏给每个PHP函数预先定义好的,类型为zval*,表示返回值。初始化为NULL,要返回变量时,修改它就可以了。

make

修改代码后,返回源码根目录直接make就可以了。

取服务器时间的最佳实践

很多场景中,我们都需要拿到服务器的时间,一般的实现方式是写个CGI,把服务器时间吐出来。但CGI容易带来安全或性能的问题。
这里的例子,使用HEAD请求,解析HTTP HEADER中的时间,从而达到同样目的。

var xhr=null;
try{xhr=new XMLHttpRequest();}catch(e){try{xhr=new ActiveXObject(“Msxml2.XMLHTTP”);}catch(e){try {xhr=new ActiveXObject(“Microsoft.XMLHTTP”);}catch (e){xhr=null;} } };
xhr.open(“HEAD”, window.location.href, false);
xhr.send(null);
var t = parseInt(new Date(Date.parse(xhr.getResponseHeader(“Date”))).getTime() / 1000);