7.PHP核心技术与最佳实践 --- PHP 扩展开发

本文详细讲解了PHP扩展开发的全过程,包括开发原因、环境搭建、生命周期解析、内核变量、HashTable结构、Zend API使用,以及如何创建变量、处理错误输出、调用用户函数等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

1.为什么要开发 PHP 扩展
	1.注重效率
	2.有些系统调用不能用 PHP 直接访问的
	3.不想暴露源码

2.windows 下
	php ext_skel_win32.php --extname=myext

3.Linux 下
	1.安装 php-dev 包
		使用 php-dev 包中的 phpize 工具可以减少很多繁琐的步骤。如果使用 php 源码编译的话,就不用按照 php-dev 包,因为源码中已经
	  有 phpize 工具。
	  apt-get install php5-dev
	  phpize -version

	2.使用 ext_skel 工具
		./ext_skel --extname=myext

To use your new extension, you will have to execute the following steps:

1.  $ cd ..
2.  $ vi ext/myext/config.m4
3.  $ ./buildconf
4.  $ ./configure --[with|enable]-myext
5.  $ make
6.  $ ./sapi/cli/php -f ext/myext/myext.php
7.  $ vi ext/myext/myext.c
8.  $ make

Repeat steps 3-6 until you are satisfied with ext/myext/config.m4 and
step 6 confirms that your module is compiled into PHP. Then, start writing
code and repeat the last two steps as often as necessary.
	生成扩展后,需要修改扩展的 m4 文件。打开 config.m4 文件,去掉以下配置前的 dnl :
	 16  PHP_ARG_ENABLE(myext, whether to enable myext support,
	 18  [  --enable-myext           Enable myext support])

	 3.创建好扩展之后,进入到扩展的目录下面,使用 phpize 命令生成扩展的配置工具,然后编译安装。
	 phpize
	 ./configure --with-php-config=/usr/local/php5/bin/php-config
	 make
	 make test
	 make install

	 4. 安装编译好之后,在 /usr/local/php5/lib/php/extensions/no-debug-zts-20060613 目录下看到生成的扩展文件 myext.so。
	 接着在 php.ini 中添加扩展信息。
	 extension=myext.so
	 使用 php -m 查看是否安装成功


4.PHP 的生命周期
	1.call each extension's MINIT
	这个过程在扩展被载入时调用。一般写在扩展的以下函数中 :
	PHP_MINIT_FUNCTION(myext)
	{
		//注册常量或者类等初始化操作
		return SUCCESS;
	}

	2.Request test.php
	请求 test.php 文件。当请求到达后,PHP 会初始化执行脚本的基本环境,例如创建一个执行环境,包括保存PHP运行过程中变量名称和变量值内容的符号
  表,以及当前所有的函数以及类等信息的符号表。然后PHP会调用所有的模块的 RINIT 函数,这个阶段各个模块也可以执行一些相关的操作,模块的RINIT函数
  和 MINIT 函数类似:
  	PHP_RINIT_FUNCTION(myext)
  	{
  		//例如记录请求开始时间
  		return SUCCESS;
  	}

  	3.Execute test.php
  	执行 test.php 阶段,主要是把 PHP 文件编译成 Opcodes, 然后在 PHP 虚拟机下执行。

  	4.Call each extension's RSHUTDOWN
  	情趣处理完后进入结束阶段,一般脚本执行到末尾或者通过调用 exit() 或者 die() 函数,PHP 都将进入结束阶段。和开始阶段对应,结束阶段也分为2个
  环节,一个在请求结束后(RSHUTDOWN),一个在 SAPI 生命周期结束时(MSHUTDOWN)。
    PHP_RSHUTDOWN_FUNCTION(myext)
    {
    	//例如记录请求结束时间
    	return SUCCESS;
    }

    PHP_MSHUTDOWN_FUNCTION(myext)
    {
    	//注销一些持久化资源
    	return SUCCESS;
    }


