- 編譯 PHP Extension 基本指令
- 製作 php extension Makefile
- 簡化過後的 Makefile
- php zend_module_entry 說明
- php zend_function_entry 說明
- php extension 基本function說明
- 建立一個 php extension function
- 相關問題
- warning: deprecated conversion from string constant to 'char*'
- undefined symbol:__gxx_personality_v0
- 錯誤: Invalid library (maybe not a PHP library)的處理
- 為何要加 extern "C" 呢?
編譯 PHP Extension 基本指令
PHP Extension 是什麼呢?很多寫PHP的工程師,或許不知道 PHP Extension是怎麼做出來的,其實 PHP 的最底層是使用 c 語言,每一句 PHP 的語法,都是透過 c 語言來實現,所以我們可以藉由 c ,去加強 PHP 的功能,讓PHP 更方便使用,一個正常的PHP Extension製作時,要先編寫 config.m4 這個檔案,雖然這個檔案不難寫,不過他與 Makefile的格式落差很大,這裡就介紹如何使用 Makefile 來編譯 PHP Extension ,先介紹一下正常的 PHP Extension編譯 流程,phpize會建立 Makefile檔案, ./configure 這個指令會自動檢查相關package 是否安裝
- 建立 c code 與 config.m4
- phpize (下 Linux 指令 phpize)
- ./configure
- make
- mv modules/xxx.so /extension : 將編譯出來的so檔,搬到extension目錄下,這樣就算完成一個 PHP Extension
製作 php extension Makefile
基實 phpize的行為就是建立 Makefile, libtool 等等工具,./configure就是檢查一些設定是否正常,以及路徑設定,即然我都要自已寫 php extension了,那就跳過這兩個過程吧,自已寫 Makefile。
- 首先我們先使用phpize ,自動生出一個 Makefile,然後再把他改成我們想要的格式。
- phpize & mv Makefile Makefile.global : 把Makefile改成Makefile.global
修改 Makefile.global 成我們要的樣子 Makefile.global範例
- 加入php 安裝的目錄 PHP_DIR = /home/program/php (路徑自已修改吧)
- 將部分變數的值改到 PHP_DIR ,如 prefix = $(PHP_DIR),phpincludedir = $(PHP_DIR)/include/php
- phplibdir = $(SRC_PATH)/modules :指定編譯完成後,so檔的路徑
- PHP_PECL_EXTENSION = extension name //(注意名稱不要重覆)
- srcdir , builddir , top_srcdir , top_builddir ,修改至當前目錄,要用絕對目錄
簡化過後的 Makefile
Makefile.global弄半天,最後終於簡化完成,以後要編譯php extension就方便多了,正式寫一個要用來編譯程式的 Makefile ,你看! 下面的 PHP Makefile 多麼簡短。
- SRC_PATH = $(shell pwd)
- LDEF = -DCOMPILE_DL_MyExtension
- CXXFILE =myClass.cc extension.cc
- EXTRA_CXXFLAGS=
- CXXOUTPUT = MyExtension
- include Makefile.global
- cp:
- sudo cp ./modules/$(CXXOUTPUT).so /home/php_extension/
make 的結果
- create myClass.lo
- create myClass.o
- create extension.lo
- create extension.o
- create MyExtension.la
- create MyExtension.so
- //最後産生 MyExtension.so 成功
php zend_module_entry 說明
php extension的程序入口,在這裡需要指定 Extension 名稱,program Functions ,init function ,shutdown function等等
- zend_module_entry myClass_module_entry = {
- #if ZEND_MODULE_API_NO >= 20010901
- STANDARD_MODULE_HEADER, //如果是php 5.3以上版本才會用到
- #endif
- PHP_MY_CLASS_EXTNAME, // 我的Extension名稱
- my_function, /* Function的定義,要先指定有那些function可以給php呼叫 */
- PHP_MINIT(myClass),
- NULL, /* MSHUTDOWN */
- NULL, /* RINIT */
- NULL, /* RSHUTDOWN */
- PHP_MINFO(myClass), /* MINFO */
- #if ZEND_MODULE_API_NO >= 20010901
- PHP_MY_CLASS_EXTVER,
- #endif
- STANDARD_MODULE_PROPERTIES
- };
php zend_function_entry 說明
上一步第二個參數(my_function),定義了 function entry 的名稱,所以我在這裡要去定義 function entry 的內容,指定給 php 有哪些 function 可以呼叫,範例中我加入兩個 function ,分別是 simple 與 add , 定義一個 function 要使用 PHP_FE,若是要定義一個 class 的 method ,則必須使用 PHP_ME。
- zend_function_entry my_function[] = {
- PHP_FE(simple, NULL)
- PHP_FE(add,NULL)
- {NULL, NULL, NULL}
- };
php extension 基本function說明
- PHP_MINIT_FUNCTION : 每一次執行php時,會先載入 extension,這時須要執行的程式寫在這裡。
- PHP_MSHUTDOWN_FUNCTION : PHP執行結束時,會執行的程式。
- PHP_MINFO_FUNCTION : extension說明資料,會出現在 phpinfo
- PHP_MINFO_FUNCTION(MyExtension)
- {
- php_info_print_table_start();
- php_info_print_table_header(2,"jk function , remove the same value in array", "enabled");
- php_info_print_table_row(2, "first line", "Available");
- php_info_print_table_end();
- }
接收php 傳進來的變數的方式,使用 zend_parse的function,有下列兩種,其中 [sal,lsl]代表要接放的變數型態。
- zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"sal",xxx,xx)
- zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,num_args TSRMLS_CC,"lsl", xx,xx,xx,xx)
- l : 代表 long
- s : string
- a : array
- b : boolean
- d : double
- 回傳result 給php的方式有下列幾種
- RETURN_LONG : 回傳 long
- RETURN_STRING : 回傳字串
- RETURN_DOUBLE : 回傳倍精數
- RETURN_BOOL : 回傳 boolean (true or false)
建立一個 php extension function
要寫一個 extension function ,就是使用 PHP_FUNCTION 這個 function ,而傳進去的第一個參數,就是 function 名稱,寫好之後,就可以在 php 裡使用 simple("xxx");
- PHP_FUNCTION(simple)
- {
- char* str = NULL;
- char* tmp = new char[50];
- string result = "";
- int str_len = 0;
- if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
- "s",
- &str,&str_len) == FAILURE) {
- RETURN_NULL();
- }
- sprintf(tmp , "Your input string is [%s]" , str);
- result = tmp;
- delete[] tmp;
- RETURN_STRING(const_cast<char*>(result.c_str()),1);
- }
第二個範例是 function entry 中定義的 add ,主要功能是將第一個參數與第二個參數做加總的功能,在 php extension 中,所有的數字回僨都是使用 RETURN_LONG,不需要依 int ,long,float 去判斷回傳值,這是由於 php 變數型態很自由,不用區分得這麼細。
- PHP_FUNCTION(add)
- {
- int num_args = ZEND_NUM_ARGS();
- if (num_args != 2) {
- RETURN_LONG(0);
- }
- int result = 0,int1,int2;
- if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
- "ll",
- &int1,&int2) == FAILURE) {
- RETURN_NULL();
- }
- result = int1 + int2;
- RETURN_LONG(result);
- }
相關問題
warning: deprecated conversion from string constant to 'char*'
我升級 gcc 4.2.0 後,編譯就會出現這個 Warning,使用 gcc 3.4.6 就沒事了。
第二招,碰到 Warning不要停掉,在 Makefile檔案中,將 -Werror 這個屬性拿掉吧
undefined symbol:__gxx_personality_v0
因為你使用 c++ 語法,但是卻沒有載入 c++ 的 library
解法:將 Makefile 的 CC 加上 -lstdc++ ,加上這句後,編譯時,會自動載入 /usr/lib/libstdc++.so。
- CC = cc -lstdc++
錯誤: Invalid library (maybe not a PHP library)的處理
這代表,php在載入 so檔時,找不到程序入口,c & c++的程序入口是 main ,而 php extension的程序入口是 zend_module_entry ,但是因為我有使用到c++ 語法,c++有個特性,在編譯時,會自動亂改變數及function名稱。
- zend_module_entry MyExtension_module_entry
像這句語法,經過 c++ 編譯後,就可能變成 MyExtension_module_entryii,結果造成找不到程序入口而報錯。
解法就是多加一句 extern "C"
- zend_module_entry MyExtension_module_entry = {
- .
- .
- .
- }
- extern "C" {
- ZEND_GET_MODULE(MyExtension)
- }
為何要加 extern "C" 呢?
我們知道C++ 有 overloading 的功能,一個 function 可以有多種不同的參數數量,可是 c++ 是怎麼辦到的呢,其實他在編譯程式的時候,就會自動做 function name 的 mapping ,例如下面的例子,可是單純的 c 語言並不懂這個東西,所以當 c 語言要去讀取 c++ 的function 時,就必需加上 extern "C" ,強迫 c++ 不要亂改名稱,這樣 c語言才能正確的執行 function 。
- int test(int a){}
- int test(char b,char c){}
- //編譯後
- test(int a) => test_1(int a)
- test(char b , char c) => test_2(char b , char c)
回應 (Leave a comment)