2019-04-29

PHP-X是我在2017年年初創(chuàng)建的一個(gè)新項(xiàng)目。這個(gè)項(xiàng)目的目標(biāo)就是讓有一定工作經(jīng)驗(yàn)的PHP程序都能夠具備擴(kuò)展開發(fā)的能力。

0x00 初衷

從2012年開始編寫swoole,現(xiàn)在算來(lái)已經(jīng)有5個(gè)年頭了。我發(fā)現(xiàn)編寫一個(gè)PHP 擴(kuò)展這個(gè)工作非常艱難。PHP 程序員群體中,甚至可以說(shuō) 100 人中都很難找出一個(gè)會(huì)編寫 PHP 擴(kuò)展的人來(lái)。PHP 官方對(duì)擴(kuò)展開發(fā)者非常不友好,源代碼中提供的Zend API極其難用,API復(fù)雜而且凌亂,充斥著各種宏的寫法。Zend API坑非常多,普通開發(fā)者很容易踩到坑里。出現(xiàn)各種莫名其妙的core dump問(wèn)題。Zend API幾乎沒(méi)有任何文檔,開發(fā)者如果要真正掌握這項(xiàng)技能需要付出大量的學(xué)習(xí)時(shí)間。

于是我今年就冒出一個(gè)新的想法,基于我編寫swoole擴(kuò)展超過(guò)5年的經(jīng)驗(yàn),我試圖在Zend API和C++之間建立一個(gè)包裝層,讓PHP擴(kuò)展開發(fā)變得簡(jiǎn)單。有一定C++基礎(chǔ)的PHPer都可以輕松得開發(fā)一個(gè)PHP擴(kuò)展。

PHP-X這個(gè)項(xiàng)目就這樣誕生了,開發(fā)只用了一個(gè)月的時(shí)間。它的開發(fā)效率非常高,在我公司中一個(gè)只工作了3年的PHP 程序員,都可以做出一個(gè)擴(kuò)展來(lái)。接下來(lái)陸續(xù)在公司的幾個(gè)項(xiàng)目中進(jìn)行了快速驗(yàn)證。在3個(gè)的時(shí)間里修復(fù)了大量崩潰和內(nèi)存泄漏問(wèn)題。目前穩(wěn)定性、性能、健壯性均已達(dá)到工業(yè)級(jí)水準(zhǔn)。

0x01 起步

PHP-X本身基于C++11開發(fā),使用cmake進(jìn)行編譯配置。首先,你需要確定所有依賴項(xiàng)已安裝好。包括:

[if !supportLists]·?[endif]gcc-4.8 或更高版本

[if !supportLists]·?[endif]php-7.0 或更高版本,需要php7-dev包

[if !supportLists]·?[endif]cmake-2.8 或更高版本

然后安裝PHP-X。

git clone https://github.com/swoole/PHP-X.gitcd?PHP-X

cmake .make?-j?4

sudo make?install

未出現(xiàn)任何編譯錯(cuò)誤,會(huì)成功編譯出libphpx.so,并安裝到系統(tǒng)的lib目錄。頭文件會(huì)復(fù)制到系統(tǒng)的include目錄。這時(shí)需要執(zhí)行sudo ldconfig刷新so文件緩存。

0x02 新建工程

使用任意開發(fā)工具,新建一個(gè)test.cc源文件。首先需要引入phpx.h頭文件。然后使用using引入phpx的命名空間。PHP官方未使用C++,因此phpx直接使用了php作為命名空間。

#include <phpx.h>using?namespace?std;using?namespace?php;

創(chuàng)建擴(kuò)展使用PHPX_EXTENSION宏來(lái)實(shí)現(xiàn)。在這宏中只需要new Extension即可創(chuàng)建擴(kuò)展。構(gòu)造方法接受2個(gè)參數(shù),第一個(gè)是擴(kuò)展的名稱,第二個(gè)是擴(kuò)展的版本號(hào)。在PHPX_EXTENSION宏中return這個(gè)擴(kuò)展對(duì)象的指針。

PHPX_EXTENSION()

{

????Extension *ext = new?Extension("test", "0.0.1");

????return?ext;

}

這里必須使用new Extension,而不能直接在棧上創(chuàng)建對(duì)象

0x03 增加函數(shù)