5.PHP 内核中的变量
	zval 结构体就是通常用到的 PHP 变量在内核中的表示方式。在 zval 结构体中,可以看到4个成员变量,分别是:
	1.zvalue_value value; //变量的值,PHP变量的值就保存在这里
	2.zend_uint refcount__gc; //表示引用计数
	3.zend_uchar type;	//变量具体的类型
	4.zend_uchar is_ref__gc;//表示是否为引用

	PHP 内核提供了一个飞吻和设置变量类型的方法:
	Z_TYPE(zval)  对应 zval 结构体的实体
	Z_TYPE_P(&zval)  对应 zval 结构体的指针
	Z_TYPE_PP(&&zval) 对应 zval 结构体的二级指针

	//可以用下面方法设置变量的类型
	Z_TYPE(zval) = IS_LONG;

	//用以下方法飞吻变量的类型
	if (Z_TYPE(zval) == IS_LONG) {
		printf("is long \n");
	}

	创建一个值为 10 的整数变量 lvar:
	zval lvar;
	Z_TYPE(lvar) = IS_LONG;
	Z_LVAL(lvar) = 10;
	如果用 PHP 脚本的话,相当于以下代码:
	$lvar = 10;

	2.引用计数器与写时复制
		PHP 是不支持指针的,为了解决这个问题,引入了引用计数器。
	$a = 'huang su hong';
	xdebug_debug_zval('a');
	$b = &$a;
	xdebug_debug_zval('a');

6.内核中的 HashTable
	HashTable 是 php 的灵魂,因为在 zend 引擎中大量使用了 HashTable,如变量表,常量表,函数表等,这些都是使用 HashTable 保存,另外
  PHP 数组也是使用 HashTable 实现的。

 typedef struct bucket {
	ulong h;			// 对char *key进行hash后的值,或者是用户指定的数字索引值			/* Used for numeric indexing */
	uint nKeyLength;// hash关键字的长度,如果数组索引为数字,此值为0
	void *pData;// 指向value,一般是用户数据的副本,如果是指针数据,则指向pDataPtr
	void *pDataPtr;//如果是指针数据,此值会指向真正的value,同时上面pData会指向此值
	struct bucket *pListNext;// 整个hash表的下一元素
	struct bucket *pListLast;// 整个哈希表该元素的上一个元素
	struct bucket *pNext; // 存放在同一个hash Bucket内的下一个元素
	struct bucket *pLast; // 同一个哈希bucket的上一个元素
	const char *arKey;// 保存当前值所对于的key字符串,这个字段只能定义在最后,实现变长结构体
} Bucket;


	一个 Bucket 结构只能保存一个数据,而 HashTable 的目的就是通过索引(key)把每个元素分散到唯一的位置(没有冲突的时候)。这是怎么做到的呢?答案就是,
  通过 hash 算法把索引处理成一个 int 的数,然后定位到一个 Bucket 数组的其中一个元素中。
    PHP 内核通过 HashTable 结构管理 Bucket 数组。

    typedef struct _hashtable {
	uint nTableSize; // hash Bucket的大小,最小为8,以2x增长。
	uint nTableMask; // nTableSize-1 , 索引取值的优化
	uint nNumOfElements;//hash Bucket中当前存在的元素个数,count()函数会直接返回此值
	ulong nNextFreeElement;//下一个数字索引的位置
	Bucket *pInternalPointer;	/* Used for element traversal */// 当前遍历的指针(foreach比for快的原因之一)
	Bucket *pListHead;// 存储数组头元素指针
	Bucket *pListTail;// 存储数组尾元素指针
	Bucket **arBuckets; // 存储hash数组
	dtor_func_t pDestructor;// 在删除元素时执行的回调函数,用于资源的释放
	zend_bool persistent;//指出了Bucket内存分配的方式。如果persisient为TRUE,则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用PHP的内存分配函数。
	unsigned char nApplyCount;// 标记当前hash Bucket被递归访问的次数(防止多次递归)
	zend_bool bApplyProtection;// 标记当前hash桶允许不允许多次访问,不允许时,最多只能递归3次
#if ZEND_DEBUG
	int inconsistent;
#endif
} HashTable;


