ホームページ >バックエンド開発 >PHPチュートリアル >PHP 拡張機能開発の概要、PHP 拡張機能の概要_PHP チュートリアル
TSRM/TSRM.hファイルには以下の定義があります
リーリーext/xsl/php_xsl.hにこんな箇所があります
リーリー1. メソッドを定義するときに TSRMLS_D (メソッドにパラメーターがない場合に使用します) または TSRMLS_DC (複数のパラメーターがある場合) を追加します。
2. TSRMLS_C (メソッドにパラメーターがない場合にこれを使用します) または TSRMLS_CC (パラメーターが 1 つある場合) を追加します。パラメータ以上のメソッドを呼び出すとき)
このように理解すべきです
最初の接尾辞文字 D は定義を意味します (つまり D=Define)、最初の接尾辞文字 C は呼び出しを意味します (つまり C=Call)、そして 2 番目の接尾辞文字 C はコンマを意味しますか? C=カンマ(カンマ)
リーリーつまり、1 つは仮パラメータで、もう 1 つは実際のパラメータです
こんな使い方もできます
リーリースレッドの安全性を確保するには、一般に tsrm_ls ポインター定義メソッドを使用することをお勧めします
TSRMLS_FETCH 呼び出しには一定の処理時間が必要です。これは 1 回の反復では目立ちませんが、スレッド数が増加し、TSRMLS_FETCH() を呼び出すポイントが増加すると、スケーリングによってこのボトルネックが明らかになります。したがって、慎重に使用してください。 注: C++ コンパイラとの互換性を確保するために、TSRMLS_FETCH() とすべての変数定義を指定されたブロック スコープの先頭 (他のステートメントの前) に配置してください。 TSRMLS_FETCH() マクロ自体はさまざまな方法で解析できるため、変数定義の最後の行として使用するのが最善です
PHP の最も一般的な 2 つの動作モードは WEB モードと CLI モードです。どちらのモードであっても、PHP は同じ原理で動作し、SAPI として実行されます。
1. ターミナルに php コマンドを入力すると、CLI が使用されます。
リクエストを完了するためにphpをサポートするWebサーバーのようなものです。リクエストが完了すると、制御がターミナルに戻ります。
2. Apacheをホストとして使用する場合、リクエストが来たとき、PHPはリクエストの完了をサポートします
Linux 上の C プログラムでは、メモリ アクセス エラーなどの理由でセグメント フォールトが発生することがよくあります。このとき、システム コア ダンプ機能がオンになっていると、その後、gdb を使用してメモリ イメージがハードディスクにダンプされます。 core ファイルを解析し、システムセグメンテーション障害発生時のスタック状況を復元します。これはプログラムのバグを見つけるのに非常に役立ちます。
システム コア ファイルのサイズ制限を表示するには、ulimit -a を使用します。システムが生成できるコア ファイルのサイズを設定するには、ulimit -c [kbytes] を使用します。
ulimit -c 0 はコア ファイルを生成しません
ulimit -c 100 は最大コア ファイルを 100k に設定します
ulimit -c unlimited はコア ファイル サイズを制限しません
手順:
1. セグメンテーション違反が発生した場合、ulimit -a (core file size (blocks, -c) 0) をチェックしますが、ファイルはありません。
2. 設定: ulimit -c unlimited はコア ファイル サイズを制限しません。プログラムを実行します。セグメンテーション違反が発生すると、コアに自動的に記録されます (php -f WorkWithArray.php)
4. そのファイル (-rw------1) leconte leconte 139264 01-06 22: 3 1 core.2065)
5. gdb を使用してプログラムを実行し、セグメンテーション違反のあるファイルを記録します。 (gdb ./test core.2065)
6. どの行が間違っているかを説明します。
多くのシステムのデフォルトのコア ファイル サイズは 0 です。シェル起動スクリプト /etc/bashrc または ~/.bashrc に ulimit -c コマンドを追加することで、コア ファイル サイズを指定できます。生成することが可能です。 さらに、/proc/sys/kernel/core_pattern にコアファイルのファイル名テンプレートを設定することもできます。詳細については、コアの公式マニュアルを参照してください。
リーリー
メンバー全員が sapi_globals_struct にいますじゃあ、こう呼んでいいですか
リーリー
このコードを感じてくださいリーリー
2. EG Executor Globals構造struct _zend_execution_globals
内のデータを取得します
リーリー
を使用して現在のスコープ内のシンボルテーブルを取得しますEG(symbol_table)
获取的是全局作用域中的符号表,使用EG(active_symbol_table)
リーリー
またはシンボルテーブルから $foo を検索しますリーリー
上記のコードでは、EG(active_symbol_table) == &EG(symbol_table)
4. PG() PHP グローバル変数。 php.ini が 1 つ以上の PHP グローバル構造をマップすることがわかっています。 (main/php_globals.h)
5. FG() ファイルのグローバル変数。ファイル I/O または関連するグローバル変数のほとんどのデータ ストリームは、標準の拡張出口構造にプラグインされます。 (ext/standard/file.h)
5. 変数の型と値を取得します
リーリー
いくつか種類があります
#define IS_NULL 0 #define IS_LONG 1 #define IS_DOUBLE 2 #define IS_BOOL 3 #define IS_ARRAY 4 #define IS_OBJECT 5 #define IS_STRING 6 #define IS_RESOURCE 7 #define IS_CONSTANT 8 #define IS_CONSTANT_ARRAY 9 #define IS_CALLABLE 10
php_printf()函数是内核对printf()函数的一层封装,我们可以像使用printf()函数那样使用它,以一个P结尾的宏的参数大多是*zval型变量。 此外获取变量类型的宏还有两个,分别是Z_TYPE和Z_TYPE_PP,前者的参数是zval型,而后者的参数则是**zval
比如gettype函数的实现
//开始定义php语言中的函数gettype PHP_FUNCTION(gettype) { //arg间接指向调用gettype函数时所传递的参数。是一个zval**结构 //所以我们要对他使用__PP后缀的宏。 zval **arg; //这个if的操作主要是让arg指向参数~ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) { return; } //调用Z_TYPE_PP宏来获取arg指向zval的类型。 //然后是一个switch结构,RETVAL_STRING宏代表这gettype函数返回的字符串类型的值 switch (Z_TYPE_PP(arg)) { case IS_NULL: RETVAL_STRING("NULL", 1); break; case IS_BOOL: RETVAL_STRING("boolean", 1); break; case IS_LONG: RETVAL_STRING("integer", 1); break; case IS_DOUBLE: RETVAL_STRING("double", 1); break; case IS_STRING: RETVAL_STRING("string", 1); break; case IS_ARRAY: RETVAL_STRING("array", 1); break; case IS_OBJECT: RETVAL_STRING("object", 1); break; case IS_RESOURCE: { char *type_name; type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(arg) TSRMLS_CC); if (type_name) { RETVAL_STRING("resource", 1); break; } } default: RETVAL_STRING("unknown type", 1); } }
获取变量的值,有这么多宏来获取
Long |
Boolean |
Double |
String value |
String length |
---|---|---|---|---|
<pre class="code">Z_LVAL( ) |
<pre class="code">Z_BVAL( ) |
<pre class="code">Z_DVAL( ) |
<pre class="code">Z_STRVAL( ) |
<pre class="code">Z_STRLEN( ) |
<pre class="code">Z_LVAL_P( ) |
<pre class="code">Z_BVAL_P( ) |
<pre class="code">Z_DVAL_P( ) |
<pre class="code">Z_STRVAL_P( ) |
<pre class="code">Z_STRLEN_P( ) |
<pre class="code">Z_LVAL_PP( ) |
<pre class="code">Z_BVAL_PP( ) |
<pre class="code">Z_DVAL_PP( ) |
<pre class="code">Z_STRVAL_PP( ) |
<pre class="code">Z_STRLEN_PP( ) |
HashTable |
Object |
Object properties |
Object class entry |
Resource value |
<pre class="code">Z_ARRVAL( ) |
<pre class="code">Z_OBJ( ) |
<pre class="code">Z_OBJPROP( ) |
<pre class="code">Z_OBJCE( ) |
<pre class="code">Z_RESVAL( ) |
<pre class="code">Z_ARRVAL_P( ) |
<pre class="code">Z_OBJ_P( ) |
<pre class="code">Z_OBJPROP_P( ) |
<pre class="code">Z_OBJCE_P( ) |
<pre class="code">Z_RESVAL_P( ) |
<pre class="code">Z_ARRVAL_PP( ) |
<pre class="code">Z_OBJ_PP( ) |
<pre class="code">Z_OBJPROP_PP( ) |
<pre class="code">Z_OBJCE_PP( ) |
<pre class="code">Z_RESVAL_PP( ) |
rot13函数的实现
PHP_FUNCTION(rot13) { zval **arg; char *ch, cap; int i; if (ZEND_NUM_ARGS( ) != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) { WRONG_PARAM_COUNT; } *return_value = **arg; zval_copy_ctor(return_value); convert_to_string(return_value); for(i=0, ch=return_value->value.str.val; i<return_value->value.str.len; i++, ch++) { cap = *ch & 32; *ch &= ~cap; *ch = ((*ch>='A') && (*ch<='Z') ? ((*ch-'A'+13) % 26 + 'A') : *ch) | cap; } }
要获取变量的值,也应该使用Zend定义的宏进行访问。对于简单的标量数据类型、Boolean,long,double, 使用Z_BVAL
, Z_LVAL
, Z_DVAL
void display_values(zval boolzv, zval *longpzv, zval **doubleppzv) { if (Z_TYPE(boolzv) == IS_BOOL) { php_printf("The value of the boolean is : %s\n", Z_BVAL(boolzv) ? "true" : "false"); } if(Z_TYPE_P(longpzv) == IS_LONG) { php_printf("The value of the long is: %ld\n", Z_LVAL_P(longpzv)); } if(Z_TYPE_PP(doubleppzv) == IS_DOUBLE) { php_printf("The value of the double is : %f\n", Z_DVAL_PP(doubleppzv)); } }
对于字符串类型,因为它含有两个字段char *
(Z_STRVAL) 和 int
(Z_STRLEN),因此需要用两个宏来进行取值,因为需要二进制安全的输出这个字符串
void display_string(zval *zstr) { if (Z_TYPE_P(zstr) != IS_STRING) { php_printf("The wronng datatype was passed!\n"); return ; } PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr)); }
因为数组在zval中是以HashTable形式存在的,因此使用Z_ARRVAL()
进行访问
void display_zval(zval *value) { switch (Z_TYPE_P(value)) { case IS_NULL: /* 如果是NULL,则不输出任何东西 */ break; case IS_BOOL: /* 如果是bool类型,并且true,则输出1,否则什么也不干 */ if (Z_BVAL_P(value)) { php_printf("1"); } break; case IS_LONG: /* 如果是long整型,则输出数字形式 */ php_printf("%ld", Z_LVAL_P(value)); break; case IS_DOUBLE: /* 如果是double型,则输出浮点数 */ php_printf("%f", Z_DVAL_P(value)); break; case IS_STRING: /* 如果是string型,则二进制安全的输出这个字符串 */ PHPWRITE(Z_STRVAL_P(value), Z_STRLEN_P(value)); break; case IS_RESOURCE: /* 如果是资源,则输出Resource #10 格式的东东 */ php_printf("Resource #%ld", Z_RESVAL_P(value)); break; case IS_ARRAY: /* 如果是Array,则输出Array5个字母! */ php_printf("Array"); break; case IS_OBJECT: php_printf("Object"); break; default: /* Should never happen in practice, * but it's dangerous to make assumptions */ php_printf("Unknown"); break; } }
一些类型转换函数
ZEND_API void convert_to_long(zval *op); ZEND_API void convert_to_double(zval *op); ZEND_API void convert_to_null(zval *op); ZEND_API void convert_to_boolean(zval *op); ZEND_API void convert_to_array(zval *op); ZEND_API void convert_to_object(zval *op); ZEND_API void _convert_to_string(zval *op ZEND_FILE_LINE_DC);
我们可以这样实例化
PHP_MINIT_FUNCTION(consts) //模块初始化时定义常量 { REGISTER_LONG_CONSTANT("CONSTS_MEANING_OF_LIFE", 42, CONST_CS | CONST_PERSISTENT); REGISTER_DOUBLE_CONSTANT("CONSTS_PI", 3.1415926, CONST_PERSISTENT); REGISTER_STRING_CONSTANT("CONSTS_NAME", "leon", CONST_CS|CONST_PERSISTENT); } PHP_RINIT_FUNCTION(consts) //每次请求时定义常量 { char buffer[40]; srand((int)time(NULL)); snprintf(buffer, sizeof(buffer), "%d", rand()); REGISTER_STRING_CONSTANT("CONSTS_RAND", estrdup(buffer), CONST_CS); return SUCCESS; }
常见的宏
/*注册LONG类型常量*/ #define REGISTER_LONG_CONSTANT(name, lval, flags) zend_register_long_constant((name), sizeof(name), (lval), (flags), module_number TSRMLS_CC) /*注册double类型常量*/ #define REGISTER_DOUBLE_CONSTANT(name, dval, flags) zend_register_double_constant((name), sizeof(name), (dval), (flags), module_number TSRMLS_CC) /*注册STRING类型常量*/ #define REGISTER_STRING_CONSTANT(name, str, flags) zend_register_string_constant((name), sizeof(name), (str), (flags), module_number TSRMLS_CC) /*注册STRING类型常量*/ #define REGISTER_STRINGL_CONSTANT(name, str, len, flags) zend_register_stringl_constant((name), sizeof(name), (str), (len), (flags), module_number TSRMLS_CC)
#php-fpm 生成 POST|GET|COOKIE|SERVER|ENV|REQUEST|FILES全局变量的流程 php_cgi_startup() -> php_module_startup() -> php_startup_auto_globals() -> 保存变量到symbol_table符号表 php_cgi_startup()在 fpm/fpm/fpm_main.c中定义 php_module_startup() 在main/main.c中定义 php_startup_auto_globals() 在main/php_variables.h中定义 zend_hash_update(&EG(symbol_table), "_GET", sizeof("_GET") + 1, &vars, sizeof(zval *), NULL); /* 读取$_SERVER变量 */ static PHP_FUNCTION(print_server_vars) { zval **val; if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) { RETURN_ZVAL(*val, 1, 0); }else{ RETURN_FALSE; } } /* 读取$_SERVER[$name] */ ZEND_BEGIN_ARG_INFO(print_server_var_arginfo, 0) ZEND_ARG_INFO(0, "name") ZEND_END_ARG_INFO() static PHP_FUNCTION(print_server_var) { char *name; int name_len; zval **val; HashTable *ht_vars = NULL; HashPosition pos; zval **ret_val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &name, &name_len) == FAILURE) { RETURN_NULL(); } if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) { ht_vars = Z_ARRVAL_PP(val); //此处需传入大于name长度+1的值,因为字符串值后面需要'\0' if (zend_hash_find(ht_vars, name, name_len+1, (void **)&ret_val) == SUCCESS) { RETURN_STRING(Z_STRVAL_PP(ret_val), 0); }else{ RETURN_NULL(); } }else{ RETURN_NULL(); } }
配置(config.m4)
SEARCH_PATH="/usr/local /usr" #lib搜索的目录 SEARCH_FOR="/include/curl/curl.h" #lib头文件的路径 if test -r $PHP_LIBS/$SEARCH_FOR; then LIBS_DIR=$PHP_LIBS else # search default path list AC_MSG_CHECKING([for libs files in default path]) for i in $SEARCH_PATH ; do if test -r $i/$SEARCH_FOR; then LIBS_DIR=$i #搜索到的lib的路径 AC_MSG_RESULT(found in $i) fi done fi /*验证lib是否存在*/ if test -z "$LIBS_DIR"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([Please reinstall the libs distribution]) fi /*编译的时候添加lib的include目录, -I/usr/include*/ PHP_ADD_INCLUDE($LIBS_DIR/include) LIBNAME=curl #lib名称 LIBSYMBOL=curl_version #lib的一个函数,用来PHP_CHECK_LIBRARY验证lib /*验证lib*/ PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, [ PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $LIBS_DIR/$PHP_LIBDIR, LIBS_SHARED_LIBADD) #编译的时候链接lib, -llibcurl AC_DEFINE(HAVE_LIBSLIB,1,[ ]) ],[ AC_MSG_ERROR([wrong libs lib version or lib not found]) ],[ -L$LIBS_DIR/$PHP_LIBDIR -lm ]) PHP_SUBST(LIBS_SHARED_LIBADD)
//这些宏都定义在Zend/zend_API.h文件里 #define RETVAL_RESOURCE(l) ZVAL_RESOURCE(return_value, l) #define RETVAL_BOOL(b) ZVAL_BOOL(return_value, b) #define RETVAL_NULL() ZVAL_NULL(return_value) #define RETVAL_LONG(l) ZVAL_LONG(return_value, l) #define RETVAL_DOUBLE(d) ZVAL_DOUBLE(return_value, d) #define RETVAL_STRING(s, duplicate) ZVAL_STRING(return_value, s, duplicate) #define RETVAL_STRINGL(s, l, duplicate) ZVAL_STRINGL(return_value, s, l, duplicate) #define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value) #define RETVAL_ZVAL(zv, copy, dtor) ZVAL_ZVAL(return_value, zv, copy, dtor) #define RETVAL_FALSE ZVAL_BOOL(return_value, 0) #define RETVAL_TRUE ZVAL_BOOL(return_value, 1) #define RETURN_RESOURCE(l) { RETVAL_RESOURCE(l); return; } #define RETURN_BOOL(b) { RETVAL_BOOL(b); return; } #define RETURN_NULL() { RETVAL_NULL(); return;} #define RETURN_LONG(l) { RETVAL_LONG(l); return; } #define RETURN_DOUBLE(d) { RETVAL_DOUBLE(d); return; } #define RETURN_STRING(s, duplicate) { RETVAL_STRING(s, duplicate); return; } #define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; } #define RETURN_EMPTY_STRING() { RETVAL_EMPTY_STRING(); return; } #define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; } #define RETURN_FALSE { RETVAL_FALSE; return; } #define RETURN_TRUE { RETVAL_TRUE; return; }
其实,除了这些标量类型,还有很多php语言中的复合类型我们需要在函数中返回,如数组和对象,我们可以通过RETVAL_ZVAL与RETURN_ZVAL来操作它们
hashTable的遍历函数
//基于long key的操作函数 zval *v3; MAKE_STD_ZVAL(v3); ZVAL_STRING(v3, "value3", 1); zend_hash_index_update(names, 0, &v3, sizeof(zval *), NULL);//按数字索引键更新HashTable元素的值 zval **v4; zend_hash_index_find(names, 1, &v4); //按数字索引获取HashTable元素的值 php_printf("v4 : "); PHPWRITE(Z_STRVAL_PP(v4), Z_STRLEN_PP(v4)); php_printf("\n"); ulong idx; idx = zend_hash_index_exists(names, 10);//按数字索引查找HashTable,如果找到返回 1, 反之则返回 0 zend_hash_index_del(names, 2); //按数字索引删除HashTable元素 //hashTable的遍历函数 zend_hash_internal_pointer_reset(names); //初始化hash指针 zend_hash_internal_pointer_reset_ex(names, &pos);//初始化hash指针,并付值给pos zend_hash_get_current_data(names, (void**) &val); //获取当前hash存储值,data should be cast to void**, ie: (void**) &data zend_hash_get_current_data_ex(names, (void**) &val, &pos) == SUCCESS; //获取当前hash存储值 zend_hash_get_current_key(names, &key, &klen, &index, 0) == HASH_KEY_IS_LONG zend_hash_get_current_key_ex(names, &key, &klen, &index, 0, &pos) == HASH_KEY_IS_LONG; //读取hashtable当前的KEY,返回值会有两种 HASH_KEY_IS_LONG | HASH_KEY_IS_STRING ,分别对应array("value"),array("key"=>"value")两种hashtable zend_hash_move_forward(names); zend_hash_move_forward_ex(names, &pos); //hash指针移至下一位 //HashTable长度 php_printf("%*carray(%d) {\n", depth * 2, ' ', zend_hash_num_elements(Z_ARRVAL_P(zv))
一个简单的函数
function hello_array_strings($arr) { if (!is_array($arr)) return NULL; printf("The array passed contains %d elements ", count($arr)); foreach($arr as $data) { if (is_string($data)) echo "$data "; } }
PHP内核实现
PHP_FUNCTION(hello_array_strings) { zval *arr, **data; HashTable *arr_hash; HashPosition pointer; int array_count; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) { RETURN_NULL(); } arr_hash = Z_ARRVAL_P(arr); array_count = zend_hash_num_elements(arr_hash); php_printf("The array passed contains %d elements ", array_count); for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) { if (Z_TYPE_PP(data) == IS_STRING) { PHPWRITE(Z_STRVAL_PP(data), Z_STRLEN_PP(data)); php_printf(" "); } } RETURN_TRUE; }
参考文章
http://php.find-info.ru/php/016/ch23lev1sec1.html
http://docstore.mik.ua/orelly/webprog/php/ch14_10.htm
http://wiki.jikexueyuan.com/project/extending-embedding-php/2.4.html
http://www.php-internals.com/book/?p=chapt02/02-03-02-opcode
http://blog.csdn.net/phpkernel/article/details/5721134
https://github.com/chenpingzhao/php-ext-trie-filter/blob/master/trie_filter.c
http://aicode.cc/tags/php%E6%89%A9%E5%B1%95/
http://www.phpboy.net/2013-12/28-php-stream-wrapper.html
http://thiniki.sinaapp.com/?p=163
http://top.jobbole.com/26400/
https://github.com/walu/phpbook/blob/master/9.1.md