一個(gè)PHP擴(kuò)展的主要作用就是提供擴(kuò)展函數(shù),擴(kuò)展函數(shù)由于是用C/C++代碼實(shí)現(xiàn),因此它的性能會(huì)比PHP用戶函數(shù)性能高幾十甚至上百倍。在phpx中實(shí)現(xiàn)函數(shù)非常簡(jiǎn)單。使用PHPX_FUNCTION來(lái)實(shí)現(xiàn)擴(kuò)展函數(shù),然后調(diào)用Extension::registerFunction來(lái)注冊(cè)擴(kuò)展函數(shù)。

PHPX_FN是一個(gè)助手宏,實(shí)際上展開就是"cpp_hello_world", cpp_hello_world?PHPX_FUNCTION展開后,包含了2個(gè)變量,第一個(gè)是參數(shù)args,第二個(gè)是返回值retval?通過(guò)操作args和retval兩個(gè)變量,就可以實(shí)現(xiàn)函數(shù)的輸入和輸出

這里我們的代碼非常簡(jiǎn)單,cpp_test($str, $n),調(diào)用這個(gè)函數(shù)返回一個(gè)$n個(gè)$str的數(shù)組。

#include <phpx.h>using?namespace?std;using?namespace?php;

//聲明函數(shù)

PHPX_FUNCTION(cpp_test);


PHPX_EXTENSION()

{

????Extension *ext = new?Extension("test", "0.0.1");

????ext->registerFunction(PHPX_FN(cpp_test));

????return?ext;

}

//實(shí)現(xiàn)函數(shù)

PHPX_FUNCTION(cpp_test)

{

????//args[1] 就是這個(gè)擴(kuò)展函數(shù)的第 2 個(gè)參數(shù)

????long?n = args[1].toInt();


????//將返回值 retval 初始化為數(shù)組

????Array _array(retval);


????for(int?i = 0; i < n; i++)

????{

????????//args[0] 就是這個(gè)擴(kuò)展函數(shù)的第 1 個(gè)參數(shù)

????????//append 方法表示向數(shù)組中追加元素

????????_array.append(args[0]);

????}

}

0x04 編譯擴(kuò)展

編寫一個(gè)Makefile文件。內(nèi)容如下:

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?這個(gè)工具是PHP提供的,使用php-config可以得到PHP的安裝路徑、頭文件目錄、擴(kuò)展目錄、其他額外的編譯參數(shù)等等。

這個(gè)Makefile支持了3個(gè)指令,make編譯,make clean清理,make install安裝到擴(kuò)展目錄中。

這里可能需要root權(quán)限,使用sudo make install進(jìn)行安裝直接從網(wǎng)頁(yè)復(fù)制,可能會(huì)出現(xiàn)tab制表符被替換為空格,請(qǐng)手工編輯一下Makefile使用tab縮進(jìn)MacOS下需要在c++編譯參數(shù)中增加-undefined dynamic_lookup

編寫好之后執(zhí)行make install,就會(huì)編譯擴(kuò)展并將擴(kuò)展test.so安裝到PHP的擴(kuò)展目錄中。這時(shí)需要修改php.ini加入extension=test.so加載擴(kuò)展。

使用php -m來(lái)觀察你的擴(kuò)展是否正常加載。

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

swooletesttokenizer

xml

xmlreader

xmlwriter

yac

zlib

zmq


[Zend Modules]

這里看到test,表明你的擴(kuò)展已經(jīng)加載成功了,現(xiàn)在就可以調(diào)用cpp_test這個(gè)擴(kuò)展函數(shù)了。

0x05 執(zhí)行

編寫一個(gè)test.php,內(nèi)容為:

<?php

var_dump(cpp_test("hello", 3));

執(zhí)行test.php:

php test.php

array(3) {

??[0]=>

??string(5) "hello"

??[1]=>

??string(5) "hello"

??[2]=>

??string(5) "hello"

}

可以看到執(zhí)行結(jié)果符合預(yù)期。那么恭喜你,現(xiàn)在你已經(jīng)成功地開發(fā)了一個(gè)PHP擴(kuò)展了。是不是很簡(jiǎn)單?

0x06 更多

上面的例子還比較簡(jiǎn)單,只是編寫了一個(gè)擴(kuò)展函數(shù)。要真正在實(shí)際項(xiàng)目中使用PHP-X你還有很多工作要做。

[if !supportLists]·?[endif]需要C++的功底

[if !supportLists]·?[endif]了解更多PHP-X的API另外配合使用Eclipse等IDE工具,可以實(shí)現(xiàn)API自動(dòng)提示和補(bǔ)齊,開發(fā)起來(lái)會(huì)更順手

[if !supportLists]·?[endif]學(xué)習(xí)資料加下微微老師QQ:1079192266


