按:注意,技术进步飞快,本文原版对版本问题的说明已经过时,我根据当下情况做了更新。实际上,Phalcon早已经解决了PHP 7的兼容性问题。不过我提醒的是,不同版本的兼容度还是有差别的。
一个小小的个人经验:基础系统的版本选择用次高版本,不用最新版,也不用太老的版本。最新版经常测试新特性,Bug多,次高版本稳定性、新技术可用性相对平衡。所以,我一般用PHP 7.1系列。当然,现在我觉得Python的Django框架优点多多,更乐意用它做事了。反正我编程可以自由切换,用啥语言无所谓。
2008-07-24
最近在用PHP的Phalcon框架做一个网站。遇到一些问题,干脆研究个透彻,免得心里面不踏实 – 我是喜欢刨根问底,凡事都要知道个为什么的人。
安装
作为多年C++开发出身,对脚本性能的追求刻在了骨子里。PHP当然要装PHP7,但Phalcon的安装文档说明的并不准确,没有针对PHP7说明,一些步骤并不需要。
从源码安装Phalcon是很方便的,也是最通用的办法。
1、安装PHP7,可以用PHP 7.1.x。因为Phalcon紧密的跟PHP内核结合,对版本的依赖性很强,没有针对测试过的内核安装,大概率得到:Segmentation fault。这是使用这种框架必然要付出的代价,对Phalcon不满的声音,很多来自于此。目前可以选PHP 7.1.20,建议从源码安装,一键安装包很多。
2、 选择phalcon 3.3.x分支:git clone https://github.com/phalcon/cphalcon.git
git checkout tags/v3.3.2
这个版本是官方发布的正式版本。
这个版本是正式版,针对PHP 7.x.x测试过的。不需要安装其它PHP5的头文件,的比如php5-dev。php5-dev php5-mysql 这类,都不要安装,版本也和PHP7对不上,从源码安装了PHP7,这些头文件都有了。
3、进入phalcon代码目录:cphalcon/build,执行./install。这个install是个shell脚本,它会自动的运行phpize,设置一些编译参数,查看你的PHP的版本,选择合适的代码编译。要从这里开始。提前安装编译工具包是必须的,但前面用源码安装PHP7,GCC这些已经有了。编译成功后,要设置php.ini,最后面增加:
[phalcon]
extension=phalcon.so
4、使用php -m测试一下,看看phalcon模块是否安装成功,如果有错误,基本都是Segmentation fault.
Nginx的配置
官方文档有错误。按照它那个设置,运行得到的是:Access denied.
问题在哪里?谷歌了一番,大概明白了,不过心里不踏实,干脆直接查源码,一探究竟。这是开源的巨大优点:代码前面藏不住秘密,只要你肯去看,能看明白。对于熟悉C/C++开发的人来说,这点更是不成问题,老本行,代码风格熟悉的很。
理解这个,要首先阐述一下Nginx+PHP的运行模式。Nginx是Web服务器,它不处理脚本的解析,可以说一无所知。那如何运行PHP脚本?当然要PHP自己的解析器。这就需要一个纽带,把它们连接起来 – CGI协议。这个其实符合UNIX程序精神:程序只做一件事,要做好;做专业的程序,不做大而全的程序。程序之间用各种机制组合起来。FastCGI是对传统CGI模式的改进,程序可以常驻内存,使用进程池,避免根据请求反复创建进程。PHP-FPM就实现了这个模式,已经整合进入核心,编译后就有这个程序了。Nginx的配置文件有相关的指令可以把两者连接起来,PHP脚本就能顺利解析运行了。
其实对于其它脚本道理也是一样,比如Python。Nginx把外部请求转换成了CGI协议,用规定协议跟脚本通信,然后返回脚本运行结果给外部请求,一般是浏览器。相当于一个网关管道,有进有出。
具体说是这样:Nginx收到请求,比如 index.php。它发现不是能处理的静态文件,就转给配置里面写好的处理器,是这条指令:fastcgi_pass unix:/dev/shm/php-cgi.sock;。
光有这个还不行,还需要一些相关的信息,比如脚本具体位置,参数等等。这个是fastcgi_param参数干的事情。一般Nginx安装包,配置文件目录都有一个fastcgi_params文件,还有fastcgi.conf,两者只差了一行代码。里面包装的就是Nginx和PHP通信的参数信息。
路由模式:
可是因为一些框架的设计需求,还需要其它的配置指令。所有的web框架,都有个路由设计问题,大体分成几种:
- 原生模式:index.php?q=index&a=run… Url有点丑陋
- rewrite模式:index/run/?id=1 需要开启rewrite模块
- pathinfo模式:index/run/id/1 用path传递操作的模块、参数信息
- html模式:index-run.htm?uid=xxx 需要开启rewrite
如果你把网站想象成一个App,那么路由其实就是一个入口操作问题。它把网站内所有的请求,都集中导入到一个地方判断、分配处理器。这个模式被无数框架使用,优点多多。但怎么选择路由模式,各个框架不一样。Phalcon/yaf等框架,选择了pathinfo模式。这个模式的Url比较优雅,但是需要额外的配置,框架需要知道传进来的路径信息,分解后才能处理它。
讨论了半天,那个“Access denied.”咋处理?哪里错了?有技术文章说,解决办法是:打开PHP的配置参数:php.ini -> cgi.fix_pathinfo=1,就解决了。试验了一下,确实立刻OK。但是此法绝不可取,因为它留下了巨大的安全漏洞。
For instance, if a request is made for /forum/avatar/1232.jpg/file.php which does not exist but if /forum/avatar/1232.jpgdoes, the PHP interpreter will process /forum/avatar/1232.jpg instead. If this contains embedded PHP code, this code will be executed accordingly.
PHP的官方配置文档说明里面也提到:
cgi.fix_pathinfo boolean
Provides real PATH_INFO/ PATH_TRANSLATED support for CGI. PHP’s previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok what PATH_INFO is. For more information on PATH_INFO, see the CGI specs. Setting this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting of zero causes PHP to behave as before. It is turned on by default. You should fix your scripts to use SCRIPT_FILENAME rather than PATH_TRANSLATED.
更简单的办法:是删除这行配置:
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
就解决了。它是不需要的,可以看源码:
一个Nginx配置:
fastcgi_split_path_info ^(.+\.php)(/.+)$;
这行是关键,把path_info信息分解。
一些参考资料:
这篇文章,对fastcgi参数阐述的非常清楚。
https://www.digitalocean.com/community/tutorials/understanding-and-implementing-fastcgi-proxying-in-nginx
Nginx官方PHP配置wiki:
https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/
不错的nginx配置参考:
https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/