分类 扩展之路 下的文章

ZendAPI中文版-尾

(十四)信息输出

就像我们在脚本中使用 print() 函数一样,我们也经常需要从扩展向输出流输出一些信息。在这方面-比如输出警告信息、phpinfo() 中对应的信息等一般性任务-PHP 也为我们提供了一系列函数。这一节我们就来详细地讨论一下它们。

zend_printf()

zend_printf() 功能跟 printf() 差不多, 唯一不同的就是它是向 Zend 的输出流提供信息。

zend_error()

zend_error() 用于创建一个错误信息。这个函数接收两个参数:第一个是错误类型(见 zend_error.h),第二个是错误的提示消息。

zend_error(E_WARNING, "This function has been called with empty arguments");

“表3.16 Zend 预定义的错误信息类型” 列出了一些可能的值(在 PHP 5.0 及以上版本中又增加了一些错误类型,可参见 zend_error.h,译注)。这些值也可以用在 php.ini 里面,这样你的错误信息将会依照 php.ini 里面的设置,根据不同的错误类型而被选择性地记录。

- 阅读剩余部分 -

ZendAPI中文版-下

(十一)创建变量

当 PHP 脚本与扩展互相交换数据时,我们还需要做一件很重要的事情,那就是创建变量。这一小节将会展示如何处理那些 PHP 脚本所支持的变量类型。

概述

要创建一个能够被 PHP 脚本所访问的“外部变量”,我们只需先创建一个 zval 容器,然后对这个 zval 结构进行必要的填充,最后再把它引入到 Zend 的内部符号表中就可以了。而且几乎所有变量的创建基本上都是这几个步骤:

zval *new_variable;   
/* 申请并初始化一个新的的 zval 容器 */   
MAKE_STD_ZVAL(new_variable); 
/* 设置变量的类型和内容,见下   */ 
/* 将名为   "new_variable_name" 变量引入符号表   */  
ZEND_SET_SYMBOL(EG(active_symbol_table), "new_variable_name", new_variable);  
/* 现在就可以在脚本中用 $new_variable_name来访问这个变量了*/

宏 MAKE_STD_ZVAL 通过 ALLOC_ZVAL 来申请一个新的 zval 容器的内存空间并调用 INIT_ZVAL(查看 PHP4/5 的源代码可知此处可能为原文笔误,实际上应为 INIT_PZVAL,下同。译注)将其初始化。在当前的 Zend Engine 中,INIT_ZVAL 所负责的初始化工作除了将 zval 容器的引用计数(refcount)置为 1 之外,还会把引用标识也清除(即把 is_ref 也置为 0)。而且在以后的 Zend Engine 中还可能会继续扩展这个 INIT_ZVAL 宏操作,因此我们推荐您使用 MAKE_STD_ZVAL 而非简单使用一个 ALLOC_ZVAL 来完成一个变量的创建工作。当然,如果您是想优化一下速度(或者是不想明确地初始化这个 zval 容器),那还是可以只用 ALLOC_ZVAL 来搞定的。不过我们并不推荐这么做,因为这将不能保证数据的完整性。

- 阅读剩余部分 -

ZendAPI中文版-中

(八)故障处理

实际上,在对静态或动态模块进行编译时没有太多故障处理工作要做。唯一可能的问题就是编译器会警告说找不到某些定义或者类似的事情。如果出现这种情况,你应该确认一下所有的头文件都是可用的并且它们的路径都已经在编译命令中被指定。为了确保每个文件都能被正确地定位,你可以先提取一个干净的 PHP 源码树,然后在 Ext 目录使用自动构建工具来创建这些文件。用这种方法就可以确保一个安全的编译环境。假如这样也不行,那就只好试试手动编译了。

PHP 也可能会警告说在你的模块里面有一些未定义的函数。(如果你没有改动样例文件的话这种情况应该不会发生。)假如你在模块中拼错了一些你想访问的外部函数的名字,那么它们就会在符号表中显示为“未能连接的符号”。这样在 PHP 动态加载或连接时,它们就不会运行--在二进制文件中没有相应的符号。为了解决这个问题,你可以在你的模块文件中找一下错误的声明或外部引用。注意,这个问题仅仅发生在动态可加载模块身上。而在静态模块身上则不会发生,因为静态模块在编译时就会抛出这些错误。

- 阅读剩余部分 -

ZendAPI中文版-上

(一)摘要

知者不言,言者不知。 

――老子《道德经》五十六章

有时候,单纯依靠 PHP “本身”是不行的。尽管普通用户很少遇到这种情况,但一些专业性的应用则经常需要将 PHP 的性能发挥到极致(这里的性能是指速度或功能)。由于受到 PHP 语言本身的限制,同时还可能不得不把庞大的库文件包含到每个脚本当中。因此,某些新功能并不是总能被顺利实现,所以我们必须另外寻找一些方法来克服 PHP 的这些缺点。

了解到了这一点,我们就应该接触一下 PHP 的心脏并探究一下它的内核-可以编译成 PHP 并让之工作的 C 代码-的时候了。

(二)概述

“扩展 PHP”说起来容易做起来难。PHP 现在已经发展成了一个具有数兆字节源代码的非常成熟的系统。要想深入这样的一个系统,有很多东西需要学习和考虑。在写这一章节的时候,我们最终决定采用“边学边做”的方式。这也许并不是最科学和专业的方式,但却应该是最有趣和最有效的一种方式。在下面的小节里,你首先会非常快速的学习到如何写一个虽然很基础但却能立即运行的扩展,然后将会学习到有关 Zend API 的高级功能。另外一个选择就是将其作为一个整体,一次性的讲述所有的这些操作、设计、技巧和诀窍等,并且可以让我们在实际动手前就可以得到一副完整的愿景。这看起来似乎是一个更好的方法,也没有死角,但它却枯燥无味、费时费力,很容易让人感到气馁。这就是我们为什么要采用非常直接的讲法的原因。

注意,尽管这一章会尽可能多讲述一些关于 PHP 内部工作机制的知识,但要想真的给出一份在任何时间任何情况下的PHP 扩展指南,那简直是不可能的。PHP 是如此庞大和复杂,以致于只有你亲自动手实践一下才有可能真正理解它的内部工作机制,因此我们强烈推荐你随时参考它的源代码来进行工作。

- 阅读剩余部分 -

zval_dtor与zval_ptr_dtor的区别

这两个东西长得很像,起初我还错误以为一个是针对zval一个针对zval*的释放函数,唉,太天真了。这两个函数都与zval的释放有关,是我们肯定会经常碰到的两个函数。

下面是两者的声明:

//zval_dtor是宏函数,最终展开后
ZEND_API   void  _zval_dtor_func(zval *zvalue ZEND_FILE_LINE_DC)

//zval_ptr_dtor是宏函数,最终展开后
ZEND_API  void  _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC)

两者的工作都与释放zval有关,但又有很大的区别。

比如我们有一个zval *tmp,而且我们已经对它进行了MAKE_STD_ZVAL等一系列操作了。

- 阅读剩余部分 -