存档在 ‘Linux & *nix’ 分类

[HOW TO] complie intl extension for php 5.3.2 under Mac OS X 10.6

2010年12月29日

近期我在研究 Symfony2,这是一个全新的、个人认为很有前途的 php 框架。和 Symfony 1.x 相比基本是推倒重来的一个版本,官方号称这是这是最快的 php 框架之一。具体可以自己上官方网站看看。

然而 Symfony2 要求 php 5.3.2+ 以上版本,还需要 php intl 库,而 Mac OS X 10.6 默认自带的 php 没有 intl 扩展,我只好自己安装。php 扩展嘛,最方便的安装方法莫过于用 pecl 安装了,然而:

$ sudo pecl install intl
downloading intl-1.1.2.tgz ...
...
/usr/temp/intl/collator/collator_class.c:92: error: duplicate ‘static’
/usr/temp/intl/collator/collator_class.c:96: error: duplicate ‘static’
/usr/temp/intl/collator/collator_class.c:101: error: duplicate ‘static’
/usr/temp/intl/collator/collator_class.c:107: error: duplicate ‘static’
make: *** [collator/collator_class.lo] Error 1
ERROR: `make' failed

Google 了一番,没什么结果,好多人在问这个问题,但就是没有一个的解决方法。

那就自力更生吧,经过一番“研究”,发现原来是 php 5.3 的使用了新的内存回收方式,zval 结构体也和 php 5.2 不一样了,一些宏也改变了,ZEND_BEGIN_ARG_INFO_EX 这个宏在 php 5.2 是这样的:

#define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \
    zend_arg_info name[] = { \
        { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args },

在 php 5.3 下是这样的:

#define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args)   \
    static const zend_arg_info name[] = {                                                                       \
        { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args },

而 intl 1.1.2 中很多地方都用到了这个宏,比如上面出错的 collator/collator_class.c 文件:

static
ZEND_BEGIN_ARG_INFO_EX( collator_0_args, 0, 0, 0 )
ZEND_END_ARG_INFO()

看见没有,php 5.3 中 ZEND_BEGIN_ARG_INFO_EX 的定义中已经那个包含了一个 static 关键字,而 intl 1.1.2 中用到这个宏的地方还是按照 php 5.2 的方式使用,这个宏经过预处理后,导致出现 static 关键字重复,编译出错。 知道了问题原因,就试着修改下吧。由于 intl 1.1.2 库中是用这个宏的地方很多,不好一个一个该,我就用了一个邪恶的方法,在 intl_common.h 中重新定义这个宏:

// HACK!!!
#define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args)   \
    const zend_arg_info name[] = {                                                                      \
        { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args },

保存再编译,这次上面的那个错误没有了,但出现了别的错误:

/Users/liang/apps/intl-1.1.2/resourcebundle/resourcebundle_class.c:215: error:‘zval’ has no member named ‘refcount’;
make: *** [resourcebundle/resourcebundle_class.lo] Error 1

这个就是上面说的 zval 结构体的改变了。php 5.3 使用了新的 GC 方式,zval 结构体中的 refcount 成员改名为 refcount__gc,is_ref 改名为 is_ref__gc,而且也有配套的管理这个新成员的宏。经过研究,intl 1.1.2 中只有 3 个地方使用 php 5.2 的方式直接访问了 refcount 成员:

$ grep -r refcount *
msgformat/msgformat_format.c:#define Z_ADDREF_P(z) ((z)->refcount++)
msgformat/msgformat_format.c:       /* TODO: needs refcount increase here? */
resourcebundle/resourcebundle_class.c:  retval->refcount--;
resourcebundle/resourcebundle_iterator.c:   object->refcount--;
resourcebundle/resourcebundle_iterator.c:   object->refcount++;

其中 msgformat/msgformat_format.c 文件已经和 php 5.3 兼容了,所以只需要修把后面的三个地方修改为 php 5.3 的方式,那这个错误应该就修改好了。 修改方法很简单,分别打开上面的几个 resourcebundle/resourcebundle_class.c 文件和 resourcebundle/resourcebundle_iterator.c 文件,按照下面的方式修改:

retval->refcount--;     => Z_DELREF_P(retval);
bject->refcount--;      => Z_DELREF_P(object);
object->refcount++;     => Z_ADDREF_P(object);

完成后重新编译,OH YEAH~~~~能通过编译了。不过别高兴得太早,可能还有问题呢。先修改一下 php.ini 中的 intl 配置为:

[intl]
intl.default_locale = zh-CN

然后运行一下测试自带的测试用例,发现有很多测试用例没通过。。。 :-(

$ make test
=====================================================================
TEST RESULT SUMMARY
---------------------------------------------------------------------
Exts skipped    :    0
Exts tested     :   55
---------------------------------------------------------------------
Number of tests :   78                78
Tests skipped   :    0 (  0.0%) --------
Tests warned    :    0 (  0.0%) (  0.0%)
Tests failed    :   17 ( 21.8%) ( 21.8%)
Expected fail   :    0 (  0.0%) (  0.0%)
Tests passed    :   61 ( 78.2%) ( 78.2%)
---------------------------------------------------------------------
Time taken      :   10 seconds
=====================================================================

=====================================================================
FAILED TEST SUMMARY
---------------------------------------------------------------------
get_locale() [tests/collator_get_locale.phpt]
collator_get_sort_key() [tests/collator_get_sort_key.phpt]
datefmt_get_pattern_code and datefmt_set_pattern_code() [tests/dateformat_get_set_pattern.phpt]
datefmt_localtime_code() [tests/dateformat_localtime.phpt]
datefmt_parse_code() [tests/dateformat_parse.phpt]
datefmt_parse_localtime() with parse pos [tests/dateformat_parse_localtime_parsepos.phpt]
datefmt_parse_timestamp_code()  with parse pos [tests/dateformat_parse_timestamp_parsepos.phpt]
datefmt_set_timezone_id_code() [tests/dateformat_set_timezone_id.phpt]
numfmt_format() [tests/formatter_format.phpt]
numfmt_format_currency() [tests/formatter_format_currency.phpt]
grapheme() [tests/grapheme.phpt]
locale_get_display_name() [tests/locale_get_display_name.phpt]
locale_get_display_region() [tests/locale_get_display_region.phpt]
locale_get_display_script() [tests/locale_get_display_script.phpt]
locale_get_display_variant() [tests/locale_get_display_variant.phpt]
locale_get_region() [tests/locale_get_region.phpt]
locale_parse_locale() [tests/locale_parse_locale.phpt]
=====================================================================

因为这些测试用例本身也是 php 程序,我就自己在命令行下运行了一遍检查为什么会导致测试失败。发现大部分输出都是正确的,但有一些情况下输出和测试用例中的断言不匹配。为什么会这样呢?我没有深入研究,也不知道为什么。哪位朋友如果熟悉这方面请赐教。

我最后的结论: 我使用了这个自己编译的版本用了一段时间,没发现有什么不正常的地方,所以这种 patch 方式应该是可行的。但是,由于存在部分测试用例没有通过,可能会隐藏着一些意想不到的bug,所以不建议在生产环境下使用。

懒人有福了,patch 后的版本我放到了 github 上,大家可以直接 clone 一份下来编译,不用自己手动修改代码了,链接在此: https://github.com/liangzhenjing/php53-intl

SELinux 导致 PHP 无法使用 fsockopen 连接到 Memcached 服务器

2010年8月5日

前段时间刚刚写了一篇关于 SELinux 导致 httpd(Apache2) 无法启动 的文章,今天又碰到 SELinux 的问题了。

事情是这样的:

首先是服务器硬盘出问题了:-(,我给换了块硬盘,然后重装系统(CentOS 5.4 i386),然后安装各种程序、还原各种数据。最后一步是使用 memcache.php 来监控 Memcache 状态。然而却发现该工具无法连接上 Memcached 服务器。经检查,Memcached 服务器已经正常启动,使用 telnet 能够正常连接上去,使用 Memcached 的应用程序(PHP程序)也正常工作。查看 memcache.php 代码发现其是使用 fsockopen 来连接 Memcached 服务器,遂怀疑 Socket 扩展的问题。然而,检查发现可以在命令行中使用 fsockopen 连接到任意地址的任意端口,说明 Socket 扩展没问题。但在 httpd 中使用 fsockopen 来就只能连接本机的 80、8080、443 端口,连接其他端口均失败。

检查 httpd 的 log 也没发现任何问题。上网搜索也没发现类似问题,郁闷ing……

于是又想到是否是 SELinux 的问题。grep 了下 /var/log/audit/audit.log,发现以下线索:

» 阅读更多: SELinux 导致 PHP 无法使用 fsockopen 连接到 Memcached 服务器