목차
PHP扩展开发教程(总结),php扩展教程总结
php教程 php手册 PHP扩展开发教程(总结),php扩展教程总结

PHP扩展开发教程(总结),php扩展教程总结

Jun 13, 2016 am 08:52 AM
php 확장된 개발 지도 시간

PHP扩展开发教程(总结),php扩展教程总结

PHP是一种解释型的语言,对于用户而言,我们精心的控制内存意味着easier prototyping和更少的崩溃!当我们深入到内核之后,所有的安全防线都已经被越过,最终还是要依赖于真正有责任心的软件工程师来保证系统的稳定运行。

1、线程安全宏定义

在TSRM/TSRM.h文件中有如下定义

#define TSRMLS_FETCH()       void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
#define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = (void ***) ctx
#define TSRMLS_SET_CTX(ctx)   ctx = (void ***) tsrm_ls
#define TSRMG(id, type, element)   (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#define TSRMLS_D   void ***tsrm_ls
#define TSRMLS_DC  , TSRMLS_D
#define TSRMLS_C   tsrm_ls
#define TSRMLS_CC  , TSRMLS_C

在ext/xsl/php_xsl.h有这么一段话

/* In every utility function you add that needs to use variables.                                                                   
   in php_xsl_globals, call TSRM_FETCH(); after declaring other.
   variables used by that function, or better yet, pass in TSRMLS_CC
   after the last function argument and declare your utility function
   with TSRMLS_DC after the last declared argument.  Always refer to
   the globals in your function as XSL_G(variable).  You are.
   encouraged to rename these macros something shorter, see
   examples in any other php module directory.
*/

1.在方法定义时加上TSRMLS_D(如果方法没有参数用这个)或者TSRMLS_DC(有1个以上的参数)

2.在方法调用时用TSRMLS_C(如果方法没有参数用这个)或者TSRMLS_CC(有1个以上的参数)

应该可以这样理解

第一个后缀字母D表示定义,即D=Define,第一个后缀字母C表示调用,即C=Call,而第二个后缀字母C是不是表示逗号呢? C=Comma (逗号)

TSRMLS_D就是定义了,所以是  void ***tsrm_ls

TSRMLS_DC是带逗号的定义,所以是 , void ***tsrm_ls

TSRMLS_C是调用,即tsrm_ls

TSRMLS_CC是调用并带逗号,即 ,tsrm_ls

所以一个是形参、一个是实参

可以这样使用

int php_myext_action(int action_id, char *message TSRMLS_DC);
php_myext_action(42, "The meaning of life" TSRMLS_CC);

一般推荐使用tsrm_ls指针定义的方式来保证线程安全

TSRMLS_FETCH调用需要一定的处理时间。这在单次迭代中并不明显,但是随着你的线程数增多,随着你调用TSRMLS_FETCH()的点的增多,你的扩展就会显现出这个瓶颈。因此,请谨慎的使用它。 注意:为了和c++编译器兼容,请确保将TSRMLS_FETCH()和所有变量定义放在给定块作用域的顶部(任何其他语句之前)。因为TSRMLS_FETCH()宏自身有多种不同的解析方式,因此最好将它作为变量定义的最后一行

2、PHP的生命周期

PHP的最多的两种运行模式是WEB模式、CLI模式,无论哪种模式,PHP工作原理都是一样的,作为一种SAPI运行。

1、当我们在终端敲入php这个命令的时候,它使用的是CLI。

它就像一个web服务器一样来支持php完成这个请求,请求完成后再重新把控制权交给终端。

2、当使用Apache作为宿主时,当一个请求到来时,PHP会来支持完成这个请求

PHP_MINIT_FUNCTION  初始化module时运行
PHP_MSHUTDOWN_FUNCTION  当module被卸载时运行
PHP_RINIT_FUNCTION  当一个REQUEST请求初始化时运行
PHP_RSHUTDOWN_FUNCTION  当一个REQUEST请求结束时运行
PHP_MINFO_FUNCTION  这个是设置phpinfo中这个模块的信息
PHP_GINIT_FUNCTION  初始化全局变量时
PHP_GSHUTDOWN_FUNCTION  释放全局变量时

