2013年9月2日 星期一

PHP Extension (1) - 環境準備與Hello World

下列所有實作均以Ubuntu為範例,視不同distribution或版本可能指令與路徑均有不同

準備環境

$sudo apt-get install apache2 php5 php5-dev

Hello World

傳統PHP function要達到輸出hello world的方式
<?php
function hello_world() {  
  return 'Hello World';  
}
echo hello_world();
?>

若要使用PHP extension開發,要使用c語言實作出相同的function

test_hello_world.h
#ifndef PHP_TEST_HELLO_WORLD_H  
#define PHP_TEST_HELLO_WORLD_H  

extern zend_module_entry test_hello_world_module_entry;  
#define phpext_test_hello_world_ptr &test_hello_world_module_entry  

#ifdef PHP_WIN32  
#define PHP_TEST_HELLO_WORLD_API __declspec(dllexport)  
#else  
#define PHP_TEST_HELLO_WORLD_API  
#endif  

#ifdef ZTS  
#include "TSRM.h"  
#endif  

PHP_MINIT_FUNCTION(test_hello_world);  
PHP_MSHUTDOWN_FUNCTION(test_hello_world);  
PHP_MINFO_FUNCTION(test_hello_world);  

PHP_FUNCTION(hello_world);  

/*  
    Declare any global variables you may need between the BEGIN 
    and END macros here:      

    ZEND_BEGIN_MODULE_GLOBALS(test_hello_world) 
    long  global_value; 
    char *global_string; 
    ZEND_END_MODULE_GLOBALS(test_hello_world) 
 */  

#ifdef ZTS  
#define TEST_HELLO_WORLD_G(v) TSRMG(test_hello_world_globals_id, zend_test_hello_world_globals *, v)  
#else  
#define TEST_HELLO_WORLD_G(v) (test_hello_world_globals.v)  
#endif  

#endif  /* PHP_TEST_HELLO_WORLD_H */  

test_hello_world.cc
#ifdef HAVE_CONFIG_H  
#include "config.h"  
#endif  

#include "php.h"  
#include "php_ini.h"  
#include "ext/standard/info.h"  
#include "test_hello_world.h"  
#include "string"  

using namespace std;  

/* If you declare any globals in php_test_hello_world.h uncomment this: 
   ZEND_DECLARE_MODULE_GLOBALS(test_hello_world) 
 */  

function_entry test_hello_world_functions[] = {  
    PHP_FE(hello_world, NULL)  
    {NULL, NULL, NULL}  
};  

zend_module_entry test_hello_world_module_entry = {  
#if ZEND_MODULE_API_NO >= 20010901  
    STANDARD_MODULE_HEADER,  
#endif  
    "test_hello_world",  
    test_hello_world_functions,  
    PHP_MINIT(test_hello_world),  
    PHP_MSHUTDOWN(test_hello_world),  
    NULL, //RINIT  
    NULL, //RSHUTDOWN  
    PHP_MINFO(test_hello_world),  
#if ZEND_MODULE_API_NO >= 20010901  
    "$Revision: 1.5 $",  
#endif  
    STANDARD_MODULE_PROPERTIES  
};  

extern "C" {  
#ifdef COMPILE_DL_TEST_HELLO_WORLD  
    ZEND_GET_MODULE(test_hello_world)  
#endif  
}  

PHP_MINIT_FUNCTION(test_hello_world)  
{  
    return SUCCESS;  
}     

PHP_MSHUTDOWN_FUNCTION(test_hello_world)  
{  
    return SUCCESS;  
}     

PHP_MINFO_FUNCTION(test_hello_world)  
{  
    php_info_print_table_start();  
    php_info_print_table_header(2, "test_hello_world support", "enabled");  
    php_info_print_table_end();  
}     

PHP_FUNCTION(hello_world)  
{  
    string ret("hello world");  
    RETURN_STRING(const_cast(ret.c_str()), 1);  
}  

config.m4
PHP_ARG_ENABLE(test_hello_world, whether to enable test_hello_world support,  
Make sure that the comment is aligned:  
[  --enable-test_hello_world           Enable test_hello_world support])  

if test "$PHP_TEST_HELLO_WORLD" != "no"; then  
    PHP_SUBST(TEST_HELLO_WORLD_LIBADD)  
    PHP_NEW_EXTENSION(test_hello_world, test_hello_world.cc, $ext_shared)  
    PHP_REQUIRE_CXX  

    PHP_ADD_INCLUDE(/usr/include)  
    PHP_ADD_LIBRARY_WITH_PATH(stdc++, /usr/lib, TEST_HELLO_WORLD_LIBADD)  
    OS=`uname`;  
    if test $OS = "FreeBSD"; then  
        CPPFLAGS="$CPPFLAGS -Wno-error -Wall -g"
    else  
        CPPFLAGS="$CPPFLAGS -Wno-error -Wall -g -m32 -lstdc++"
    fi  
fi  

編譯
$ phpize

$ ./configure

$ make
如果編譯成功,可以看到如下畫面



將設定檔放入下列路徑/etc/php5/conf.d/test_hello_world.ini,php才知道需要載入此模組
extension = test_hello_world.so

將編譯完成的.so放入php的library中
/usr/lib/php5/20090626+lfs/test_hello_world.so



重起apache服務
$ service apache2 restart

重新測試test.php
<?php
echo hello_world();
?>

若需要清除編譯結果,指令如下
$ make clean
$ phpize --clean











 
疑難排解
如果遇到錯誤訊息undefined symbol: _ZNSs4_Rep20_S_empty_rep_storageE

PHP Warning: PHP Startup: Unable to load dynamic library '/usr/lib/php5/20090626+lfs/yahoo_hello_world.so' - /usr/lib/php5/20090626+lfs/yahoo_hello_world.so: undefined symbol: _ZNSs4_Rep20_S_empty_rep_storageE in Unknown on line 0

解決方法
加上-lstdc++在CPPFLAGS中

參考: http://stackoverflow.com/questions/11939934/c-apache-module-fails-on-znss4-rep20-s-empty-rep-storagee