7.Zend API 详解与扩展编写
	1.什么是 zend 引擎
		zend 引擎是脚本语言引擎(解析器+虚拟器),主要的工作就是解析,翻译和执行php 脚本。

		zend 引擎的流程:
		PHP脚本 => zend Engine Compiler => 编译 => Opcodes => Zend Engine Excutor => 解析

		zend 引擎2个主要工作:
		1.编译 php 脚本
		2.解析执行 Opcodes,输出结果
		在解析执行的过程中 zend 引擎可以调用到所有已经载入到 php 环境的扩展库。

	2.zend 引擎内存管理

	3.PHP 扩展架构
		1.包含头文件
		2.声明导出函数
		3.声明 zend 函数块
		4.声明 zend 模块
		5.实现 get_module() 函数
		6.实现导出函数

		上面提到的这些结构在 php 扩展中都是必不可少的,php内核就是通过这些约定的规矩与扩展通信的。

		1.声明导出函数:
		  编写扩展的目的是能够让 php 脚本调用扩展中的函数和类,那么怎么才能够php脚本调用编写的扩展呢?答案就是声明和实现 '导出函数'.
		那什么是 ‘导出函数’呢 : 导出函数就是按照 php 内核规定的标准编写的函数。形式如下:
		void zif_ext_function (
			int ht,
			zval *return_value,
			zval *this_ptr,
			int return_value_used,
			zend_executor_globals *executor_globals
		)

		php 脚本中就可以使用下面代码调用上面函数:
		<?php
			ext_function(...);

		因为导出函数的固定的,所以 zend 引擎提供一个方面的宏声明,具体如下:
		ZEND_FUNCTION(functioin_name);
		function_name 就是在 php 脚本中调用的函数名。ZEND_FUNCTION 宏会把导出函数补充成以下形式:
		void zif_function_name (
			int ht,
			zval *return_value,
			zval *this_ptr,
			int return_value_used,
			zend_executor_globals *executor_globals
		)
		导出函数是没有返回值的。
			导出函数的参数和作用:
			1.ht 保存扩展函数参数的个数。但是不应该直接访问这个值,而是通过 ZEND_NUM_ARGS()宏获取。
			2.return_value 用来保存扩展函数向 php 脚本返回的值
			3.this_ptr 根据这个参数可以访问该函数所在的对象
			4.return_value_used 用来标识函数的返回值是否为脚本所用。0表示脚本不适用其他返回值,1相反。
			5.executor_globals 指向 zend 引擎的全局设置,在创建新变量的时候有用。

		2.声明 zend 函数块:
			已经知道怎么声明导出函数,但 zend 引擎是不会自动引入声明的导出函数的。那怎么才能把编写的函数引入到zend 引擎呢.答案就是
		zend_function_entry 结构体。

		typedef struct _zend_function_entry {
			const char *fname;
			void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
			const struct _zend_arg_info *arg_info;
			zend_uint num_args;
			zend_uint flags;
		} zend_function_entry;

		zend 引擎就是通过 zend_function_entry 结构数组把声明的导出函数导入内部。zend 引擎在载入扩展时,主动把这个数组中的所有函数
	  引入到函数表中,这样 php 脚本就可以调用这些函数了。

	    3.声明 zend 模块
	      php 扩展信息被保存在 zend_module_entry 结构体中,这个结构体包含所有需要向 zend 引起提供的模块信息。

struct _zend_module_entry {
	unsigned short size;
	unsigned int zend_api;
	unsigned char zend_debug;
	unsigned 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);
	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);
	const char *version;
	size_t globals_size;
#ifdef ZTS
	ts_rsrc_id* globals_id_ptr;
#else
	void* globals_ptr;
