欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

编写PHP扩展一:PHP与Zend介绍

程序员文章站 2022-05-24 14:38:33
...
Extension Writing Part I: Introduction to PHP and Zend

译:我在江湖丢了

原文地址:http://devzone.zend.com/303/extension-writing-part-i-introduction-to-php-and-zend/

博客地址:http://lg.uuhonghe.com/index/view?id=3

简介

何为扩展

生命周期

Hello World

创建自己的扩展

ini 配置

全局变量

设置 ini 为全局变量

完整性检查

然后哩?

编写PHP扩展一:PHP与Zend介绍简介

如果你正阅读本教程,那你可能对PHP语言的扩展编写颇感兴趣。如果不是。。。也许你读完之后会发现你对这个之前不知道的东西产生了兴趣。

本文假设读者基本了解PHP语言和用C写的PHP解释器。

让我们先确认一下你为何想写一个PHP扩展:

1、由于语言内核的抽象深度使你有一些库和系统调用无法使用PHP直接完成。

2、你希望PHP以一些不寻常的方法实现自身行为。

3、你已经写了一堆PHP代码,但你知道它可以跑得更快。

4、你有个实现了特别机智点子的代码想卖,但更重要的是你要卖的代码要能跑但不能在源码里看到。

这些都是非常正当的理由了,但要创建一个扩展,首先你得理解什么是扩展。

编写PHP扩展一:PHP与Zend介绍何为扩展?

如果你写过PHP,那你一定用过扩展了。只需要小小的几个扩展,一切PHP里的用户空间功能都在这个或那个扩展的函数组里了。大量这些函数都在标准扩展的一部分——标准扩展总共的400多个。PHP源码里捆绑了86个扩展,平均每个有大概30个函数。掐指一算,总约2500个函数。如果这还不够,PECL仓库里还提供了100个以上的附加扩展,更多的可以再网上别的地方找到。

“这些函数都在扩展里,那还有什么?”你会问,“它们扩展了什么?PHP的核心是什么?”。

PHP的内核由两部分组成。在最底层你能找到Zend引擎(简称ZE)。ZE把人能识别的脚本解析为机器识别的符号,并且在进程空间里运行这些符号。ZE同时处理内存管理,变量域和函数调用。这种区分方式的另一部门是PHP内核。PHP内核处理通信、连接和SAPI层(Server Application Programming Interface, 通常也用于指主机环境,如Apache, IIS, CLI, CGI 等),还提供了控制层对 safe_mode 和 open_basedir 的统一检测,还有和用于文件和网络I/O的用户空间函数fopen(), fread()和fwrite()相关的串流层。

编写PHP扩展一:PHP与Zend介绍生命周期

当一个SAPI启动,例如在 /usr/local/apache/bin/apachectl start 的响应中,PHP以初始化它的内核子系统开始。在这个启动全程将结束之际,会加载每个扩展的内核并调用他们的模块初始化例程(MINIT)。

这给每个扩展一个机会去初始化内部变量,分配资源,注册资源句柄,并且使用ZE注册它的函数,以便当脚本调这些函数时,ZE知道运行哪段代码。

接下来,PHP等待SAPI层来请求一个页面去处理。在CGI或者CLI SAPI的情况下,这会直接发生并且只发生一次。在Apache, IIS 或者其他一些成熟的 web 服务器SAPI中,这是在远程用户请求时发生,并且或发生多次,可能伴随着并发。无论请求如何到达,PHP以通知ZE建立一个供脚本运行的环境为开始,然后调用每个扩展的请求初始化(RINI)函数。RINI给扩展一个机会去建立自己特定的环境变量,分配请求特定的资源,或执行其他任务如审计。RINI函数行为的一个主要例子是在sessions 扩展里,如果session.auto_start项是开启的, RINI将会自动触发用户空间session_start()函数并且预设置$_SESSION变量。

一旦请求被初始化了,ZE通过翻译PHP脚本为tokens,最后转为opcodes来接管,opcodes能够单步调试和执行。

当其中一个opcodes包含的一个扩展方法被调用,ZE会捆绑上该方法的参数,并且临时性地交出控制直接方法完成。

在脚本运行完成之后,PHP调用每个扩展的请求关闭(RSHUTDOWN)函数来做最后的清理工作(例如保证会话变量到磁盘)。接着,ZE运行一个清理进程(被称为垃圾回收),该进程能有效在请求前期中使用的每个变量上执行unset()操作.


操作完成后,PHP等待SAPI请求另一个文档或者信号关闭。在CGI和CLI SAPI情况下,是没有“下一个请求”的,因此SAPI直接启动关闭进程。在关闭进程中,PHP再次遍历每个扩展,调用模块关闭(MSHUTDOWN)函数,最后关闭自己的内核子系统。

以上或许听来使人生畏,然后当你开始钻石一个工作中的扩展时,一些将逐渐清晰。

内存管理

为了防止写得很烂的扩展内存丢失,ZE用一个表明持续性的附加标识来执行它内部的内存管理器。持续性分配是很重要的,能保证内存分配持续到比一个页面请求还长。相对而言,一个不持续性分配在它分配的请求结束时被释放,无论释放函数是否被调用。例如,用户空间变量会在请求结束之后不再使用时候时分配为不持续性的。

然而也许一个扩展理论上会依赖ZE在页面请求结束时自动释放不持续性内存,这是不推荐的。内存分配会给未回收的一个更长的周期,与内存有关的资源则不太可能会被在恰当的时候关闭,没有清理工作会让这一次工作乱作一团。稍后你会看到,确保分配的数据适时清理其时是一件很简单的事情,下面我们简单的对比一下传统内存分配(必须在使用外部类库时使用)和PHP/ZE中的持续性与未持续性内存分配。

Traditional Non-Persistent Persistent
malloc(count)
calloc(count, num)
emalloc(count)
ecalloc(count, num)
pemalloc(count, 1)*
pecalloc(count, num, 1)
strdup(str)
strndup(str, len)
estrdup(str)
estrndup(str, len)
pestrdup(str, 1)
pemalloc() & memcpy()
free(ptr) efree(ptr) pefree(ptr, 1)
realloc(ptr, newsize) erealloc(ptr, newsize) perealloc(ptr, newsize, 1)
malloc(count * num + extr)** safe_emalloc(count, num, extr) safe_pemalloc(count, num, extr)

* pemalloc() 家族包含一个 'persistent' 标志来让他们与他们的未持续性部分相对应。例如:

safe_emalloc(1234)safe_pemalloc(1234, 0) 一样。

** safe_emalloc()safe_pemalloc() (在 PHP5中) 加了个检查避免整型溢出。

构建一个开发环境

现在我们已经学习了PHP和Zend引擎工作方式背后的一些理论,我猜你想运足功力开始开发了。然后在你开始前,必须得收集一些必备的工作来搭建一个满足你需要的开发环境。

首先,你需要PHP本身,因为这一系列开发工具都离不开PHP。如果你还对使用源码搭建PHP不熟悉,推荐你看下这篇文章先:http://www.php.net/install.unix. (使用Windows开发PHP扩展的文章稍后给出)虽然用你的linux发行版里的二进制包安全是非常诱人的,但是这些会遗漏两项在开发过程中十分方便的./configure选项。第一个就是--enable-debug