?PHP-X是我在2017年年初創(chuàng)建的一個(gè)新項(xiàng)目。這個(gè)項(xiàng)目的目標(biāo)就是讓有一定工作經(jīng)驗(yàn)的PHP程序都能夠具備擴(kuò)展開發(fā)的能力

0x00 初衷

從2012年開始編寫swoole,現(xiàn)在算來(lái)已經(jīng)有5個(gè)年頭了。我發(fā)現(xiàn)編寫一個(gè)PHP 擴(kuò)展這個(gè)工作非常艱難。PHP 程序員群體中,甚至可以說(shuō) 100 人中都很難找出一個(gè)會(huì)編寫 PHP 擴(kuò)展的人來(lái)。PHP 官方對(duì)擴(kuò)展開發(fā)者非常不友好,源代碼中提供的Zend API極其難用,API復(fù)雜而且凌亂,充斥著各種宏的寫法。Zend API坑非常多,普通開發(fā)者很容易踩到坑里。出現(xiàn)各種莫名其妙的core dump問(wèn)題。Zend API幾乎沒(méi)有任何文檔,開發(fā)者如果要真正掌握這項(xiàng)技能需要付出大量的學(xué)習(xí)時(shí)間。

于是我今年就冒出一個(gè)新的想法,基于我編寫swoole擴(kuò)展超過(guò)5年的經(jīng)驗(yàn),我試圖在Zend API和C++之間建立一個(gè)包裝層,讓PHP擴(kuò)展開發(fā)變得簡(jiǎn)單。有一定C++基礎(chǔ)的PHPer都可以輕松得開發(fā)一個(gè)PHP擴(kuò)展。

PHP-X這個(gè)項(xiàng)目就這樣誕生了,開發(fā)只用了一個(gè)月的時(shí)間。它的開發(fā)效率非常高,在我公司中一個(gè)只工作了3年的PHP 程序員,都可以做出一個(gè)擴(kuò)展來(lái)。接下來(lái)陸續(xù)在公司的幾個(gè)項(xiàng)目中進(jìn)行了快速驗(yàn)證。在3個(gè)的時(shí)間里修復(fù)了大量崩潰和內(nèi)存泄漏問(wèn)題。目前穩(wěn)定性、性能、健壯性均已達(dá)到工業(yè)級(jí)水準(zhǔn)。

0x01 起步

PHP-X本身基于C++11開發(fā),使用cmake進(jìn)行編譯配置。首先,你需要確定所有依賴項(xiàng)已安裝好。包括:

[if !supportLists]·?[endif]gcc-4.8 或更高版本

[if !supportLists]·?[endif]php-7.0 或更高版本,需要php7-dev包

[if !supportLists]·?[endif]cmake-2.8 或更高版本

然后安裝PHP-X。

git clone https://github.com/swoole/PHP-X.gitcd?PHP-X

cmake .make?-j?4

sudo make?install

未出現(xiàn)任何編譯錯(cuò)誤,會(huì)成功編譯出libphpx.so,并安裝到系統(tǒng)的lib目錄。頭文件會(huì)復(fù)制到系統(tǒng)的include目錄。這時(shí)需要執(zhí)行sudo ldconfig刷新so文件緩存。

0x02 新建工程

使用任意開發(fā)工具,新建一個(gè)test.cc源文件。首先需要引入phpx.h頭文件。然后使用using引入phpx的命名空間。PHP官方未使用C++,因此phpx直接使用了php作為命名空間。

#include <phpx.h>using?namespace?std;using?namespace?php;

創(chuàng)建擴(kuò)展使用PHPX_EXTENSION宏來(lái)實(shí)現(xiàn)。在這宏中只需要new Extension即可創(chuàng)建擴(kuò)展。構(gòu)造方法接受2個(gè)參數(shù),第一個(gè)是擴(kuò)展的名稱,第二個(gè)是擴(kuò)展的版本號(hào)。在PHPX_EXTENSION宏中return這個(gè)擴(kuò)展對(duì)象的指針。

PHPX_EXTENSION()

{

????Extension *ext = new?Extension("test", "0.0.1");

????return?ext;

}

這里必須使用new Extension,而不能直接在棧上創(chuàng)建對(duì)象

0x03 增加函數(shù)

