phpphp
在常见的webserver环境中,你不能一直启动php解释器;通常是启动apache或者其余webserver,由他们加载php处理须要处理的脚本(请求的.php文档)html
SAPI是Server Application Programming Interface(服务器应用编程接口)的缩写。PHP经过SAPI提供了一组接口,供应用和PHP内核之间进行数据交互。git
尽管看起来不一样,可是实际上CLI的行为和web方式一致,在命令行键入php命令将启动"命令行sapi"他实际就像一个设计用于服务单个请求的迷你版webserver.当脚本运行完成后,这个迷你的php-webserver终止并返回控制给shell.程序员
这里的启动和终止过程分为两个独立的启动阶段和两个独立的终止阶段,一个周期用于php解释器总体执行所需结构和值的初始化设置,他们在sapi生命周期中持久存在,另外一个仅服务于单页面请求,生命周期短暂一些.
初始化启动在全部的请求发生以前,php调用每个扩展的模块初始化方法.这里,扩展可能会定义常量,定义类,注册资源,流,过滤处理器等全部将要被请求脚本所使用的的资源.全部这些都有一个特性,就是他们被设计跨全部请求存在,也就能够成为"持久".
在请求到达时,php会安装一个操做系统,该环境包含符号表(变量存储)并会同步每一个目录的配置值.php接着遍历全部的扩展,这一次调用每个扩展的请求初始化方法.这里,扩展可能充当全局变量到默认值.预置变量到脚本的符号表,或执行其余的任务好比记录页面请求日志到文件.
在一个请求完成处理后,(到达脚本末尾或者调用die()语句),php经过调用每个扩展的请求终止开始清理过程.
在符号表和其余资源释放前所须要作的最后一件事就是showdown,当完成方法后,符号表中的全部变量都会被当即unset(). 在此期间,全部非持久化资源和对象的析构器都将被调用去释放资源.
最后,当全部的请求都等到知足,webserver和其余的sapi开始准备终止,php循环执行每一个扩展MSHUTDOWN(模块终止)方法.这是MINIT周期内,扩展最后一次卸载处理器和释放持久化分配内存的机会.github
每一个php实例,不管从init脚本启动仍是命令行启动,接下来就是系列的请求/模块的初始化/终止事件,以及脚本自身的执行.每一个启动和终止阶段会被执行多少次,上门频率执行?都依赖所使用的sapi.最多见的模式:多线程模块,多进程模块,嵌入式.web
cli(和cgi)sapi在他的单请求生命周期中至关特殊,由于此时整个php的生命周期只有一个请求.不过,前面讲的各个阶段仍然会所有执行.shell
随着发展,php逐渐被一些webserver以多线程方式使用.在多线程的webserver中永远都只有一个进程在运行,可是在进程空间中有多个线程同时执行.这样作能下降一些负载.apache
sapi接口一致的规则基本路径:模块初始化=>请求初始化=>请求=>请求终止=>模块终止.编程
当php嵌入多进程webserver,给定的内部变量仍然定在全局而且能够经过在每一个请求启动时正确的初始化,终止时去作适当的清理工做来作到安全访问,由于在一个进程空间中同时只会有一个请求,这个时候增长了请求的内存管理,以防止资源泄露增加失去控制.
单进程多线程webserver出现后,就须要一种对全局数据处理的新方法,最后这做为新的一层TSRM(线程安全资源管理)canvas
在一个简单的非线程应用中,你可能很喜欢定义全局变量,将他们放在你的源代码的顶部,编译器会在你的程序的数据段分配内存块保存信息.
在多线程应用中,每一个线程须要他本身的数据元素,须要为每个线程分配独立的内存块,一个给定线程在他须要访问本身的数据时须要可以正确的访问本身的内存块.
每一种编程语言有个共同的特色就是存储和取回信息,PHP也是一样的,不少语言都要求全部变量在使用前被定义,而且他们的类型信息时固定的.而后php容许程序员在使用时候建立变量.而且能够存储任何类型语言能表达的信息.而且还能够在须要的时候自动转换变量的类型.php是弱类型的语言,C的类型是强类型的.
固然,数据的编码只是一半工做,为了保持对全部信息片的跟踪,每一个变量还须要一个标签和一个容器.从用户角度来看,你能够把他们看作变量名和做用域.
php中的数据存储单位是zval,也被称为Zend Value.他是一个由四个成员的结构体.在Zend/zend.h中定义.
zend当前定义了下列的8中数据类型: * ISNULL * ISBOOL * ISDOUBLE * ISSTRING * ISARRAY * ISOBJECT * IS_RESOURCE
和类型同样,zval的值也能够用3个一组的宏检查
对于简单的标量类型,boolean,long,double,简写为BVAL,LVAL,DVAL.
字符串包含两个成员,所以tab有一对宏分别表示char和int
数组数据类型内部以HashTable存储
你在空间内侧使用过php,所以比较熟悉数组,咱们能够将任意的数量的php变量放到容器中,并能够为他们指派数字或者字符串格式的名字.
若是不出意外,php脚本中的每个变量都应该能够在一个数组中找到,当你建立变量时,为他赋一个值.Zend把这个值放到称为符号表的一个内部数组中.
本章咱们看到php变量的内部表示,你学习了区别类型,设置和取回值,将变量增长到符号表中以及将他们取回.
php和c最重要的区别就是是否控制内存指针.
在php中,设置一个字符串变量很简单:<?php $str="helloworld"; ?>,字符串能够自由的修改,拷贝,移动,在c中,则是另外一种方式.虽然你能够简单的用静态字符串初始化;char*str="helloworld";可是这个字符串不能被修改.由于他存在于代码段中,要建立一个可维护的字符串,你须要分配一块内存,并使用strdup()这样的函数将内容拷贝到其中.
因为请求跳出(故障)产生的内存泄露的解决方案是Zend内存管理层.引擎在这一部分扮演至关于操做系统一般扮演的角色,分配内存给调用应用.不一样的是,站在进程空间请你的认知角度.他足够底层.当请求die的时候,他执行和OS在进程die时所作的相同的事情.也就是说他会隐式的释放全部请求拥有的内存空间.
php是一种托管语言,从用户空间一侧考虑,当心的控制资源和内存就意味着更容易的原型涉及和更少的崩溃.在你深刻研究解开引擎的面纱后, 就不能有博彩内心,而是对运行环境完整性的开发和维护负责.
每个扩展的构建至少须要两个文件:一个configuration文件,它告诉编译期要构建那些文件须要哪些什么外部的库,还须要至少一个源文件,它执行实际的工做.
实际上,一般会有第二或者第三个配置文件,以及一个或多个头文件,对于你的第一个扩展,你须要添加每一种类型的有个文件并使用他们工做.
要开始了,首先在你的php源代码目录树的ext/目录下建立名为sample的目录,实际上这个心的目录能够放在任何地方,可是为了在本章后面演示win和静态页面,咱们还先创建源代码目录.
当使用C开发的时候,将数据的类型定义放到外部的头文件中隔离起来,由源文件包含是最多见的作法.尽管php并不要求这样,可是这样作在模块增加到不能放到单一源文件.
用户空间函数利用return关键字向他调用空间回传信息,这一点和c语言的语法相同.
你可能认为你的内部函数应该直接返回一个zvel,或者说分配一个zval的内存空间并返回zval*.可是这样是不正确的,并不强制每一个函数实现分配zval并返回它.而是zend引擎在函数调用以前预先分配这个空间,接着讲zval的类型初始化为isnull,并将值做为参数名returnvalue传递.
咱们须要注意的是phpfunction()实现并不会直接返回任何值,取而代之的是直接将恰当的数据弹出到returnvalue参数中,zend引擎会在内部函数执行完成后处理它.
使用return(语法)结构将值和变量回传给调用方是没有问题的,可是,有时候你须要从一个函数返回多个值,你可使用数组达到这个目的,或者你可使用出参数栈返回值.
本章你看到怎样从一个内部函数返回值,包括直接返回值和引用方式返回,以及经过参数栈应用返回,以及经过参数栈引发返回.此外还简单了解zend引擎2的参数类型.
在c语言中,有两种不一样的基础方法用来在一个结构体中存储任意数量的独立数据元素,两种方法都有.
咱们也能够不使用回调进行hashtable的迭代,咱们须要记得hashtable中经常别忽略的概念:内部指针.
在用户空间,函数reset(),key(),current(),next(),prev(),each(),end()能够访问数组内的元素,他们依赖于一个不可访问的"当前"位置.
迄今为止,你都是工做再很是基础的用户空间数据类型上,字符串,数值,TRUE/FALSE等值.即使上一章开始接触数组,但也是收集这些基础数据类型的数组.
现实世界中,你一般须要更加复杂的数据集合下工做,一般涉及到晦涩的结构体指针,一个常见的晦涩的结构指针,即使在c语言当中也只是一个指针.
stdio的文件描述符合其余多数文件描述符一致,都像书签,你扩展的调用应用须要在feof(),fread(),fwrite(),fclose()这样的实现函数调用时传递这个值.同时书签必须是用户空间代码能够访问的,所以,就须要在标准的php变量或者说zval中有表示他的方法.
这里就须要一种新的数据类型.resource数据类型在zval中存储一个简单的整型值,使用做为已注册资源的索引来查找.资源条目包含了资源索引所表示的内部数据类型,以及存储资源数据的指针的信息.
为了使用注册的资源条目所包含额资源信息更加明确,须要定义资源的类型.
如今引擎知道你要存储一些资源数据,是时候给用户空间的代码一种方式去产生实际的资源,要作到这一点,须要以下从新实现fopen()命令.
建立资源仅仅是第一步,由于书签的做用只是让你能够回到原来的那一页,这里是另外一个函数.
使用本章内容,你能够开始应用php著名的粘合性了.资源数据类型使得你的扩展能够很容易将第三方库的透明指针这样的抽象概念.
之前的版本,php不支持任何面相对象呢的编程语法,在php4中引入了zend引擎,出现几个新特性,其中就包括对象数据类型.
第一次面相对象编程(OOP)支持仅实现对象关联语义.php4的对象只是将一个数组和一些方法绑定到一块儿.
在php5中引入一些新特性,属性和方法均可以经过修饰符来定义类类外面的可见性.函数的重载.
在进入OOP的世界前,咱们须要轻装上阵.
将php5的对象,和他的前辈[php4对象进行比较.在php5对象变量中有两个关键的组件,第一个就是数值得标识.第二是对象变量的句柄表.使用他能够自定义zend引擎对实例的处理方式.
接口的定义和类的定义除了几个差别外基本是一致的么搜西安是全部的方法都是定义为抽象的.因为这些方法是抽象的,因此须要实现,接下来的第二个差别就是注册.
实现接口,假设须要实现这个接口的定义的全部的抽象方法.
php用户空间中全部的文件I/O处理都是经过php引入的php流包装层处理.在内部,扩展代码能够选择使用stdio或者posix文件处理和本地文件系统或者伯克利域套接字进行通讯,或者也能够调用和用户空间流I/O相同的API.
一般,直接的文件描述相比调用包装层消耗更少的CPU和内存,然而,这样会将实现某个特定协议的全部工做都堆积到做为扩展开发者的你身上.
尽管是一个统一的API,但实际上依赖所需的流的类型,有四种不一样的路径去打开一个流.从用户空间角度来看,这四种不一样的类型有: * fopen();//fopen包装 * fsockopen();//传输 * opendir();//目录流 * procopen();//特殊流 不管你打开什么类型的流,他们都存储在一个公共的结构phpstream中.
php的流最强力的特征之一是他能够访问众多数据资源:普通文件,压缩文件,网络透明通道,加密网络,命名管道以及域套接字,他们对于用户空间以及内部都是统一的API.
php常被提起的特性是上下文,这个可选的参数甚至在用户空间多数流建立相关的函数中均可用,他做为一个泛化的框架用于向定向包装器或流实现传入/传出额外的信息.
每个流的上下文包含两种内部消息类型,首先最经常使用的是上下文选项.这些值被安排在上下文中一个二维数组中,一般用于改变流包装器的初始化行为.还有一种则是上下文参数.当前提供了一种方式用于在流包装层内部的事件通知.
php的嵌入式可以提供的可不只仅是同步的加载和执行脚本,经过理解php的执行模块各个部分是怎样组合的,甚至给出一个脚本还能够回调到你的宿主应用中,本章将涉及SAPI层提供的I/O钩子带来的好处.
除了加载外部的脚本,和你在上一章看到的相似,你的php嵌入式应用,下面将实现一个类