日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

【php7扩展开发一】注册一个内部函数hello world

發布時間:2024/1/23 php 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【php7扩展开发一】注册一个内部函数hello world 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

通過擴展可以將C語言實現的函數提供給PHP腳本使用,如同大量PHP內置函數一樣,這些函數統稱為內部函數(internal function),與PHP腳本中定義的用戶函數不同,它們無需精力用戶函數的編譯過程,同時執行時也不像用戶函數那樣每一個指令都調用一次C語言編寫的handler函數,因此,內部函數的執行效率更高。除了性能上的優勢,內部函數還可以擁有更高的控制權限,可發揮的作用也更大,能夠完成很多用戶函數無法實現的功能。

函數通過zend_function來表示,這是一個聯合體,用戶函數使用zend_function.opoo_array, 內部函數使用zend_function.internal_function,兩者具有相同的頭部用來記錄函數的基本信心。不管是用戶函數還是內部函數,其最終都被注冊到EG(function_table)中,函數被調用時根據函數名稱向這個符號表中查找。從內部函數的注冊、使用過程可以看出,其定義實際非常簡單,我們只需要定義一個zend_internal_function結構,然后注冊到EG(function_table)中即可。

接下來再重新看下內部函數的結構:

// zend_compile.htypedef struct _zend_internal_function {/* Common elements */zend_uchar type;zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */uint32_t fn_flags;zend_string *function_name;zend_class_entry *scope;zend_function *prototype;uint32_t num_args;uint32_t required_num_args;zend_internal_arg_info *arg_info;/* END of common elements */void (*handler)(INTERNAL_FUNCTION_PARAMETERS);struct _zend_module_entry *module;void *reserved[ZEND_MAX_RESERVED_RESOURCES]; } zend_internal_function;

Common elements就是與用戶函數相同的頭部,用來記錄函數的基本信息:函數類型、參數信息、函數名等,handler是此內部函數的具體實現,PHP提供了一個宏用于此handler的定義:

PHP_FUNCTION(function_name)或ZEND_FUNCTION(),展開后:

void *zif_function_name(zend_execute_data *execute_data, zval *return_value) { ... }

PHP為函數名加了"zif_"前綴,gdb調試時記得加上這個前綴;另外內部函數定義了兩個參數:execute_data、return_value、execute_data不用說了,return_value是函數的返回值,這兩個值在擴展中會經常用到。

比如要在擴展中定義兩個函數:my_func_1()、my_func_2(),首先是編寫函數:

PHP_FUNCTION(my_func_1) {printf("Hello, I'm my_func_1\n"); } PHP_FUNCTION(my_func_2) {printf("hello, I'm my_func_2\n"); }

函數定義完了就需要向PHP注冊了,這里并不需要擴展自己注冊,PHP提供了一個內部函數注冊結構:zend_function_entry,擴展只需要為每個內部函數生成這樣一個結構,然后把它們保存到擴展zend_module_entry.functions即可,在加載擴展中會自動向EG(function_table)注冊。

