PHP擴展開發-內核執行流程與擴展結構

在開發擴展以前,最好了解下PHP內核的執行流程,PHP大概包括三個方面:

 

SAPIphp

Zend VMhtml

內部擴展linux

  • Zend VM是PHP的虛擬機,與JVM相似,都是各自語言的編譯/執行的核心。它們都會把各自的代碼先編譯爲一種中間代碼,PHP的一般叫 opcode,Java一般叫bytecode,不一樣的是PHP的opcode直接被Zend VM的執行單元調用對應的C函數執行,不會顯示保留下來(能夠cache保留),而Java一般是生成class文件保留下來。而這一點可能也是PHP interpreter的名稱的由來吧。其實相對嚴格的C/C++等編譯型語言,PHP和Java更多的是結合了編譯型和解釋性的風格。
  • SAPI能夠看做是Zend VM向外界提供編譯/執行PHP代碼服務的方式和規範。不管是做爲cli/cgi/fastcgi/apache_mod與其餘程序交互,仍是嵌入其餘語 言中如C/C++等,均可以經過SAPI的規範實現。它的一個重要數據結構就是sapi_module_struct(main/SAPI.h line 217)
  • 內部擴展部分能夠看做是搭建在Zend VM和SAPI之上的庫,爲PHP開發人員提供性能和易用性上的保證。Java的各類包/Python的各類模塊功能相似,不一樣的是PHP中爲了性能是用 C擴展來實現的,相似的在Java中能夠經過JNI來實現,Python中如_socket和_select(多路複用)都不是原生Python實現。

生命週期

關於各類SAPI或者PHP自己的生命週期,可能會和其餘組件如apache耦合,後續再細談。關於PHP擴展的生命週期,這裏借用一張圖。流程應該是很容易明白的,關於這個過程,網上也有不少資料,再也不贅述。咱們開發擴展須要注意的幾個地方也能夠對應到圖中的某些節點:apache

 

全局變量的定義,一般是zend_modulename_globalsapi

模塊的初始化,包括資源/類/常量/ini配置等模塊級的初始化安全

請求的初始化,包括與單次請求相關的一些初始化數據結構

請求的結束,清理單次請求相關的數據/內存多線程

模塊的卸載,清理模塊相關的數據/內存框架

基本上咱們要作的就是按照上面的流程,實現相關的內置函數,定義本身的資源/全局變量/類/函數等。值得注意的地方是在在嵌入其餘語言如Python或者被嵌入其餘組件如apache時,要當心多進程多線程相關的問題。socket

php-src/ext/ext_skel能夠生成PHP擴展的框架

 

./ext_skel --extname=myext
[tan@tan ~/software/needbak/php-5.5.20/ext 12:24]$==> ls myext/
config.m4  config.w32  CREDITS  EXPERIMENTAL  myext.c  myext.php  php_myext.h  tests

比較重要的文件是config.m4(固然還有源碼),config.m4文件可使用phpize命令生成configure文件,其中說明了咱們是否開啓模塊,以及外部依賴的庫。

//config.m4
//若是你的擴展依賴其餘外部庫
dnl PHP_ARG_WITH(myext, for myext support,
dnl Make sure that the comment is aligned:
dnl [  --with-myext             Include myext support])

//擴展不依賴外部庫
dnl PHP_ARG_ENABLE(myext, whether to enable myext support,
dnl Make sure that the comment is aligned:
dnl [  --enable-myext           Enable myext support])

//尋找幷包含頭文件
if test "$PHP_MYEXT" != "no"; then
  dnl Write more examples of tests here...

  dnl # --with-myext -> check with-path
  dnl SEARCH_PATH="/usr/local /usr"     # you might want to change this
  dnl SEARCH_FOR="/include/myext.h"  # you most likely want to change this
  dnl if test -r $PHP_MYEXT/$SEARCH_FOR; then # path given as parameter
  dnl   MYEXT_DIR=$PHP_MYEXT
  dnl else # search default path list
  dnl   AC_MSG_CHECKING([for myext files in default path])
  dnl   for i in $SEARCH_PATH ; do
  dnl     if test -r $i/$SEARCH_FOR; then
  dnl       MYEXT_DIR=$i
  dnl       AC_MSG_RESULT(found in $i)
  dnl     fi
  dnl   done
  dnl fi
  dnl
  dnl if test -z "$MYEXT_DIR"; then
  dnl   AC_MSG_RESULT([not found])
  dnl   AC_MSG_ERROR([Please reinstall the myext distribution])
  dnl fi

  dnl # --with-myext -> add include path
  dnl PHP_ADD_INCLUDE($MYEXT_DIR/include)

  //加載的lib位置
  dnl # --with-myext -> check for lib and symbol presence
  dnl LIBNAME=myext # you may want to change this
  dnl LIBSYMBOL=myext # you most likely want to change this 

  dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
  dnl [
  dnl   PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $MYEXT_DIR/$PHP_LIBDIR, MYEXT_SHARED_LIBADD)
  dnl   AC_DEFINE(HAVE_MYEXTLIB,1,[ ])
  dnl ],[
  dnl   AC_MSG_ERROR([wrong myext lib version or lib not found])
  dnl ],[
  dnl   -L$MYEXT_DIR/$PHP_LIBDIR -lm
  dnl ])
  dnl
  dnl PHP_SUBST(MYEXT_SHARED_LIBADD)

  PHP_NEW_EXTENSION(myext, myext.c, $ext_shared)