比如PHP_GINIT_FUNCTION

PHP_GINIT_FUNCTION(test)
{
  /** 初始化全局变量 */
}
//对应的C代码
void zm_globals_ctor_test (zend_test_globals *test_globals TSRMLS_DC)
{
  /** 初始化全局变量 */
}
//在线程退出时,需要将之前自己申请的资源释放时,可以使用 PHP_GSHUTDOWN_FUNCTION来注册析构函数。
PHP_GSHUTDOWN_FUNCTION(test)
{
  /** 清除全局变量 */
}
//对应的C代码
void zm_globals_dtor_test (zend_test_globals *test_globals TSRMLS_DC)
{
  /** 清除全局变量 */
}
로그인 후 복사

这里有一段代码,可以测试一下

int minit_time;
PHP_MINIT_FUNCTION(test)
{
  minit_time = time(NULL);
  return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(test)
{
  FILE *fp=fopen("mshutdown.txt","a+");
  fprintf(fp,"%ld\n",time(NULL));
  fclose(fp);
  return SUCCESS;
}
int rinit_time;
PHP_RINIT_FUNCTION(test)
{
  rinit_time = time(NULL);
  return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(test)
{
  FILE *fp=fopen("rshutdown.txt","a+");
  fprintf(fp,"%ld\n",time(NULL));
  fclose(fp);
  return SUCCESS;
}
PHP_MINFO_FUNCTION(test)
{
  php_info_print_table_start();
  php_info_print_table_header(, "module info", "enabled");
  php_info_print_table_end();
  /* Remove comments if you have entries in php.ini
  DISPLAY_INI_ENTRIES();
  */
}
PHP_FUNCTION(test)
{
  php_printf("%d",time_of_minit);
  php_printf("%d",time_of_rinit);
  return;
}
로그인 후 복사

3、段错误调试

Linux下的C程序常常会因为内存访问错误等原因造成segment fault(段错误)此时如果系统core dump功能是打开的,那么将会有内存映像转储到硬盘上来,之后可以用gdb对core文件进行分析,还原系统发生段错误时刻的堆栈情况。这对于我们发现程序bug很有帮助。
使用ulimit -a可以查看系统core文件的大小限制;使用ulimit -c [kbytes]可以设置系统允许生成的core文件大小。

ulimit -c 0 不产生core文件
ulimit -c 100 设置core文件最大为100k
ulimit -c unlimited 不限制core文件大小

步骤:

1、当发生段错误时,我们查看ulimit -a (core file size (blocks, -c) 0)并没有文件,
2、设置 :ulimit -c unlimited 不限制core文件大小
3、运行程序 ,发生段错误时会自动记录在core中 (php -f WorkWithArray.php)
4、ls -al core.* 在那个文件下(-rw------- 1 leconte leconte 139264 01-06 22:3 1 core.2065)
5、使用gdb 运行程序和段错误记录的文件。(gdb ./test core.2065)
6、会提哪行有错。

很多系统默认的core文件大小都是0,我们可以通过在shell的启动脚本/etc/bashrc或者~/.bashrc等地方来加入 ulimit -c 命令来指定core文件大小,从而确保core文件能够生成。
除此之外,还可以在/proc/sys/kernel/core_pattern里设置core文件的文件名模板,详情请看core的官方man手册。

4、常见的变量操作宏

CG -> Complier Global 编译时信息,包括函数表等(zend_globals_macros.h:32)
EG -> Executor Global 执行时信息(zend_globals_macros.h:43)
PG -> PHP Core Global 主要存储php.ini中的信息
SG -> SAPI Global SAPI信息

1、SG 针对SAPI信息 在main/SAPI.h文件中

typedef struct _sapi_globals_struct {
  void *server_context;
  sapi_request_info request_info;
  sapi_headers_struct sapi_headers;
  int read_post_bytes;
  unsigned char headers_sent;
  struct stat global_stat;
  char *default_mimetype;
  char *default_charset;
  HashTable *rfc1867_uploaded_files;
  long post_max_size;
  int options;
  zend_bool sapi_started;
  double global_request_time;
  HashTable known_post_content_types;
  zval *callback_func;
  zend_fcall_info_cache fci_cache;
  zend_bool callback_run;
} sapi_globals_struct;
로그인 후 복사

看一下SG的定义

BEGIN_EXTERN_C()
#ifdef ZTS
# define SG(v) TSRMG(sapi_globals_id, sapi_globals_struct *, v)
SAPI_API extern int sapi_globals_id;
#else
# define SG(v) (sapi_globals.v)
extern SAPI_API sapi_globals_struct sapi_globals;
#endif
SAPI_API void sapi_startup(sapi_module_struct *sf);
SAPI_API void sapi_shutdown(void);
SAPI_API void sapi_activate(TSRMLS_D);
SAPI_API void sapi_deactivate(TSRMLS_D);
SAPI_API void sapi_initialize_empty_request(TSRMLS_D);
END_EXTERN_C()

成员都在sapi_globals_struct这里了

那么我么可以这样调用

SG(default_mimetype)
SG(request_info).request_uri

可以感受一下这么一段代码

static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
{
  char buf[SAPI_CGI_MAX_HEADER_LENGTH];
  sapi_header_struct *h;
  zend_llist_position pos;
  long rfc2616_headers = 0;
  if(SG(request_info).no_headers == 1) {
    return SAPI_HEADER_SENT_SUCCESSFULLY;
  }
  if (SG(sapi_headers).http_response_code != 200) {
    int len;
    len = sprintf(buf, "Status: %d\r\n", SG(sapi_headers).http_response_code);
    PHPWRITE_H(buf, len);
  }
  if (SG(sapi_headers).send_default_content_type) {
    char *hd;
    hd = sapi_get_default_content_type(TSRMLS_C);
    PHPWRITE_H("Content-type:", sizeof("Content-type: ")-1);
    PHPWRITE_H(hd, strlen(hd));
    PHPWRITE_H("\r\n", 2);
    efree(hd);
  }
  h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
  while (h) {
    PHPWRITE_H(h->header, h->header_len);
    PHPWRITE_H("\r\n", 2);
    h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  }
  PHPWRITE_H("\r\n", 2);
  return SAPI_HEADER_SENT_SUCCESSFULLY;
}
로그인 후 복사

2、EG Executor Globals

EG获取的是struct _zend_execution_globals结构体中的数据

struct _zend_execution_globals {
 ...
 HashTable symbol_table;  /* 全局作用域,如果没有进入函数内部,全局=活动 */
 HashTable *active_symbol_table; /* 活动作用域,当前作用域 */
 ...
}
로그인 후 복사

通常,使用EG(symbol_table)获取的是全局作用域中的符号表,使用EG(active_symbol_table)获取的是当前作用域下的符号表

例如 来定义$foo = 'bar'

zval *fooval;

MAKE_STD_ZVAL(fooval);
ZVAL_STRING(fooval, "bar", 1);
ZEND_SET_SYMBOL(EG(active_symbol_table), "foo", fooval);

或者从符号表中查找$foo

zval **fooval;
if(zend_hash_find(&EG(symbol_table), "foo", sizeof("foo"), (void **)&fooval) == SUCCESS) {
RETURN_STRINGL(Z_STRVAL_PP(fooval), Z_STRLEN_PP(fooval));
} else {
RETURN_FALSE;
}

上面的代码中,EG(active_symbol_table) == &EG(symbol_table)

3、CG() 用来访问核心全局变量。(zend/zend_globals_macros.h)

4、PG() PHP全局变量。我们知道php.ini会映射一个或者多个PHP全局结构。(main/php_globals.h)

5、FG() 文件全局变量。大多数文件I/O或相关的全局变量的数据流都塞进标准扩展出口结构。(ext/standard/file.h)

5、获取变量的类型和值

#define Z_TYPE(zval) (zval).type
#define Z_TYPE_P(zval_p) Z_TYPE(*zval_p)
#define Z_TYPE_PP(zval_pp) Z_TYPE(**zval_pp)

比如获取一个变量的类型

void describe_zval(zval *foo)
{
  if ( Z_TYPE_P(foo) == IS_NULL )
  {
    php_printf("这个变量的数据类型是: NULL");
  }
  else
  {
    php_printf("这个变量的数据类型不是NULL,这种数据类型对应的数字是: %d", Z_TYPE_P(foo));
  }
}
로그인 후 복사

有这么几种类型

#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

Z_LVAL( )
로그인 후 복사
Z_BVAL( )
로그인 후 복사
Z_DVAL( )
로그인 후 복사
Z_STRVAL( )
로그인 후 복사
Z_STRLEN( )
로그인 후 복사
Z_LVAL_P( )
로그인 후 복사
Z_BVAL_P( )
로그인 후 복사
Z_DVAL_P( )
로그인 후 복사
Z_STRVAL_P( )
로그인 후 복사
Z_STRLEN_P( )
로그인 후 복사
Z_LVAL_PP( )
로그인 후 복사
Z_BVAL_PP( )
로그인 후 복사
Z_DVAL_PP( )
로그인 후 복사
Z_STRVAL_PP( )
로그인 후 복사
Z_STRLEN_PP( )
로그인 후 복사

HashTable

Object

Object properties

Object class entry

Resource value

Z_ARRVAL( )
로그인 후 복사
Z_OBJ( )
로그인 후 복사
Z_OBJPROP( )
로그인 후 복사
Z_OBJCE( )
로그인 후 복사
Z_RESVAL( )
로그인 후 복사
Z_ARRVAL_P( )
로그인 후 복사
Z_OBJ_P( )
로그인 후 복사
Z_OBJPROP_P( )
로그인 후 복사
Z_OBJCE_P( )
로그인 후 복사
Z_RESVAL_P( )
로그인 후 복사
Z_ARRVAL_PP( )
로그인 후 복사
Z_OBJ_PP( )
로그인 후 복사
Z_OBJPROP_PP( )
로그인 후 복사
Z_OBJCE_PP( )
로그인 후 복사
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') &#63; ((*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) &#63; "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;
}
로그인 후 복사

以上所述就是本文给大家介绍的PHP扩展开发教程,希望大家喜欢。

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

<gum> : Bubble Gum Simulator Infinity- 로얄 키를 얻고 사용하는 방법
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
Nordhold : Fusion System, 설명
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora : 마녀 트리의 속삭임 - Grappling Hook 잠금 해제 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

PHP vs. Python : 차이점 이해 PHP vs. Python : 차이점 이해 Apr 11, 2025 am 12:15 AM

PHP와 Python은 각각 고유 한 장점이 있으며 선택은 프로젝트 요구 사항을 기반으로해야합니다. 1.PHP는 간단한 구문과 높은 실행 효율로 웹 개발에 적합합니다. 2. Python은 간결한 구문 및 풍부한 라이브러리를 갖춘 데이터 과학 및 기계 학습에 적합합니다.

PHP : 웹 개발의 핵심 언어 PHP : 웹 개발의 핵심 언어 Apr 13, 2025 am 12:08 AM

PHP는 서버 측에서 널리 사용되는 스크립팅 언어이며 특히 웹 개발에 적합합니다. 1.PHP는 HTML을 포함하고 HTTP 요청 및 응답을 처리 할 수 ​​있으며 다양한 데이터베이스를 지원할 수 있습니다. 2.PHP는 강력한 커뮤니티 지원 및 오픈 소스 리소스를 통해 동적 웹 컨텐츠, 프로세스 양식 데이터, 액세스 데이터베이스 등을 생성하는 데 사용됩니다. 3. PHP는 해석 된 언어이며, 실행 프로세스에는 어휘 분석, 문법 분석, 편집 및 실행이 포함됩니다. 4. PHP는 사용자 등록 시스템과 같은 고급 응용 프로그램을 위해 MySQL과 결합 할 수 있습니다. 5. PHP를 디버깅 할 때 error_reporting () 및 var_dump ()와 같은 함수를 사용할 수 있습니다. 6. 캐싱 메커니즘을 사용하여 PHP 코드를 최적화하고 데이터베이스 쿼리를 최적화하며 내장 기능을 사용하십시오. 7

PHP 및 Python : 두 가지 인기있는 프로그래밍 언어를 비교합니다 PHP 및 Python : 두 가지 인기있는 프로그래밍 언어를 비교합니다 Apr 14, 2025 am 12:13 AM

PHP와 Python은 각각 고유 한 장점이 있으며 프로젝트 요구 사항에 따라 선택합니다. 1.PHP는 웹 개발, 특히 웹 사이트의 빠른 개발 및 유지 보수에 적합합니다. 2. Python은 간결한 구문을 가진 데이터 과학, 기계 학습 및 인공 지능에 적합하며 초보자에게 적합합니다.

PHP 실행 : 실제 예제 및 응용 프로그램 PHP 실행 : 실제 예제 및 응용 프로그램 Apr 14, 2025 am 12:19 AM

PHP는 전자 상거래, 컨텐츠 관리 시스템 및 API 개발에 널리 사용됩니다. 1) 전자 상거래 : 쇼핑 카트 기능 및 지불 처리에 사용됩니다. 2) 컨텐츠 관리 시스템 : 동적 컨텐츠 생성 및 사용자 관리에 사용됩니다. 3) API 개발 : 편안한 API 개발 및 API 보안에 사용됩니다. 성능 최적화 및 모범 사례를 통해 PHP 애플리케이션의 효율성과 유지 보수 성이 향상됩니다.