#endif
	void (*globals_ctor)(void *global TSRMLS_DC);
	void (*globals_dtor)(void *global TSRMLS_DC);
	int (*post_deactivate_func)(void);
	int module_started;
	unsigned char type;
	void *handle;
	int module_number;
	const char *build_id;
};

	4.实现 get_module() 函数
		ZEND_GET_MODULE(myext) 展开后就是 get_module 函数了, 这个函数返回一个 zend_module_entry 指针。这个指针就是 zend 模块。
	  这样 php 内核就可以通过调研 get_module() 函数取得编写的扩展信息了。php 内核和扩展通信的渠道就是 get_module() 函数。

	5.实现导出函数
		实现导出函数是构建扩展的最后一步。导出函数可以在 php 脚本中调用的函数。
		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);
		}

		实现导出函数跟声明导出函数一样都是使用 PHP_FUNCTION 宏定义一个导出函数,然后在函数体中实现想要的功能。
		在声明的 zend 函数块中的所有函数都必须实现,不然会出现错误。例如,在 zend 函数块中声明一个 ZEND_FE(show_message, NULL) 函数,就必须实现
	  ZEND_FUNCTION(show_message) 函数。

	  6.接收用户传递的参数
	  	1.取得参数
	  		ZEND_NUM_ARGS

	  	PHP 内核在传递参数的时候也使用引用计数的方式,所以不管是否使用了引用传递参数,它们所指向的值都是相同的。
	  但是当参数不是使用引用传递过来的时候,但内核却是使用引用传递。为了解决这个问题 php 内核中实现了 'zval 分离'。
	  zval 分离就是写时复制。使用 PZVAL_IS_REF(zval *) 宏可以判断传递过来的参数是否使用引用传递。而使用 SEPARATE_ZVAL(zval **)
	  可以分离一个 zval。SEPARATE_ZVAL() 宏通过 emalloc() 申请一个新的 zval。


	  6.在 PHP 扩展中创建变量
	  	1.局部变量
	  	要创建一个能够被 php 脚本访问的局部变量,先要创建一个 zval 容器,然后对这个 zval 容器进行填充,最后把它引入 zend 引擎的内部
	  符号表。
	    zval *new_var;
	    //申请并初始化一个新的zval容器
	    MAKE_STD_ZVAL(new_val);//申请并初始化一个新的zval容器
	    //将 'new_var' 变量引入当前的活动符号表中,现在就可以在脚本中使用 $new_var 了
	    ZEND_SET_SYMBOL(EG(active_symbol_table),"new_var",new_var) 
	    符号表就像一个字典。在 php 内核中,所有的变量都是保存在这个符号表中的,php 内核就是在这个符号表中查找对应的变量的。
	    如果在意程序的运行效率,而不在乎内存的话,可以使用 zend_hash_update() 函数跳过检查变量名是否存在符号表中,强行插入。

	    2.全局变量
	    	全局变量和局部变量在 php 内核看来是一样的,不一样的只是它们保存在不同的符号表中,局部变量保存在 active_symbol_table 中,
	    而全局变量保存在 symbol_table 中。只要把 active_symbol_table 替换成 symbol_table 就可以了。
	    zval *new_var;
	    MAKE_STD_ZVAL(new_var);
	    ZEND_SET_SYMBOL(&EG(symbol_table), "new_var", new_var);
	    除了符号表把 active_symbol_table 替换成 symbol_table 之外,还应该注意 active_symbol_table 是一个指针,而 symbol_table 不是,
	   所以还需要增加取地址符号,&EG,因为 ZEND_SET_SYMBOL 宏需要一个指针作为参数。

	    3.在 php 扩展中为变量赋值
	    	在 php 中变量不但保存着值,还保存着类型。所以不但要为变量赋值,同时还要为变量设置类型。

	    	1.长整型类型变量
	    	zval *new_var;
	    	MAKE_STD_ZVAL(new_var);
	    	new_var->value.lval = 12;
	    	new_var->type = IS_LONG;

	    	//为了兼容,使用 ZVAL_LONG 宏赋值
	    	zval *new_var;
	    	MAKE_STD_ZVAL(new_var);
	    	ZVAL_LONG(new_var,12);

	    	//等同于
	    	<?php
	    	$new_var = 12;

	    	2.双精度类型变量
	    	zval *new_var;
	    	MAKE_STD_ZVAL(new_var);
	    	ZVAL_DOUBLE(new_var, 12.56);

	    	3.字符串
	    	zval *new_var;
	    	MAKE_STD_ZVAL(new_var);
	    	new_var->value.str.len = strlen(str);
	    	new_var->value.str.val = estrdup(str);
	    	new_var->type = IS_STRING;
	    	estrdup() 函数是使用 zend 引起的内存管理函数.

	    	4.布尔类型
	    	zval *new_var;
	    	MAKE_STD_ZVAL(new_var);
	    	ZVAL_BOOL(new_var, 1);

	    	5.数组类型
	    		在为变量赋值数组类型的时候,先要创建一个 HashTable,然后将其保存在 zval.val 容器的 ht 字段中。对于这项工作,zend 引擎提供了一个简单的接口
	    	--- array_init()。
	    	zval *new_var;
	    	MAKE_STD_ZVAL(new_var);
	    	arrary_init(new_array);

	    	//等同于
	    	<?php
	    	$new_var = array();

	    	zval *array,*element;
	    	char *key = "key_for_search";
	    	char *value = "value_for_element";
	    	MAKE_STD_ZVAL(array);
	    	MAKE_STD_ZVAL(element);
	    	array_init(array);
	    	ZVAL_STRING(element,value,1);
	    	add_assoc_zval(array,key,element);

	    	//等同于
	    	<?php
	    	$array = array();
	    	$element = 'value_for_element';
	    	$array["key_for_search"] = $element;

	    	6.对象
	    	zval *new_object;
	    	MAKE_STD_ZVAL(new_object);
	    	if (object_init(new_object) != SUCCESS) {
	    		RETURN_NULL();
	    	}
	    	add_prooerty_string(new_object, "name", "Jamens", 1);

	    	7.资源类型
	    		创建资源类型的变量比创建其他类型的变量繁琐一点。严格来说,资源不是数据类型,它是一个可以维护的任何数据类型的抽象。所有的资源类型
	    	都是保存在一个 zend 内部的资源列表中,列表中的每份资源都有一个指向实际数据的指针。
	    		如果某一个资源失去了所有的引用,就会触发相应的析构函数,而这个析构函数是由资源自己提供的。为什么要提供析构函数呢?因为zend引擎不能管理
	    	资源的实际数据,所以为了防止内存泄露,就必须提供析构函数释放这些内存。
	    		使用 zend_register_list_destructors_ex()函数可以用来注册一个资源变量的析构函数,该函数返回一个句柄。这个句柄的作用是把资源与析构函数
	    	相关联。
	    		一般需要一个模块全局变量保存 zend_register_list_destructors_ex()函数返回的资源。如果用 ext_skel 工具生成扩展框架,这个全局变量已经
	    	自动生成,以 'le_' 作为前缀。如 le_myext 用来保存 zend_register_list_descructors_ex() 函数返回的资源句柄。
	    		一般在 MINIT 阶段注册析构函数。
	    		注册完析构函数之后,还需要把真正的资源与这资源的句柄相关联起来(这样当资源没有用时,就会自动调用析构函数)。可以使用 zend_register_resource()
	    	或者 ZEND_REGISTER_RESOURCE() 宏实现。
	    		zend_list_insert() 函数把资源注册到资源表中,并且返回资源在所在列表的位置(资源ID)。之后就可以通过这个id访问资源了。可以通过 RETURN_RESOURCE()
	    	宏返回这个资源给用户。如:	RETURN_RESOURCE(rsrc_id);
	    		当注册一个资源后,zend 引擎就会一直监视这个资源的引用,当这个资源的引用为0 时,zend 引擎会自动调用资源的析构函数。
	    		大多数创建资源的函数都有相应的用于释放资源的函数,如 mysql_connect()有 mysql_close(). 或者直接使用 unset() 一个资源变量。
	    		<?php
	    		$fp = fopen('fiel.txt','r');
	    		unset($fp);
	    		没有使用 fclose() 函数关闭文件的句柄,但是结果这个文件句柄却被关闭了。当 unset 一个资源的时候,会把这个资源的引用减1,当变量的引用等于0时
	    	会自动调用注册的资源析构函数。这就利用了php内核的垃圾回收机制。
	    		但是如果强制释放资源怎么办?这个时候可以使用 zend_list_delete 函数完成。该函数的作用就是把资源变量的引用减1,然后判断引用是否等于0,如果是的
	    	话,就把资源删除。因为该函数只是把资源从列表删除,资源占用的内存是不会被释放的,所以删除资源后还要释放内存。

	    4.错误和输出api
	    	在 php 内核中,不能简单的使用printf()函数打印数据。因为php有自己的一套管理输出流的函数,如果调用 printf()函数,可能会出现意外情况。
	    	1.php_printf()
	    		php_printf() 函数跟 c 语言的标准库的 printf() 函数很相似,唯一不同的就是 php_printf()函数指向的是 zend 输出流。

	    	2.zend_error() 
	    		用于输出一个错误的信息。

	    	3.向 phpinfo() 中输出信息
	    		怎么向 phpinfo() 中添加信息呢? 答案就是实现 ZEND_MINFO() 函数。当在 php 脚本中调用 phpinfo() 函数时,ZEND_MINFO() 函数会自动被调用。
	    		如果指定 ZEND_MINFO()函数,phpinfo() 会自动打印一个小节,而小节的头部就是模块名。
	    		php 内核提供了一些以表格格式显示信息的接口,可以使用这些接口格式化要显示的信息。一般情况下,需要完成3个步骤:
	    		1.调用 php_info_print_table_start 函数制定表格
	    		2.调用 php_info_print_table_header 和 php_info_print_table_row 这2个函数打印表格具体的行列信息。
	    		3.调用 php_info_print_table_end 函数结束表格 
	    		php_info_print_table_start, php_info_print_table_end 这2个函数没有参数,而 php_info_print_table_header, php_info_print_table_row
	    	这2个函数需要的参数会工具需要而变化。

	    5. 运行时信息函数
	    	如果想要了解正在执行的文件名或者正在进行的函数名,可以通过 php 内核提供的一系列查看这些信息的函数。
	    	1.查看当前正在执行的函数名, get_active_function_name()。
	    	2.查看当前执行的文件名, zend_get_executed_filename()。
	    	3.查看当前执行到哪一行代码,zend_get_executed_lineno()。

	    6.调用用户自定义函数
	    	能不能在扩展里面调用用户在 php 脚本中定义的函数呢?可以的。php 允许在扩展里面调用 php 脚本中定义的函数,实现一些回调机制之类的功能。
	    	要在扩展中调用一个 php 脚本中定义的函数,可以使用 call_user_function_ex() 函数实现。

	    7.php 配置项
	    	如果你想为你的扩展创建一个 .ini 文件的配置节,就可以使用 PHP_INI_BEGIN() 宏标识这个节的开始,并使用 PHP_INI_END() 宏标识配置节的结束。
	    可以在两者之间使用 PHP_INI_ENTRY()宏创建具体的配置项,如下:
	    	PHP_INI_BEGIN();
	    	PHP_INI_ENTRY('myext.setting', 'hello world', PHP_INI_ALL,NULL);
	    	PHP_INI_END();

	    	PHP_INI_ENTRY()宏接收4个参数:配置项名称,初始值,改变这个值所需要的权限以及改变这个值时的回调函数句柄。配置项名称和初始值必须是一个字符串,
	    即使它们是一个整数也要转换成字符串。
	    	改变配置项所需的权限可以分为三种:
	    	1.PHP_INI_SYSTEM, 只允许在 php.ini 中修改这些值
	    	2.PHP_INI_USER, 允许用户在运行像 .htaccess 这样的文件时重写其值
	    	3.PHP_INI_ALL, 允许随意修改这些值
	    	第四个参数指定当初始值被修改后的回调函数句柄。可以在 PHP_INI_MH() 宏定义函数:
	    	PHP_INI_MH(OnChangeSetting);

	    	要让扩展能够读到配置的值,就必须把整个初始化配置项引入到 php 内核中,这项工作可以在模块的其实和结束函数中使用 REGISTER_INI_ENTRIES() 宏和
	    UNREGISTER_INI_ENTRIES() 宏完成.

	    8.创建常量的宏
	    	PHP 的常量是不需要使用 '$' 作为前缀的,而且是全局有效。
	    	//创建长整型常量 MY_NEW_CONSTANT,并且是大小写敏感和持久化的
	    	REGISTER_LOONG_CONSTANT('MY_NEW_CONSTANT', 10, CONST_CS | CONST_PERSISTENT);

 

1.windows

 

2.Linux 下

3.PHP 的生命周期

 

4.PHP 内核中的变量

 

5.内核中的 HashTable

 

6.Zend API 详解与扩展编写

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值