diff --git "a/ZendEngine1:ZendAPI:\346\267\261\345\205\245PHP\345\206\205\346\240\270.md" "b/ZendEngine1:ZendAPI:\346\267\261\345\205\245PHP\345\206\205\346\240\270.md" index b232e25..e56cbf2 100644 --- "a/ZendEngine1:ZendAPI:\346\267\261\345\205\245PHP\345\206\205\346\240\270.md" +++ "b/ZendEngine1:ZendAPI:\346\267\261\345\205\245PHP\345\206\205\346\240\270.md" @@ -430,11 +430,133 @@ Zend能够确定一个块是否在使用,自动释放未使用的块和丢失 ### 分析解决问题 === -... +实际上,当编译静态或动态模块时没有太多的故障排除可以做。唯一可能产生的问题是编译器会对未定义的或类似的东西发出警告。 +在本例中,确保所有的头文件可用并且命令行中的路径正确。确保所有的东西放在正确的位置,获取一个干净的 PHP 源码树, +使用 `ext` 目录内的干净文件自动构建;这保证了一个安全的编译环境。如果失败,尝试手动编译。 +PHP 可能同样编译你的模块中没有的函数。(如果在相同的源码中不修改它们不会发生这种情况。)如果从外部访问你模块的函数是拼写错误的, +它们会在符号表中以 "unlinked symbols" 保存。在被 PHP 动态加载和链接期间,类型错误不会被解析 - 在主二进制中没有相符的符号。 +在你的模块文件里查找错误的定义或错误的外部引用。注意,这个问题特别是对动态加载的模块;它不会在动态模块中产生。 +静态模块的错误会在编译时显示。 + +### 根源讨论 +=== + +现在你已经有了一个安全的构建环境并且可以把模块引入到 PHP 文件中,是时候讨论这一切如何工作了。 + +#### 模块结构 + +所有的 PHP 模块有共同的结构: + +* 头文件包含(引入所有需要的宏,API定义,等。) + +* C定义的导出函数(需要定义 Zend 函数块) + +* Zend 函数块定义 + +* Zend 模块块定义 + +* **get_module()** 实现 + +* 所有导出函数的实现 + +#### 头文件包含 + +你模块真正需要包含的头文件是 `php.h`,在PHP目录内。这个文件引入所有的宏和 API 来构建对代码可用的新模块。 + +*提示*:为你的模块创建独立的包含模块特殊定义的头文件是个好的实践。这个头文件应该包含所有导出函数前面的定义并引入 `php.h`。 +如果你用 *ext_skel* 创建模块,你已经有了这样一个准备好的头文件。 + +#### 定义导出函数 + +要定义导出函数(作为一个原生函数对 PHP 可用),Zend 提供一套宏。一个简单的定义如下: + +``` +ZEND_FUNCTION ( my_function ); +``` + +*ZEND_FUNCTION* 定义了一个由 Zend 内部 API 编译的新的C函数。它的意思是函数类型是 *void* 并接收 +*INTERNAL_FUNCTION_PARAMETERS(another macro)* 作为参数。另外,它给函数名加 *zif* 前缀。 +上面定义的展开版本如下: + +``` +void zif_my_function ( INTERNAL_FUNCTION_PARAMETERS ); +``` + +展开 *INTERNAL_FUNCTION_PARAMETERS* 结果如下: + +``` +void zif_my_function( int ht + , zval * return_value + , zval * this_ptr + , int return_value_used + , zend_executor_globals * executor_globals + ); +``` + +由于解释器和执行器核心已经从主要的 PHP 包中分离出来,第二种定义宏和函数设置的API已逐步演化:Zend API。 +所以 Zend API 现在处理很少的先前属于PHP的责任,大量的 PHP 函数已经缩减为调用 Zend API 的宏别名。 +推荐的实践是任何使用 Zend API 的地方,旧的API只作兼容性维护。例如,zval 和 pval 是相同的。zval 是 Zend 的定义; +pval 是 PHP 的定义(实际上,pval 现在是 zval 的别名)。由于 *INTERNAL_FUNCTION_PARAMETERS* 宏现在是一个 Zend 宏, +上面的定义包含 zval。当写代码时,你应该总是使用 zval 确保是新的 Zend API。 + +这个定义的参数列表非常重要;你应该记住这些参数: +Zend's Parameters to Functions Called from PHP + +| 参数 | 描述 +|--- |--- +| ht | 传递给 Zend 函数的参数个数。你应该避免直接接触,而用 ZEND_NUM_ARGS() 获取值。 +| return_value | 这个变量用来从你的函数返回值给 PHP。访问这个值的最好方式是用预定义宏。具体描述见下面。 +| this_ptr | 使用这个变量,你可以访问你函数中含有的对象,如果在对象中使用。使用函数 **getThis()** 来获取这个指针。 +| return_value_used | 这个标记标明这个函数最终的返回值是否实际被脚本使用。0表明没有使用;1标明调用者使用了。
这个标记的评估可以用来验证函数的正确使用 以及 在返回值需要很高代价的例子中的速度优化(例如,看 array.c 如何使用它)。 +| executor_globals | 这个变量指向 Zend 引擎的全局设置。你在创建新变量时会发现很有用,(更多例子在后面)。
执行器全局变量同样可以用宏 *TSRMLS_FETCH()* 引入到你的函数中。 + +#### Zend 函数块定义 + +现在你已经定义了导出函数,你同样需要引入 Zend 中。使用 Zend_function_entry 来引入函数列表。 +这个数组连续包含所有的函数使它在外部可用,带有希望出现在PHP中的函数名和在C源码中定义的名。在内部,zend_function_entry 定义如下: + +Example #4 Internal declaration of zend_function_entry. + +``` +typedef struct _zend_function_entry { + char *fname; + void (*handler)(INTERNAL_FUNCTION_PARAMETERS); + unsigned char *func_arg_types; +} zend_function_entry; +``` + +| 名字 | 描述 +|--- |--- +| fname | 表示PHP中可见的函数名(如,fopen,mysql_connect,或在我们的例子中,first_module)。 +| handler | 指向C函数负责处理函数调用。如,见前面讨论的标准宏 *INTERNAL_FUNCTION_PARAMETERS* +| func_arg_types | 允许你标记指定的参数,这样它们被强制以引用方式传递。你通常应该设为 NULL。 + +在上面的例子中,定义看起来像这样: + +``` +zend_function_entry firstmod_functions[] = +{ + ZEND_FE(first_module, NULL) + {NULL, NULL, NULL} +}; +``` + +你可以看到数组中最后一个记录是 {NULL, NULL, NULL}。这个标记设置用来让 Zend 知道何时到达导出函数的结尾。 + +``` +Note: +你不能使用预定义宏给结尾的标记,因为它会尝试指向一个 “NULL” 名字的函数! +``` + + +后面雷同,参考前面 1~7 章节. + +--- ### 调用用户使用的函数 [ Calling User Functions ] === + 你可以在自己的模块中调用用户函数,当实现回调时非常有用;例如,数组回调,检索,或简单的事件驱动程序。 用户函数可以用 `call_user_function_ex()` 来调用。它需要一个你想访问得函数表的哈希值,一个指向对象的指针(如果想使用方法), @@ -521,6 +643,7 @@ Return value: 'hello' ### 初始化文件支持 === + PHP4以重新设计的文件支持为特色。现在直接在代码中指定默认的初始化方式已成为可能,在运行时读取和改变这些值, 并且为更改通知创建消息句柄。 @@ -605,6 +728,7 @@ ZEND_MSHUTDOWN_FUNCTION(mymodule) ### 从这儿去哪儿 === + 你已经学了很多关于PHP的。你知道如何创建动态加载的模块并静态链接的扩展。你已经学了PHP和Zend变量的内部存储并能创建和访问这些变量。 你知道相当一套做许多常规任务的工具函数如打印信息字符,自动绑定变量到符号表,等等。 @@ -614,6 +738,23 @@ ZEND_MSHUTDOWN_FUNCTION(mymodule) ### 参考:一些配置宏 === -**config.m4** +#### config.m4 +文件 `config.m4` 由 `buildconf` 处理,并且必须包含配置期间所有要执行的命令。例如,它们可以是包含测试的外部文件,如头文件,库,等。 +PHP 定义了一套宏用于这个处理,最有用的描述如下。 + +| 宏 | 描述 +|--- |--- +| *AC_MSG_CHECKING(message)* | 配置期间打印一个 "checking " 文本。 +| *AC_MSG_RESULT(value)* | 把结果给 *AC_MSG_CHECKING*; 应该指定为 yes 或 no。 +| *AC_MSG_ERROR(message)* | 配置期间打印消息和错误消息并终止脚本。 +| *AC_DEFINE(name, value, description)* | 添加 `#define` 到 *php_config.h* ,以value为值,并含有描述(这对模块的条件式编译很有用)。 +| *AC_ADD_INCLUDE(path)* | 添加一个含路径的编译器;例如,如果模块需要添加头文件的检索路径时使用。 +| *AC_ADD_LIBRARY_WITH_PATH(libraryname, librarypath)* | 指定链接的一个其它库 +| *AC_ARG_WITH(modulename, description, unconditionaltest, conditionaltest)* | 相当强大的宏,添加模块描述到 configure --help 输出。
PHP检测选项 --with- 是否传给了配置脚本。如果是,它运行非条件式的脚本(如 --with-myext=yes),
例子中选项值保存在 $withval 中。否则,它执行条件检测。 +| *PHP_EXTENSION(modulename, [shared])* | 这个调用PHP配置你的扩展的宏是必须的。你可以应用第二个参数标示是否想要编译为共享模块。
这将在编译时为你的代码产生一个定义 *COMPILE_DL_* + +--- + +后面雷同,参考前面第3章节