typedef struct _zend_function_entry {const char *fname; //函數名稱void (*handler)(INTERNAL_FUNCTION_PARAMETERS); //handler實現const struct _zend_internal_arg_info *arg_info; //參數信息uint32_t num_args; //參數數目uint32_t flags; } zend_function_entry;

zend_function_entry結構可以通過PHP_FE()或ZEND_FE()定義:

const zend_function_entry mytest_functions[] = {PHP_FE(my_func_1, NULL)PHP_FE(my_func_2, NULL)PHP_FE_END //末尾必須加這個 };

用一張圖來簡單串一下數據結構的關系:

接下來進入正題我們要寫個擴展代替以下的功能:

<?phpfunction hello() {return 'hello world';}

我的開發環境是:

系統: Ubuntu 16.04

PHP: 7.0+

gcc: 4.8.4

PHP已經提供了工具用來創建擴展,并初始化代碼: ext_skel

$ cd php-src/ext # ./ext_skel --extname=hello

第一步修改配置文件 config.m4 (phpize用來準備編譯擴展的配置文件)

dnl PHP_ARG_WITH(hello, for hello support, dnl Make sure that the comment is aligned: dnl [ --with-hello Include hello support])dnl Otherwise use enable:PHP_ARG_ENABLE(hello, whether to enable hello support, Make sure that the comment is aligned: [ --enable-hello Enable hello support])

dnl是注釋符,表示當前行是注釋。說如果此擴展依賴其他擴展,去掉PHP_ARG_WITH段的注釋符;

否則去掉PHP_ARG_ENABLE段的注釋符。顯然我們不依賴其他擴展或lib庫,所以去掉PHP_ARG_ENABLE段的注釋符:

第二步? 在hello.c中添加我們需要的函數,然后加到編譯列表里

首先需要創建一個zend_module_entry結構,這個變量必須是全局變量,且變量名必須是: 擴展名稱_module_entry,內核通過這個結構得到這個擴展都提供了哪些功能,換句話說,一個擴展可以只包含一個zend_module_entry結構,相當于定義了一個什么功能都沒有的擴展。

//zend_modules.h struct _zend_module_entry {unsigned short size; //sizeof(zend_module_entry)unsigned int zend_api; //ZEND_MODULE_API_NOunsigned char zend_debug; //是否開啟debugunsigned char zts; //是否開啟線程安全const struct _zend_ini_entry *ini_entry;const struct _zend_module_dep *deps;const char *name; //擴展名稱,不能重復const struct _zend_function_entry *functions; //擴展提供的內部函數列表int (*module_startup_func)(INIT_FUNC_ARGS); //擴展初始化回調函數,PHP_MINIT_FUNCTION或ZEND_MINIT_FUNCTION定義的函數int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); //擴展關閉時回調函數int (*request_startup_func)(INIT_FUNC_ARGS); //請求開始前回調函數int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); //請求結束時回調函數void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); //php_info展示的擴展信息處理函數const char *version; //版本...unsigned char type;void *handle;int module_number; //擴展的唯一編號const char *build_id; }

這個結構包含很多成員,但并不是所有的都需要自己定義,經常用到的主要有下面幾個:

name: 擴展名稱,不能重復

functions: 擴展定義的內部函數entry

module_startup_func: PHP在模塊初始化時回調的hook函數,可以使擴展介入modulestartup階段

module_shutdown_func: 在模塊關閉階段回調的函數

request_startup_func: 在請求初始化階段回調的函數

request_shutdown_func: 在請求結束階段回調的函數

__info_func: __php_info()函數時調用,用于展示一些配置、運行信息

version:擴展

除了上面這些需要手動設置的成員,其他部分可以通過STANDARD_MODULE_HEADER,STANDARD_MODULE_PROPERTIES宏統一設置,擴展提供的內部函數及四個執行階段的鉤子函數是擴展最常用到的部分,幾乎所有的擴展都是基于這兩部分實現的。有了這個結構還需要提供一個接口來獲取這個結構變量,這個接口是統一的,擴展中通過ZEND_GET_MODULE(extension_name)完成這個接口的定義:

//zend_API.h #define ZEND_GET_MODULE(name) \ BEGIN_EXTERN_C() \ ZEND_DLEXPORT zend_module_entry *get_module(void) { return &name##_module_entry; }\ END_EXTERN_C

展開后可以看到,實際就是定義了一個get_module()函數,返回擴展zend_module_entry結構的地址,這就是為什么這個結構的變量名必須是 擴展名稱_module_entry這種格式的原因。

有了擴展的zend_module_entry結構以及獲取這個結構的接口一個合格的擴展就編寫完成了,只是這個擴展目前還什么都干不了:

zend_module_entry hello_module_entry = {STANDARD_MODULE_HEADER,"hello",hello_functions,PHP_MINIT(hello),PHP_MSHUTDOWN(hello),PHP_RINIT(hello), /* Replace with NULL if there's nothing to do at request start */PHP_RSHUTDOWN(hello), /* Replace with NULL if there's nothing to do at request end */PHP_MINFO(hello),PHP_HELLO_VERSION,STANDARD_MODULE_PROPERTIES };

寫上我們的實現:

PHP_FUNCTION(hello) {zend_string *strg;strg = strpprintf(0, "hello world.");RETURN_STR(strg); }

添加到編譯列表里:

const zend_function_entry hello_functions[] = {PHP_FE(confirm_hello_compiled, NULL) /* For testing, remove later */PHP_FE(hello, NULL)PHP_FE_END /* Must be the last line in hello_functions[] */ };

第三步?編譯與安裝

$ phpize $ ./configure --with-php-config=/usr/bin/php-config $ make & make install

phpize這個腳本主要是操作復雜的autoconf/automake/autoheader/autolocal等系列命令,用于生成configure文件,GNU auto系列的工具眾多,這里不做介紹了。

php-config這個腳本為PHP源碼中的/script/php-config.in,PHP安裝后被移到安裝路徑的/bin目錄下,并重命名為php-config,這個腳本主要是獲取PHP的安裝信息的,主要有:

PHP安裝路徑

PHP版本

PHP源碼的頭文件目錄:main、Zend、ext、TSRM中的頭文件,編寫擴展時會用到這些頭文件,這些頭文件保存在PHP安裝位置/include/php目錄下

LDFLAGS: 外部庫路徑,比如: -L/usr/bib -L/usr/local/lib

依賴的外部庫: 告訴編譯器要鏈接哪些文件,-lcrypt -lresolv -lcrypt等等

擴展存放目錄: 擴展.so保存位置,安裝擴展make install時將安裝到此路徑下

編譯的SAPI: 如cli、fpm、cgi等

PHP編譯參數: 執行./configure時帶的參數

執行./configure --with-php-config=xxx生成Makefile時作為參數傳入即可,它的作用是提供給configure.in獲取上面幾個配置,生成Makefile

第四步

修改php.ini,開啟擴展,若找不到可以用phpinfo()查看使用哪個配置文件。

extension = hello.so

寫個腳本: <?php echo hello();不出意外就能看到輸出了。

附上完整的hello.c, php_hello.h

/*+----------------------------------------------------------------------+| PHP Version 7 |+----------------------------------------------------------------------+| Copyright (c) 1997-2016 The PHP Group |+----------------------------------------------------------------------+| This source file is subject to version 3.01 of the PHP license, || that is bundled with this package in the file LICENSE, and is || available through the world-wide-web at the following url: || http://www.php.net/license/3_01.txt || If you did not receive a copy of the PHP license and are unable to || obtain it through the world-wide-web, please send a note to || license@php.net so we can mail you a copy immediately. |+----------------------------------------------------------------------+| Author: |+----------------------------------------------------------------------+ *//* $Id$ */#ifdef HAVE_CONFIG_H #include "config.h" #endif#include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_hello.h"/* If you declare any globals in php_hello.h uncomment this: ZEND_DECLARE_MODULE_GLOBALS(hello) *//* True global resources - no need for thread safety here */ static int le_hello;/* {{{ PHP_INI*/ /* Remove comments and fill if you need to have entries in php.ini PHP_INI_BEGIN()STD_PHP_INI_ENTRY("hello.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_hello_globals, hello_globals)STD_PHP_INI_ENTRY("hello.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_hello_globals, hello_globals) PHP_INI_END() */ /* }}} *//* Remove the following function when you have successfully modified config.m4so that your module can be compiled into PHP, it exists only for testingpurposes. *//* Every user-visible function in PHP should document itself in the source */ /* {{{ proto string confirm_hello_compiled(string arg)Return a string to confirm that the module is compiled in */ PHP_FUNCTION(confirm_hello_compiled) {char *arg = NULL;size_t arg_len, len;zend_string *strg;if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {return;}strg = strpprintf(0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "hello", arg);RETURN_STR(strg); }PHP_FUNCTION(hello) {zend_string *strg;strg = strpprintf(0, "hello world.");RETURN_STR(strg); }/* }}} */ /* The previous line is meant for vim and emacs, so it can correctly fold andunfold functions in source code. See the corresponding marks just beforefunction definition, where the functions purpose is also documented. Pleasefollow this convention for the convenience of others editing your code. *//* {{{ php_hello_init_globals*/ /* Uncomment this function if you have INI entries static void php_hello_init_globals(zend_hello_globals *hello_globals) {hello_globals->global_value = 0;hello_globals->global_string = NULL; } */ /* }}} *//* {{{ PHP_MINIT_FUNCTION*/ PHP_MINIT_FUNCTION(hello) {/* If you have INI entries, uncomment these linesREGISTER_INI_ENTRIES();*/return SUCCESS; } /* }}} *//* {{{ PHP_MSHUTDOWN_FUNCTION*/ PHP_MSHUTDOWN_FUNCTION(hello) {/* uncomment this line if you have INI entriesUNREGISTER_INI_ENTRIES();*/return SUCCESS; } /* }}} *//* Remove if there's nothing to do at request start */ /* {{{ PHP_RINIT_FUNCTION*/ PHP_RINIT_FUNCTION(hello) { #if defined(COMPILE_DL_HELLO) && defined(ZTS)ZEND_TSRMLS_CACHE_UPDATE(); #endifreturn SUCCESS; } /* }}} *//* Remove if there's nothing to do at request end */ /* {{{ PHP_RSHUTDOWN_FUNCTION*/ PHP_RSHUTDOWN_FUNCTION(hello) {return SUCCESS; } /* }}} *//* {{{ PHP_MINFO_FUNCTION*/ PHP_MINFO_FUNCTION(hello) {php_info_print_table_start();php_info_print_table_header(2, "hello support", "enabled");php_info_print_table_end();/* Remove comments if you have entries in php.iniDISPLAY_INI_ENTRIES();*/ } /* }}} *//* {{{ hello_functions[]** Every user visible function must have an entry in hello_functions[].*/ const zend_function_entry hello_functions[] = {PHP_FE(confirm_hello_compiled, NULL) /* For testing, remove later. */PHP_FE(hello, NULL)PHP_FE_END /* Must be the last line in hello_functions[] */ }; /* }}} *//* {{{ hello_module_entry*/ zend_module_entry hello_module_entry = {STANDARD_MODULE_HEADER,"hello",hello_functions,PHP_MINIT(hello),PHP_MSHUTDOWN(hello),PHP_RINIT(hello), /* Replace with NULL if there's nothing to do at request start */PHP_RSHUTDOWN(hello), /* Replace with NULL if there's nothing to do at request end */PHP_MINFO(hello),PHP_HELLO_VERSION,STANDARD_MODULE_PROPERTIES }; /* }}} */#ifdef COMPILE_DL_HELLO #ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE() #endif ZEND_GET_MODULE(hello) #endif/** Local variables:* tab-width: 4* c-basic-offset: 4* End:* vim600: noet sw=4 ts=4 fdm=marker* vim<600: noet sw=4 ts=4*/ /* $Id$ */#ifndef PHP_HELLO_H #define PHP_HELLO_Hextern zend_module_entry hello_module_entry; #define phpext_hello_ptr &hello_module_entry#define PHP_HELLO_VERSION "0.1.0" /* Replace with version number for your extension */#ifdef PHP_WIN32 # define PHP_HELLO_API __declspec(dllexport) #elif defined(__GNUC__) && __GNUC__ >= 4 # define PHP_HELLO_API __attribute__ ((visibility("default"))) #else # define PHP_HELLO_API #endif#ifdef ZTS #include "TSRM.h" #endif#if defined(ZTS) && defined(COMPILE_DL_HELLO) ZEND_TSRMLS_CACHE_EXTERN() #endif#endif /* PHP_HELLO_H *//** Local variables:* tab-width: 4* c-basic-offset: 4* End:* vim600: noet sw=4 ts=4 fdm=marker* vim<600: noet sw=4 ts=4*/

?

總結

以上是生活随笔為你收集整理的【php7扩展开发一】注册一个内部函数hello world的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。