ホームページ  >  記事  >  バックエンド開発  >  PHP 拡張機能開発の概要、PHP 拡張機能の概要_PHP チュートリアル

PHP 拡張機能開発の概要、PHP 拡張機能の概要_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-12 09:05:471153ブラウズ

PHP拡張開発の関連まとめ、PHP拡張の概要

1. スレッドセーフなマクロ定義

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() マクロ自体はさまざまな方法で解析できるため、変数定義の最後の行として使用するのが最善です

2. PHP のライフサイクル

PHP の最も一般的な 2 つの動作モードは WEB モードと CLI モードです。どちらのモードであっても、PHP は同じ原理で動作し、SAPI として実行されます。

1. ターミナルに php コマンドを入力すると、CLI が使用されます。
リクエストを完了するためにphpをサポートするWebサーバーのようなものです。リクエストが完了すると、制御がターミナルに戻ります。
2. Apacheをホストとして使用する場合、リクエストが来たとき、PHPはリクエストの完了をサポートします

リーリー

例: PHP_GINIT_FUNCTION

リーリー

これはコードの一部です。テストできます

リーリー

3. セグメンテーション障害のデバッグ

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 にコアファイルのファイル名テンプレートを設定することもできます。詳細については、コアの公式マニュアルを参照してください。

4. 共通変数操作マクロ

リーリー

1. SG は、main/SAPI.h ファイル内の SAPI 情報です

リーリー

SGの定義を見てください

リーリー

メンバー全員が sapi_globals_struct にいます

じゃあ、こう呼んでいいですか

リーリー

このコードを感じてください

リーリー

2. EG Executor Globals

EGは

構造struct _zend_execution_globals内のデータを取得します リーリー

通常、

を使用して現在のスコープ内のシンボルテーブルを取得しますEG(symbol_table)获取的是全局作用域中的符号表,使用EG(active_symbol_table)

たとえば、$foo = 'bar' を定義するには

リーリー

またはシンボルテーブルから $foo を検索します

リーリー

上記のコードでは、

EG(active_symbol_table) == &EG(symbol_table)

3. CG() は、コアのグローバル変数にアクセスするために使用されます。 (zend/zend_globals_macros.h)

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_BVALZ_LVALZ_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);

6、常量的实例化

我们可以这样实例化

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)

7、全局变量

#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();
    }
}

8、包装第三方库

配置(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)

9、用于返回的宏

//这些宏都定义在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来操作它们

 

10、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

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1067638.htmlTechArticlePHP扩展开发相关总结,php扩展总结 1、线程安全宏定义 在TSRM/TSRM.h文件中有如下定义 #define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0,...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。