PHP의 지속적인 관련성 : 여전히 살아 있습니까? PHP의 지속적인 관련성 : 여전히 살아 있습니까? Apr 14, 2025 am 12:12 AM

PHP는 여전히 역동적이며 현대 프로그래밍 분야에서 여전히 중요한 위치를 차지하고 있습니다. 1) PHP의 단순성과 강력한 커뮤니티 지원으로 인해 웹 개발에 널리 사용됩니다. 2) 유연성과 안정성은 웹 양식, 데이터베이스 작업 및 파일 처리를 처리하는 데 탁월합니다. 3) PHP는 지속적으로 발전하고 최적화하며 초보자 및 숙련 된 개발자에게 적합합니다.

PHP와 Python : 다른 패러다임이 설명되었습니다 PHP와 Python : 다른 패러다임이 설명되었습니다 Apr 18, 2025 am 12:26 AM

PHP는 주로 절차 적 프로그래밍이지만 객체 지향 프로그래밍 (OOP)도 지원합니다. Python은 OOP, 기능 및 절차 프로그래밍을 포함한 다양한 패러다임을 지원합니다. PHP는 웹 개발에 적합하며 Python은 데이터 분석 및 기계 학습과 같은 다양한 응용 프로그램에 적합합니다.

PHP 대 기타 언어 : 비교 PHP 대 기타 언어 : 비교 Apr 13, 2025 am 12:19 AM

PHP는 특히 빠른 개발 및 동적 컨텐츠를 처리하는 데 웹 개발에 적합하지만 데이터 과학 및 엔터프라이즈 수준의 애플리케이션에는 적합하지 않습니다. Python과 비교할 때 PHP는 웹 개발에 더 많은 장점이 있지만 데이터 과학 분야에서는 Python만큼 좋지 않습니다. Java와 비교할 때 PHP는 엔터프라이즈 레벨 애플리케이션에서 더 나빠지지만 웹 개발에서는 더 유연합니다. JavaScript와 비교할 때 PHP는 백엔드 개발에서 더 간결하지만 프론트 엔드 개발에서는 JavaScript만큼 좋지 않습니다.

PHP 및 Python : 코드 예제 및 비교 PHP 및 Python : 코드 예제 및 비교 Apr 15, 2025 am 12:07 AM

PHP와 Python은 고유 한 장점과 단점이 있으며 선택은 프로젝트 요구와 개인 선호도에 달려 있습니다. 1.PHP는 대규모 웹 애플리케이션의 빠른 개발 및 유지 보수에 적합합니다. 2. Python은 데이터 과학 및 기계 학습 분야를 지배합니다.

See all articles