Skip to content

Commit 6d0eae2

Browse files
author
weiChen
authored
Update ZendEngine1:ZendAPI:深入PHP内核.md
1 parent a8ba906 commit 6d0eae2

1 file changed

Lines changed: 143 additions & 2 deletions

File tree

ZendEngine1:ZendAPI:深入PHP内核.md

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,11 +430,133 @@ Zend能够确定一个块是否在使用,自动释放未使用的块和丢失
430430
### 分析解决问题
431431
===
432432

433-
...
433+
实际上,当编译静态或动态模块时没有太多的故障排除可以做。唯一可能产生的问题是编译器会对未定义的或类似的东西发出警告。
434+
在本例中,确保所有的头文件可用并且命令行中的路径正确。确保所有的东西放在正确的位置,获取一个干净的 PHP 源码树,
435+
使用 `ext` 目录内的干净文件自动构建;这保证了一个安全的编译环境。如果失败,尝试手动编译。
434436

437+
PHP 可能同样编译你的模块中没有的函数。(如果在相同的源码中不修改它们不会发生这种情况。)如果从外部访问你模块的函数是拼写错误的,
438+
它们会在符号表中以 "unlinked symbols" 保存。在被 PHP 动态加载和链接期间,类型错误不会被解析 - 在主二进制中没有相符的符号。
439+
在你的模块文件里查找错误的定义或错误的外部引用。注意,这个问题特别是对动态加载的模块;它不会在动态模块中产生。
440+
静态模块的错误会在编译时显示。
441+
442+
### 根源讨论
443+
===
444+
445+
现在你已经有了一个安全的构建环境并且可以把模块引入到 PHP 文件中,是时候讨论这一切如何工作了。
446+
447+
#### 模块结构
448+
449+
所有的 PHP 模块有共同的结构:
450+
451+
* 头文件包含(引入所有需要的宏,API定义,等。)
452+
453+
* C定义的导出函数(需要定义 Zend 函数块)
454+
455+
* Zend 函数块定义
456+
457+
* Zend 模块块定义
458+
459+
* **get_module()** 实现
460+
461+
* 所有导出函数的实现
462+
463+
#### 头文件包含
464+
465+
你模块真正需要包含的头文件是 `php.h`,在PHP目录内。这个文件引入所有的宏和 API 来构建对代码可用的新模块。
466+
467+
*提示*:为你的模块创建独立的包含模块特殊定义的头文件是个好的实践。这个头文件应该包含所有导出函数前面的定义并引入 `php.h`
468+
如果你用 *ext_skel* 创建模块,你已经有了这样一个准备好的头文件。
469+
470+
#### 定义导出函数
471+
472+
要定义导出函数(作为一个原生函数对 PHP 可用),Zend 提供一套宏。一个简单的定义如下:
473+
474+
```
475+
ZEND_FUNCTION ( my_function );
476+
```
477+
478+
*ZEND_FUNCTION* 定义了一个由 Zend 内部 API 编译的新的C函数。它的意思是函数类型是 *void* 并接收
479+
*INTERNAL_FUNCTION_PARAMETERS(another macro)* 作为参数。另外,它给函数名加 *zif* 前缀。
480+
上面定义的展开版本如下:
481+
482+
```
483+
void zif_my_function ( INTERNAL_FUNCTION_PARAMETERS );
484+
```
485+
486+
展开 *INTERNAL_FUNCTION_PARAMETERS* 结果如下:
487+
488+
```
489+
void zif_my_function( int ht
490+
, zval * return_value
491+
, zval * this_ptr
492+
, int return_value_used
493+
, zend_executor_globals * executor_globals
494+
);
495+
```
496+
497+
由于解释器和执行器核心已经从主要的 PHP 包中分离出来,第二种定义宏和函数设置的API已逐步演化:Zend API。
498+
所以 Zend API 现在处理很少的先前属于PHP的责任,大量的 PHP 函数已经缩减为调用 Zend API 的宏别名。
499+
推荐的实践是任何使用 Zend API 的地方,旧的API只作兼容性维护。例如,zval 和 pval 是相同的。zval 是 Zend 的定义;
500+
pval 是 PHP 的定义(实际上,pval 现在是 zval 的别名)。由于 *INTERNAL_FUNCTION_PARAMETERS* 宏现在是一个 Zend 宏,
501+
上面的定义包含 zval。当写代码时,你应该总是使用 zval 确保是新的 Zend API。
502+
503+
这个定义的参数列表非常重要;你应该记住这些参数:
504+
Zend's Parameters to Functions Called from PHP
505+
506+
| 参数 | 描述
507+
|--- |---
508+
| ht | 传递给 Zend 函数的参数个数。你应该避免直接接触,而用 ZEND_NUM_ARGS() 获取值。
509+
| return_value | 这个变量用来从你的函数返回值给 PHP。访问这个值的最好方式是用预定义宏。具体描述见下面。
510+
| this_ptr | 使用这个变量,你可以访问你函数中含有的对象,如果在对象中使用。使用函数 **getThis()** 来获取这个指针。
511+
| return_value_used | 这个标记标明这个函数最终的返回值是否实际被脚本使用。0表明没有使用;1标明调用者使用了。<br/>这个标记的评估可以用来验证函数的正确使用 以及 在返回值需要很高代价的例子中的速度优化(例如,看 array.c 如何使用它)。
512+
| executor_globals | 这个变量指向 Zend 引擎的全局设置。你在创建新变量时会发现很有用,(更多例子在后面)。<br/>执行器全局变量同样可以用宏 *TSRMLS_FETCH()* 引入到你的函数中。
513+
514+
#### Zend 函数块定义
515+
516+
现在你已经定义了导出函数,你同样需要引入 Zend 中。使用 Zend_function_entry 来引入函数列表。
517+
这个数组连续包含所有的函数使它在外部可用,带有希望出现在PHP中的函数名和在C源码中定义的名。在内部,zend_function_entry 定义如下:
518+
519+
Example #4 Internal declaration of zend_function_entry.
520+
521+
```
522+
typedef struct _zend_function_entry {
523+
char *fname;
524+
void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
525+
unsigned char *func_arg_types;
526+
} zend_function_entry;
527+
```
528+
529+
| 名字 | 描述
530+
|--- |---
531+
| fname | 表示PHP中可见的函数名(如,fopen,mysql_connect,或在我们的例子中,first_module)。
532+
| handler | 指向C函数负责处理函数调用。如,见前面讨论的标准宏 *INTERNAL_FUNCTION_PARAMETERS*
533+
| func_arg_types | 允许你标记指定的参数,这样它们被强制以引用方式传递。你通常应该设为 NULL。
534+
535+
在上面的例子中,定义看起来像这样:
536+
537+
```
538+
zend_function_entry firstmod_functions[] =
539+
{
540+
ZEND_FE(first_module, NULL)
541+
{NULL, NULL, NULL}
542+
};
543+
```
544+
545+
你可以看到数组中最后一个记录是 {NULL, NULL, NULL}。这个标记设置用来让 Zend 知道何时到达导出函数的结尾。
546+
547+
```
548+
Note:
549+
你不能使用预定义宏给结尾的标记,因为它会尝试指向一个 “NULL” 名字的函数!
550+
```
551+
552+
553+
后面雷同,参考前面 1~7 章节.
554+
555+
---
435556

