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嵌入式應用,下面將實現一個類