起因

YAF 是用 C 写的 PHP MVC 框架扩展(框架流程编进 .so,不走纯 PHP 框架的解释执行),性能比纯 PHP 框架高一截。要在已有的 PHP 5.6 站点上跑 YAF 项目,第一步就是给这套老环境装上扩展。

需求

基于阿里云的轻应用服务器安装,(Apache+php-fpm,php5.6)

技术实现原理

phpize 是干什么的

PECL 下载的扩展源码里没有 configure 脚本——phpize 负责按本机 PHP 的版本信息现场生成它。因果链由此展开:扩展 .so 是按 PHP 的内部 ABI 编译的(zend 结构体、宏在版本间会变),所以必须用“目标 PHP”自带的 phpize 和 php-config 来编译;机器上装了多个 PHP 时,PATH 里默认的那个很可能不是想要的那个,用错版本编出来的 .so 装上就报错。

安装目录名里藏着 API 版本号

make install 落地的 no-debug-non-zts-20131226 不是随机名:20131226 是 PHP 5.6 的扩展 API 编号,non-zts 表示非线程安全构建。PHP 进程加载扩展时会核对这套编号,对不上直接拒绝加载——“扩展必须与 PHP 版本严格配套”就是靠这个机制保证的。

报错的根因:YAF 与 PHP 的版本对应

文末报错 unknown type name ‘zend_string’ 的根因:zend_string 是 PHP 7 内核才引入的类型,PHP 5.6 里根本没有;而 YAF 3.x 是为 PHP 7 内核写的。所以 PHP 5.6 要用 YAF 2.3.x(本文用的 2.3.3),YAF 3.x 留给 PHP 7+。这不是 bug,是两代内核 ABI 的断层。

为什么要配 Apache 重写

YAF 是单入口 MVC 框架:所有动态请求收敛到 index.php,由框架解析路由。RewriteCond 的 !-d!-f 先放行真实存在的目录和文件(图片、css 直出),剩下的才由 RewriteRule 交给入口文件。

安装YAF扩展

1
2
3
4
5
6
7
8
yum -y install  autoconf
wget http://pecl.php.net/get/yaf-2.3.3
mv yaf-2.3.3 yaf.tar.gz
tar -xzvf yaf.tar.gz
cd yaf-2.3.3
/usr/local/php56/bin/phpize
./configure --with-php-config=/usr/local/php56/bin/php-config
make && make install

安装位置

1
/usr/local/php56/lib/php/extensions/no-debug-non-zts-20131226/

php.ini中配置yaf

编辑文件

1
/usr/local/php56/etc/php.ini

在文件末尾增加记录

1
2
3
4
5
6
7
[yaf]
yaf.use_namespace = 1
yaf.environ = 'product'
yaf.cache_config = 0
yaf.name_suffix = 1
yaf.lowcase_path = 1
extension = /usr/local/php56/lib/php/extensions/no-debug-non-zts-20131226/yaf.so

修改apache的重写规则

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.php/$1 [QSA,PT,L]

报错:

这是因为用了高版本的yaf导致的。用yaf-2.3.3 就好了。

1
/root/yaf-3.3.3/php_yaf.h:148:1: error: unknown type name ‘zend_string’

apache重写规则

在网站目录上传文件.htaccess

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>

    RewriteEngine On

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    #RewriteRule ^(.*)/$ /$1 [L,R=301]

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule .* index.php
</IfModule>

踩坑记录与注意事项

  • YAF 与 PHP 版本必须配套(本文最大的坑):PHP 5.6 用 YAF 2.3.x,PHP 7+ 才用 YAF 3.x;高版本 YAF 在 5.6 下编译直接报 unknown type name ‘zend_string’,根因见上文;
  • phpize / php-config 要用目标 PHP 的:多版本共存时(本文明确写 /usr/local/php56/bin/phpize 就是在防这个),用 PATH 默认那套编出来的 .so 装不上;
  • 改完 php.ini 要重启:扩展在进程启动时加载,不重启 php-fpm/Apache 永远不生效;
  • 验证php -m | grep yaf 有输出才算装上,光看 make install 的 Installing shared extensions 提示不算数;
  • extension 写全路径(本文做法)最直观;也可以把目录配进 extension_dir 再写短名;
  • yaf.environ 要和项目一致:它决定框架读哪套配置(product/dev),对不上会出现“改了配置不生效”的假象。