首先,仍是強烈推薦一下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]; } }