一個(gè)PHP擴(kuò)展的主要作用就是提供擴(kuò)展函數(shù),擴(kuò)展函數(shù)由于是用C/C++代碼實(shí)現(xiàn),因此它的性能會(huì)比PHP用戶函數(shù)性能高幾十甚至上百倍。在phpx中實(shí)現(xiàn)函數(shù)非常簡(jiǎn)單。使用PHPX_FUNCTION來(lái)實(shí)現(xiàn)擴(kuò)展函數(shù),然后調(diào)用Extension::registerFunction來(lái)注冊(cè)擴(kuò)展函數(shù)。

PHPX_FN是一個(gè)助手宏,實(shí)際上展開就是"cpp_hello_world", cpp_hello_world?PHPX_FUNCTION展開后,包含了2個(gè)變量,第一個(gè)是參數(shù)args,第二個(gè)是返回值retval?通過(guò)操作args和retval兩個(gè)變量,就可以實(shí)現(xiàn)函數(shù)的輸入和輸出

這里我們的代碼非常簡(jiǎn)單,cpp_test($str, $n),調(diào)用這個(gè)函數(shù)返回一個(gè)$n個(gè)$str的數(shù)組。

#include <phpx.h>using?namespace?std;using?namespace?php;

//聲明函數(shù)

PHPX_FUNCTION(cpp_test);


PHPX_EXTENSION()

{

????Extension *ext = new?Extension("test", "0.0.1");

????ext->registerFunction(PHPX_FN(cpp_test));

????return?ext;

}

//實(shí)現(xiàn)函數(shù)

PHPX_FUNCTION(cpp_test)

{

????//args[1] 就是這個(gè)擴(kuò)展函數(shù)的第 2 個(gè)參數(shù)

????long?n = args[1].toInt();


????//將返回值 retval 初始化為數(shù)組

????Array _array(retval);


????for(int?i = 0; i < n; i++)

????{

????????//args[0] 就是這個(gè)擴(kuò)展函數(shù)的第 1 個(gè)參數(shù)

????????//append 方法表示向數(shù)組中追加元素

????????_array.append(args[0]);

????}

}

0x04 編譯擴(kuò)展

編寫一個(gè)Makefile文件。內(nèi)容如下:

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?這個(gè)工具是PHP提供的,使用php-config可以得到PHP的安裝路徑、頭文件目錄、擴(kuò)展目錄、其他額外的編譯參數(shù)等等。

這個(gè)Makefile支持了3個(gè)指令,make編譯,make clean清理,make install安裝到擴(kuò)展目錄中。

這里可能需要root權(quán)限,使用sudo make install進(jìn)行安裝直接從網(wǎng)頁(yè)復(fù)制,可能會(huì)出現(xiàn)tab制表符被替換為空格,請(qǐng)手工編輯一下Makefile使用tab縮進(jìn)MacOS下需要在c++編譯參數(shù)中增加-undefined dynamic_lookup

編寫好之后執(zhí)行make install,就會(huì)編譯擴(kuò)展并將擴(kuò)展test.so安裝到PHP的擴(kuò)展目錄中。這時(shí)需要修改php.ini加入extension=test.so加載擴(kuò)展。

使用php -m來(lái)觀察你的擴(kuò)展是否正常加載。

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

swooletesttokenizer

xml

xmlreader

xmlwriter

yac

zlib

zmq


[Zend Modules]

這里看到test,表明你的擴(kuò)展已經(jīng)加載成功了,現(xiàn)在就可以調(diào)用cpp_test這個(gè)擴(kuò)展函數(shù)了。

0x05 執(zhí)行

編寫一個(gè)test.php,內(nèi)容為:

<?php

var_dump(cpp_test("hello", 3));

執(zhí)行test.php:

php test.php

array(3) {

??[0]=>

??string(5) "hello"

??[1]=>

??string(5) "hello"

??[2]=>

??string(5) "hello"

}

可以看到執(zhí)行結(jié)果符合預(yù)期。那么恭喜你,現(xiàn)在你已經(jīng)成功地開發(fā)了一個(gè)PHP擴(kuò)展了。是不是很簡(jiǎn)單?

0x06 更多

上面的例子還比較簡(jiǎn)單,只是編寫了一個(gè)擴(kuò)展函數(shù)。要真正在實(shí)際項(xiàng)目中使用PHP-X你還有很多工作要做。

[if !supportLists]·?[endif]需要C++的功底

[if !supportLists]·?[endif]了解更多PHP-X的API另外配合使用Eclipse等IDE工具,可以實(shí)現(xiàn)API自動(dòng)提示和補(bǔ)齊,開發(fā)起來(lái)會(huì)更順手

[if !supportLists]·?[endif]學(xué)習(xí)資料加下微微老師QQ:1079192266

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容