標籤:stand strong 第一個 順序 loaded 地方 定義 自己 手動
概述
php通過擴充方式,調用c++源碼,這樣做的理由有很多,當你搜到這篇文章時,相信你已經有自己的考慮了。
寫這篇部落格的理由有二:
- 整理下php擴充調用c++代碼的過程。嗯,對,網上有很多類似的文章,不過對於php擴充c++的方式,很多文章在關鍵的地方並沒有說明,或者說,大部分都是擴充調用c代碼的。
- 已經兩年有餘沒寫部落格了,在這2017剛開始的時候,開個部落格,開始新的旅程,也是不錯的,哈哈。
大概目錄
註:下面步驟的作業環境——系統macOS10.12.2,PHP版本 5.6.29.
c++編譯靜態庫
標頭檔:hello.h
#include<string>std::string hello_joint(std::string a, std::string b);
實現標頭檔定義的函數:hello.cpp
#include "hello.h" std::string hello_joint(std::string a, std::string b) { std::string str = a+b; return str; }
產生hello.o檔案
g++ -c hello.cpp
產生靜態庫libhello.a檔案
ar -r libhello.a hello.o
寫個簡單的test.cpp測試下:
1 #include<iostream>2 #include "hello.h"3 int main()4 {5 std::string a = std::string("Hello ");6 std::string b = std::string("World!");7 std::cout<<hello_joint(a, b)<<std::endl;8 return 0;9 }
編譯
g++ test.cpp libhello.a
運行
./a.out
終端輸出
Hello World!
這樣,你的c++靜態庫製作完成。
php 擴充編輯
如果要編輯php擴充,需要下載php源碼,這裡下載的是php-5.6.16。寫文章的時候,才發現php源碼版本和系統的php版本不一致。因為是先下載的php源碼,然後通過brew install php56,不過,影響不大,不用糾結。
運行如下命令,產生擴充檔案夾january。
./ext_skel --extname=january
命名為january,主要是不想跟c++源碼hello產生任何關係,以免後面混淆,當然,也是因為想不出其他比較好的名字。
首先編輯config.m4,需要改的地方大概歸結為三處:
1、找到如下三行,並把前面的注釋dnl去掉。
PHP_ARG_ENABLE(january, whether to enable january support,
Make sure that the comment is aligned:
[ --enable-january Enable january support])
2、找到如下的代碼,並在它的下面加上相應代碼,以支援c++調用。
if test "$PHP_JANUARY" != "no"; then dnl Write more examples of tests here...
要加上的相應代碼,注意其中的參數名。
if test "$PHP_JANUARY" != "no"; then dnl Write more examples of tests here... PHP_ADD_INCLUDE(./include) PHP_ADD_LIBRARY(stdc++, 1, JANUARY_SHARED_LIBADD) PHP_ADD_LIBRARY_WITH_PATH(hello, ./lib, JANUARY_SHARED_LIBADD) PHP_REQUIRE_CXX() PHP_SUBST(JANUARY_SHARED_LIBADD)
3、同時,在該函數的最後一行,把january.c改為january.cpp
PHP_NEW_EXTENSION(january, january.cpp, $ext_shared)
january.cpp需要做的工作比較多:
1、重新命名:把january.c改為為january.cpp;
2、在兩處地方加上EXTERN "C"標識,這點不要忘了:
第一處加extern "C"{},並在下面加上需要的c++標頭檔:
extern "C" {#ifdef HAVE_CONFIG_H#include "config.h"#endif#include "php.h"#include "php_ini.h"#include "ext/standard/info.h"#include "php_january.h"}#include<string>#include "hello.h"
第二處加BEGIN_EXTERN_C()和END_EXTERN_C():
#ifdef COMPILE_DL_JANUARYBEGIN_EXTERN_C() ==>添加的ZEND_GET_MODULE(january)END_EXTERN_C() ==>添加的#endif
3、在如下的地方,加入函數名稱,可以理解為php要調用的函數的聲明:
const zend_function_entry january_functions[] = { PHP_FE(confirm_january_compiled, NULL) /* For testing, remove later. */ PHP_FE(january_say, NULL) PHP_FE_END /* Must be the last line in january_functions[] */};
4、注意到january.cpp中,它編寫了一個函數例子confirm_january_compiled,現在,我們也需要完成january_say的函數實現,它接收來自php的參數,並在函數中調用c++函數。
PHP_FUNCTION(january_say){ char *arg1 = NULL, *arg2 = NULL; int arg1_len, arg2_len; int argc = ZEND_NUM_ARGS(); if(zend_parse_parameters(argc TSRMLS_CC, "ss", &arg1, &arg1_len, &arg2, &arg2_len) == FAILURE) return; std::string a = std::string(arg1); std::string b = std::string(arg2); std::string res = hello_joint(a, b); RETURN_STRING(res.c_str(), res.length());}
完成這些步驟之後,剩下的工作就少多了。
要接下去做其他步驟的話,建議先做兩個拷貝動作:
1,建立lib檔案夾,將之前製作的libhello.a拷貝到lib裡;
2,建立include檔案夾(也可以等到./configure的時候,它會幫你建立這個檔案夾),將hello.h標頭檔拷貝到這裡。
利用php工具,按順序運行如下四個命令:
phpize./configuremakemake install
註:如果中間修改了程式碼什麼的,記得要從第一個phpize重新執行來過。
運行正常的話,january.so會安裝到.../lib/php/extensions/no-debug-non-zts-20131226/目錄下。
最後,需要載入january.so,主要是改配置php.ini。如果不知道這個檔案在哪的話,你可以在php程式中輸出(echo phpinfo())。
echo phpinfo()的輸出,可以看到指明了php.ini的所在目錄。Virtual Directory Support => disabledConfiguration File (php.ini) Path => /usr/local/etc/php/5.6Loaded Configuration File => /usr/local/etc/php/5.6/php.ini
首先,找到enable_dl,改為On;
enable_dl = On --在php.ini 中開啟php動態載入dl
再找到有很多extension=xxxxxx.so的地方,添加下面句子,載入動態庫,當然也可以在程式中手動載入。
extension=january.so
在php程式中測試:
echo january_say("hello ", "world!");
輸出:
hello world!
結語
快過年了,都不能靜心的好好寫部落格了(真的是水平不夠,經驗有限的極好借口\(^o^)/~),如果有錯誤的地方,歡迎指正。
同時,剛開始做的時候,可能會碰到這樣那樣的問題,希望你最終能順利完成!當然,如果這篇部落格能對你有一點點協助的話,就更好了.
PHP擴充調用C++靜態庫