PHP-X
是我在2017
年年初建立的一個新項目。這個項目的目標就是讓有必定工做經驗的PHP
程序都可以具有擴展開發的能力。php
從2012
年開始編寫swoole
,如今算來已經有5
個年頭了。我發現編寫一個 PHP 擴展這個工做很是艱難。PHP 程序員羣體中,甚至能夠說 100 人中都很難找出一個會編寫 PHP 擴展的人來。PHP 官方對擴展開發者很是不友好,源代碼中提供的Zend API
極其難用,API
複雜並且凌亂,充斥着各類宏的寫法。Zend API
坑很是多,普通開發者很容易踩到坑裏。出現各類莫名其妙的core dump
問題。Zend API
幾乎沒有任何文檔,開發者若是要真正掌握這項技能須要付出大量的學習時間。mysql
因而我今年就冒出一個新的想法,基於我編寫swoole
擴展超過5
年的經驗,我試圖在Zend API
和C++
之間創建一個包裝層,讓PHP
擴展開發變得簡單。有必定C++
基礎的PHPer
均可以輕鬆得開發一個PHP
擴展。c++
PHP-X
這個項目就這樣誕生了,開發只用了一個月的時間。它的開發效率很是高,在我公司中一個只工做了3
年的 PHP 程序員,均可以作出一個擴展來。接下來陸續在公司的幾個項目中進行了快速驗證。在3
個的時間裏修復了大量崩潰和內存泄漏問題。目前穩定性、性能、健壯性均已達到工業級水準。git
PHP-X
自己基於C++11
開發,使用cmake
進行編譯配置。首先,你須要肯定全部依賴項已安裝好。包括:程序員
php7-dev
包而後安裝PHP-X
。github
git clone https://github.com/swoole/PHP-X.git cd PHP-X cmake . make -j 4 sudo make install
未出現任何編譯錯誤,會成功編譯出libphpx.so
,並安裝到系統的lib
目錄。頭文件會複製到系統的include
目錄。這時須要執行 sudo ldconfig
刷新so
文件緩存。redis
使用任意開發工具,新建一個test.cc
源文件。首先須要引入phpx.h
頭文件。而後使用using
引入phpx
的命名空間。PHP
官方未使用C++
,所以phpx
直接使用了php
做爲命名空間。sql
#include <phpx.h> using namespace std; using namespace php;
建立擴展使用PHPX_EXTENSION
宏來實現。在這宏中只須要new Extension
便可建立擴展。構造方法接受2
個參數,第一個是擴展的名稱,第二個是擴展的版本號。在PHPX_EXTENSION
宏中return
這個擴展對象的指針。mongodb
PHPX_EXTENSION() { Extension *ext = new Extension("test", "0.0.1"); return ext; }
這裏必須使用
new Extension
,而不能直接在棧上建立對象shell
一個PHP
擴展的主要做用就是提供擴展函數,擴展函數因爲是用C/C++
代碼實現,所以它的性能會比PHP
用戶函數性能高几十甚至上百倍。在phpx
中實現函數很是簡單。使用PHPX_FUNCTION
來實現擴展函數,而後調用Extension::registerFunction
來註冊擴展函數。
PHPX_FN
是一個助手宏,實際上展開就是"cpp_hello_world", cpp_hello_world
PHPX_FUNCTION
展開後,包含了2
個變量,第一個是參數args
,第二個是返回值retval
經過操做args
和retval
兩個變量,就能夠實現函數的輸入和輸出
這裏咱們的代碼很是簡單,cpp_test($str, $n)
,調用這個函數返回一個$n
個$str
的數組。
#include <phpx.h> using namespace std; using namespace php; //聲明函數 PHPX_FUNCTION(cpp_test); PHPX_EXTENSION() { Extension *ext = new Extension("test", "0.0.1"); ext->registerFunction(PHPX_FN(cpp_test)); return ext; } //實現函數 PHPX_FUNCTION(cpp_test) { //args[1] 就是這個擴展函數的第 2 個參數 long n = args[1].toInt(); //將返回值 retval 初始化爲數組 Array _array(retval); for(int i = 0; i < n; i++) { //args[0] 就是這個擴展函數的第 1 個參數 //append 方法表示向數組中追加元素 _array.append(args[0]); } }
編寫一個Makefile
文件。內容以下:
PHP_INCLUDE = `php-config --includes` PHP_LIBS = `php-config --libs` PHP_LDFLAGS = `php-config --ldflags` PHP_INCLUDE_DIR = `php-config --include-dir` PHP_EXTENSION_DIR = `php-config --extension-dir` test.so: test.cc c++ -DHAVE_CONFIG_H -g -o test.so -O0 -fPIC -shared test.cc -std=c++11 ${PHP_INCLUDE} -I${PHP_INCLUDE_DIR} -lphpx install: test.so cp test.so ${PHP_EXTENSION_DIR}/ clean: rm *.so
php-config
這個工具是PHP
提供的,使用php-config
能夠獲得PHP
的安裝路徑、頭文件目錄、擴展目錄、其餘額外的編譯參數等等。
這個Makefile
支持了3
個指令,make
編譯,make clean
清理,make install
安裝到擴展目錄中。
這裏可能須要
root
權限,使用sudo make install
進行安裝
直接從網頁複製,可能會出現tab
製表符被替換爲空格,請手工編輯一下Makefile
使用tab
縮進MacOS
下須要在c++
編譯參數中增長-undefined dynamic_lookup
編寫好以後執行make install
,就會編譯擴展並將擴展test.so
安裝到PHP
的擴展目錄中。這時須要修改php.ini
加入extension=test.so
加載擴展。
使用php -m
來觀察你的擴展是否正常加載。
php -m [PHP Modules] Core ctype curl date dom fileinfo filter gd hash iconv inotify json libxml mbstring mcrypt memcached mongodb mysqli mysqlnd openssl pcntl pcre PDO pdo_mysql pdo_sqlite Phar posix redis Reflection session SimpleXML sockets SPL sqlite3 standard swoole test tokenizer xml xmlreader xmlwriter yac zlib zmq [Zend Modules]
這裏看到test
,代表你的擴展已經加載成功了,如今就能夠調用cpp_test
這個擴展函數了。
編寫一個test.php
,內容爲:
<?php var_dump(cpp_test("hello", 3));
執行test.php
:
php test.php array(3) { [0]=> string(5) "hello" [1]=> string(5) "hello" [2]=> string(5) "hello" }
能夠看到執行結果符合預期。那麼恭喜你,如今你已經成功地開發了一個PHP
擴展了。是否是很簡單?
上面的例子還比較簡單,只是編寫了一個擴展函數。要真正在實際項目中使用PHP-X
你還有不少工做要作。
C++
的功底PHP-X
的 API 另外配合使用Eclipse
等IDE
工具,能夠實現API
自動提示和補齊,開發起來會更順手。
相比Zend API
,PHP-X
要簡單易用地多了,相信你不會花太多時間就能夠掌握此項技能。在接下來我會撰寫更多教程,教你們如何使用PHP-X
實現擴展類、資源、回調函數等更復雜的功能。