436557
### 调用用户使用的函数 [ Calling User Functions ]
437558
===
559+
438560
你可以在自己的模块中调用用户函数,当实现回调时非常有用;例如,数组回调,检索,或简单的事件驱动程序。
439561

440562
用户函数可以用 `call_user_function_ex()` 来调用。它需要一个你想访问得函数表的哈希值,一个指向对象的指针(如果想使用方法),
@@ -521,6 +643,7 @@ Return value: 'hello'
521643

522644
### 初始化文件支持
523645
===
646+
524647
PHP4以重新设计的文件支持为特色。现在直接在代码中指定默认的初始化方式已成为可能,在运行时读取和改变这些值,
525648
并且为更改通知创建消息句柄。
526649

@@ -605,6 +728,7 @@ ZEND_MSHUTDOWN_FUNCTION(mymodule)
605728

606729
### 从这儿去哪儿
607730
===
731+
608732
你已经学了很多关于PHP的。你知道如何创建动态加载的模块并静态链接的扩展。你已经学了PHP和Zend变量的内部存储并能创建和访问这些变量。
609733
你知道相当一套做许多常规任务的工具函数如打印信息字符,自动绑定变量到符号表,等等。
610734

@@ -614,6 +738,23 @@ ZEND_MSHUTDOWN_FUNCTION(mymodule)
614738

615739
### 参考:一些配置宏
616740
===
617-
**config.m4**
618741

742+
#### config.m4
619743

744+
文件 `config.m4``buildconf` 处理,并且必须包含配置期间所有要执行的命令。例如,它们可以是包含测试的外部文件,如头文件,库,等。
745+
PHP 定义了一套宏用于这个处理,最有用的描述如下。
746+
747+
| 宏 | 描述
748+
|--- |---
749+
| *AC_MSG_CHECKING(message)* | 配置期间打印一个 "checking <message>" 文本。
750+
| *AC_MSG_RESULT(value)* | 把结果给 *AC_MSG_CHECKING*; 应该指定为 yes 或 no。
751+
| *AC_MSG_ERROR(message)* | 配置期间打印消息和错误消息并终止脚本。
752+
| *AC_DEFINE(name, value, description)* | 添加 `#define`*php_config.h* ,以value为值,并含有描述(这对模块的条件式编译很有用)。
753+
| *AC_ADD_INCLUDE(path)* | 添加一个含路径的编译器;例如,如果模块需要添加头文件的检索路径时使用。
754+
| *AC_ADD_LIBRARY_WITH_PATH(libraryname, librarypath)* | 指定链接的一个其它库
755+
| *AC_ARG_WITH(modulename, description, unconditionaltest, conditionaltest)* | 相当强大的宏,添加模块描述到 configure --help 输出。<br/>PHP检测选项 --with-<modulename> 是否传给了配置脚本。如果是,它运行非条件式的脚本(如 --with-myext=yes),<br/>例子中选项值保存在 $withval 中。否则,它执行条件检测。
756+
| *PHP_EXTENSION(modulename, [shared])* | 这个调用PHP配置你的扩展的宏是必须的。你可以应用第二个参数标示是否想要编译为共享模块。<br/>这将在编译时为你的代码产生一个定义 *COMPILE_DL_<modulename>*
757+
758+
---
759+
760+
后面雷同,参考前面第3章节

0 commit comments

Comments
 (0)