首先,仍是强烈推荐一下Phalcon这个框架。
php
因为对这个框架很感兴趣,因此看了其官方文档,并在先前用PHP根据其思想写了两个Phalcon核心类,见连接:
html
#年前福利#Phalcon框架部分核心类的伪实现
web
http://www.oschina.net/code/snippet_256338_32995shell
今天,再次查看Phalcon官方博客说明时,其后来改用了Zephir进行重写。延伸看了一下Zephir,发现其能够用来开发PHP扩展,最终效果效果有点相似C语言。关于Zephir,请见连接:http://zephir-lang.com/index.htmlubuntu
这里主要说一下使用Zephir进行扩展开发实践过程,并延用了先前的依赖注入类DI,对其改用Zephir进行重写。vim
得益于以前完善的测试用例,能够继续使用测试用例进行验证以及TDD开发。如下为以前测试的结果:服务器
phpunit ./test_FDI.php . setUp ... tearDown ... . setUp ... tearDown ... . setUp ... tearDown ... . setUp ... tearDown ... . setUp ... Demo::__construct() Demo::__construct() Demo::__construct() Demo:: onInitialize() tearDown ... . setUp ... Demo2::__construct() Demo2::onConstruct() Demo2::onInitialize() Demo2::onInitialize() tearDown ... . setUp ... tearDown ... . setUp ... tearDown ... Time: 11 ms, Memory: 3.50Mb OK (8 tests, 30 assertions)
在进行Zephir开发前,能够参考官方说明进行安装。而后使用:zephir init dogstar建立开发项目。而后:框架
~zephir$ vim ./dogstar/dogstar/Di.zep
因为初次使用Zephir,并且其语法又介于PHP和C之间,并且发现此语言好像还有好多未完善的语法。如没有elseif / else if这两种用法,在对一个变量初始化为null而后判断isset和empty时结果都为非预期值,不知怎么调用匿名函数,和建立一个动态的实例(根据类名实例化,如:$a = new $className())。因此在开发过程当中,都是边查看官方文档,边进行开发。函数
Zephir是编译语言,因此每次开发完后都须要从新编译。调用命令:zephir build,若是没有语法错误将会提示:测试
dogstar@ubuntu:~/projects/zephir/dogstar$ zephir build Compiling... Installing... [sudo] password for dogstar: Extension installed! Don't forget to restart your web server
编译构建时,须要root权限,而且会提示须要重启PHP。
不一样服务器重启PHP方式不同,如在Ubuntu环境下,则可以使用:sudo /etc/init.d/php5-fpm force-reload
结果显示:
* Reloading PHP5 FastCGI Process Manager php5-fpm [ OK ]
注意,在初次成功构建后,为了让PHP扩展生效,须要修改php.ini文件以添加新的扩展。这里是:
vim /etc/php5/fpm/conf.d/dogstar.ini
而后添加如下内容:
; configuration for dogstar extension=dogstar.so
重启PHP服务器后,php -m | grep dogstar确认一下是否生效。
对原来的测试套件,只须要稍微改动一下(即将实例化的类名改一下),便可对新的PHP扩展类进行测试。
最终测试结果以下:
phpunit ./test_DI.php . setUp ... tearDown ... F setUp ... tearDown ... . setUp ... tearDown ... F setUp ... tearDown ... E setUp ... tearDown ... . setUp ... Demo2::__construct() Demo2::onConstruct() Demo2::onInitialize() Demo2::onInitialize() tearDown ... . setUp ... tearDown ... . setUp ... tearDown ... Time: 13 ms, Memory: 3.50Mb There was 1 error: 1) FDI_Test::testAnonymousFunction Closure object cannot have properties /home/dogstar/projects/php/test/test_dogstar/test_DI.php:85 -- There were 2 failures: 1) FDI_Test::testMagicFunction Failed asserting that 'dogstar' matches expected null. /home/dogstar/projects/php/test/test_dogstar/test_DI.php:38 2) FDI_Test::testMixed Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'name1' +'dogstar2' /home/dogstar/projects/php/test/test_dogstar/test_DI.php:68 FAILURES! Tests: 8, Assertions: 19, Failures: 2, Errors: 1.
一、不知什么缘由,调用魔法函数进行注入时,将会发生奇怪的事情。即_data的值将会发生丢失或变成key值。
二、在Zephir内未找到调用匿名函数的方法。
三、在Zephir内未找到根据一个类名建立对应实例的方法,特别如今添加了命名空间。所以采用了getInstance()的折中方案。
附最终的DI代码:
amespace Dogstar; class Di implements \ArrayAccess { protected static _instance = null; protected _data = []; public function __construct() { } public static function getInstance() { if (typeof self::_instance == "NULL") { let self::_instance = new Di(); self::_instance->onConstruct(); } self::_instance->onInitialize(); return self::_instance; } public function onConstruct() { //TODO } public function onInitialize() { //TODO } public function set(var key, var value) { this->_checkKey(key); let this->_data[key] = value; } public function get(key, defaultValue = null, boolean isShare = false) { this->_checkKey(key); var value; if !(fetch value, this->_data[key]) { return defaultValue; } let value = this->_data[key]; if gettype(value) == "object" && is_callable(value) { //TODO how to call clourse? //let value = {value}(); } else { if is_string(value) && class_exists(value) { //TODO obtain class instance by call getInstance //let value = new {value}(); if is_callable([value, "getInstance"]) { let value = call_user_func([value, "getInstance"]); } if gettype(value) == "object" && is_callable([value, "onConstruct"]) { call_user_func([value, "onConstruct"]); } let isShare = false; } else { //TODO //init by array configs } } if !isShare && gettype(value) == "object" && is_callable([value, "onInitialize"]) { call_user_func([value, "onInitialize"]); } let this->_data[key] = value; return value; } protected function _checkKey(var key) { if empty(key) || (!is_string(key) && !is_numeric(key)) { throw new \Exception("Unvalid key(" . gettype(key) . "), expect to string or numeric"); } } public function __call(name, params) { var prefix; let prefix = substr(name, 0, 3); var key; let key = lcfirst(substr(name, 3, strlen(name))); var value = null; fetch value, params[0]; if prefix == "get" { return this->get(key, value); } if prefix == "set" { this->set(key, value); return; } throw new \Exception("Call to undefined method Di::" . name . "()"); } public function __set(key, value) { this->set(key, value); } public function __get(key) { return this->get(key, null, true); } public function offsetSet(offset, value) { this->set(offset, value); } public function offsetGet(offset) { return this->get(offset); } public function offsetUnset($offset) { unset(this->_data[offset]); } public function offsetExists(offset) { var value; return fetch value, this->_data[offset]; } }