fi
//php_myext.h
#ifndef PHP_MYEXT_H
#define PHP_MYEXT_H
extern zend_module_entry myext_module_entry;
#define phpext_myext_ptr &myext_module_entry
//導出符號,在連接的時候有用
#ifdef PHP_WIN32
#   define PHP_MYEXT_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#   define PHP_MYEXT_API __attribute__ ((visibility("default")))
#else
#   define PHP_MYEXT_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
//幾個核心函數的聲明
PHP_MINIT_FUNCTION(myext);
PHP_MSHUTDOWN_FUNCTION(myext);
PHP_RINIT_FUNCTION(myext);
PHP_RSHUTDOWN_FUNCTION(myext);
PHP_MINFO_FUNCTION(myext);
//自動生成的測試函數聲明,咱們本身定義的模塊函數須要在此聲明
PHP_FUNCTION(confirm_myext_compiled);   
//全局變量在這定義,展開後是zend_myext_globals結構體
ZEND_BEGIN_MODULE_GLOBALS(myext)
  long  global_value;
  char *global_string;
ZEND_END_MODULE_GLOBALS(myext)
//線程安全與非線程安全下獲取全局變量的方式
#ifdef ZTS
#define MYEXT_G(v) TSRMG(myext_globals_id, zend_myext_globals *, v)
#else
#define MYEXT_G(v) (myext_globals.v)
#endif
#endif  /* PHP_MYEXT_H */
//myext.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_myext.h"
//全局變量聲明
ZEND_DECLARE_MODULE_GLOBALS(myext)
/* True global resources - no need for thread safety here */
static int le_myext;
//模塊函數的導出
const zend_function_entry myext_functions[] = {
  PHP_FE(confirm_myext_compiled,  NULL)	   /* For testing, remove later. */
    PHP_FE_END  /* Must be the last line in myext_functions[] */
};
//模塊結構
zend_module_entry myext_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
  STANDARD_MODULE_HEADER,
#endif
  "myext",
  myext_functions,
  PHP_MINIT(myext),
  PHP_MSHUTDOWN(myext),
  PHP_RINIT(myext),	   /* Replace with NULL if there's nothing to do at request start */
  PHP_RSHUTDOWN(myext),   /* Replace with NULL if there's nothing to do at request end */
  PHP_MINFO(myext),
#if ZEND_MODULE_API_NO >= 20010901
  PHP_MYEXT_VERSION,
#endif
  STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_MYEXT
ZEND_GET_MODULE(myext)
#endif
//ini配置文件的設置
PHP_INI_BEGIN()
  STD_PHP_INI_ENTRY("myext.global_value",	  "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_myext_globals, myext_globals)
  STD_PHP_INI_ENTRY("myext.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_myext_globals, myext_globals)
PHP_INI_END()
//初始化全局變量
static void php_myext_init_globals(zend_myext_globals *myext_globals)
{
  myext_globals->global_value = 0;
  myext_globals->global_string = NULL;
}
//模塊加載時的函數
PHP_MINIT_FUNCTION(myext)
{
  /* If you have INI entries, uncomment these lines 
  REGISTER_INI_ENTRIES();
  */
  return SUCCESS;
}
//模塊卸載時函數
PHP_MSHUTDOWN_FUNCTION(myext)
{
  /* uncomment this line if you have INI entries
  UNREGISTER_INI_ENTRIES();
  */
  return SUCCESS;
}
//請求初始化函數
PHP_RINIT_FUNCTION(myext)
{
  return SUCCESS;
}
//請求關閉函數
PHP_RSHUTDOWN_FUNCTION(myext)
{
  return SUCCESS;
}
//模塊信息,phpinfo
PHP_MINFO_FUNCTION(myext)
{
  php_info_print_table_start();
  php_info_print_table_header(2, "myext support", "enabled");
  php_info_print_table_end();
  /* Remove comments if you have entries in php.ini
  DISPLAY_INI_ENTRIES();
  */
}
//測試函數
PHP_FUNCTION(confirm_myext_compiled)
{
  char *arg = NULL;
  int arg_len, len;
  char *strg;
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
    return;
  }
  len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "myext", arg);
  RETURN_STRINGL(strg, len, 0);
}
相關文章
相關標籤/搜索