PHP內核的學習--建立PHP擴展

開始看PHP內核也有一段時間了,如今開始邊學邊總結,今天就總結一下如何建立本身的PHP擴展。php

個人環境以下:html

系統:Ubuntu 14.04web

php版本:5.5.19數據庫

參考摘錄:用C/C++擴展你的PHPsvn

PHP取得成功的一個主要緣由之一是它擁有大量的可用擴展。web開發者不管有何種需求,這種需求最有可能在PHP發行包裏找到。PHP發行包包括支持各類數據庫,圖形文件格式,壓縮,XML技術擴展在內的許多擴展。函數

擴展API的引入使PHP3取得了巨大的進展,擴展API機制使PHP開發社區很容易的開發出幾十種擴展。如今,兩個版本過去了,API仍然和PHP3時的很是類似。擴展主要的思想是:儘量的從擴展編寫者那裏隱藏PHP的內部機制和腳本引擎自己,僅僅須要開發者熟悉API。php-fpm

有兩個理由須要本身編寫PHP擴展。第一個理由是:PHP須要支持一項她還未支持的技術。這一般包括包裹一些現成的C函數庫,以便提供PHP接口。例如,若是一個叫FooBase的數據庫已推出市場,你須要創建一個PHP擴展幫助你從PHP裏調用FooBase的C函數庫。這個工做可能僅由一我的完成,而後被整個PHP社區共享(若是你願意的話)。第二個不是很廣泛的理由是:你須要從性能或功能的緣由考慮來編寫一些商業邏輯。性能

假設你正在開發一個網站,須要一個把字符串重複n次的函數。下面是用PHP寫的例子:測試

function util_str_repeat($string, $n){
    $result = "";
    for($i = 0; $i < $n; $i++){
        $result .= $string;
    }
    return $result;
}
 
util_str_repeat("One", 3);// returns "OneOneOne".
util_str_repeat("One", 1);// returns "One".

假設因爲一些奇怪的緣由,你須要時常調用這個函數,並且還要傳給函數很長的字符串和大值n。這意味着在腳本里有至關巨大的字符串鏈接量和內存從新分配過程,以致顯著地下降腳本執行速度。若是有一個函數可以更快地分配大量且足夠的內存來存放結果字符串,而後把$string重複n次,就不須要在每次循環迭代中分配內存。網站

爲擴展創建函數的第一步是寫一個函數定義文件,該函數定義文件定義了擴展對外提供的函數原形。該例中,定義函數只有一行函數原形util_str_repeat() :

string util_str_repeat(string str, int n)

函數定義文件的通常格式是一個函數一行。你能夠定義可選參數和使用大量的PHP類型,包括: bool, float, int, array等。

保存爲util.def文件至PHP原代碼目錄樹下(即與ext_skel文件放在同一目錄下,個人目錄是/usr/share/php5/)。

而後就是經過擴展骨架(skeleton)構造器運行函數定義文件的時機了。該構造器腳本就是ext_skel。假設你把函數定義保存在一個叫作util.def的文件裏,並且你但願把擴展取名爲util,運行下面的命令來創建擴展骨架:

sudo ./ext_skel --extname=util --proto=util.def

執行以後,我這裏報了以下錯誤:

./ext_skel: 1: cd: can't cd to /usr/lib/php5/skeleton
Creating directory util
awk: cannot open /create_stubs (No such file or directory)
Creating basic files: config.m4 config.w32 .svnignore util.c./ext_skel: 216: ./ext_skel: cannot open /skeleton.c: No such file
 php_util.h./ext_skel: 234: ./ext_skel: cannot open /php_skeleton.h: No such file
 CREDITS./ext_skel: 238: ./ext_skel: cannot open /CREDITS: No such file
 EXPERIMENTAL./ext_skel: 242: ./ext_skel: cannot open /EXPERIMENTAL: No such file
 tests/001.phpt./ext_skel: 247: ./ext_skel: cannot open /tests/001.phpt: No such file
 util.php./ext_skel: 251: ./ext_skel: cannot open /skeleton.php: No such file
rm: cannot remove ‘function_entries’: No such file or directory
rm: cannot remove ‘function_declarations’: No such file or directory
rm: cannot remove ‘function_stubs’: No such file or directory
 [done].

To use your new extension, you will have to execute the following steps:

1.  $ cd ..
2.  $ vi ext/util/config.m4
3.  $ ./buildconf
4.  $ ./configure --[with|enable]-util
5.  $ make
6.  $ ./php -f ext/util/util.php
7.  $ vi ext/util/util.c
8.  $ make

Repeat steps 3-6 until you are satisfied with ext/util/config.m4 and
step 6 confirms that your module is compiled into PHP. Then, start writing
code and repeat the last two steps as often as necessary.

很明顯是/usr/lib/php5/skeleton路徑的錯誤,編輯ext_skel文件,將/usr/lib/php5/skeleton修改成/usr/share/php5/skeleton,而後移除掉生成的util文件夾,再次執行以前的命令,成功後提示以下:

Creating directory util
Creating basic files: config.m4 config.w32 .svnignore util.c php_util.h CREDITS EXPERIMENTAL tests/001.phpt util.php [done].

To use your new extension, you will have to execute the following steps:

1.  $ cd ..
2.  $ vi ext/util/config.m4
3.  $ ./buildconf
4.  $ ./configure --[with|enable]-util
5.  $ make
6.  $ ./php -f ext/util/util.php
7.  $ vi ext/util/util.c
8.  $ make

Repeat steps 3-6 until you are satisfied with ext/util/config.m4 and
step 6 confirms that your module is compiled into PHP. Then, start writing
code and repeat the last two steps as often as necessary.

而後採用靜態編譯的方式編譯擴展。爲了使擴展可以被編譯,須要修改擴展目錄util/下的config.m4文件。擴展沒有包裹任何外部的C庫,你須要添加支持–enable-util配置開關到PHP編譯系統裏(–with-extension 開關用於那些須要用戶指定相關C庫路徑的擴展)。找到以下內容:

dnl PHP_ARG_ENABLE(util, whether to enable util support,
dnl Make sure that the comment is aligned:
dnl [  --enable-util           Enable util support])

將前面的dnl 去掉,修改成以下結果:

PHP_ARG_ENABLE(util, whether to enable util support,
Make sure that the comment is aligned:
[  --enable-util           Enable util support])

而後修改util.c文件,找到以下代碼:

PHP_FUNCTION(util_str_repeat)
{
    char *str = NULL;
    int argc = ZEND_NUM_ARGS();
    int str_len;
    long n;

    if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE) 
        return;

    php_error(E_WARNING, "util_str_repeat: not yet implemented");
}

將其修改成以下代碼:

PHP_FUNCTION(util_str_repeat)
{
    char *str = NULL;
    int argc = ZEND_NUM_ARGS();
    int str_len;
    long n;
    char *result; /* Points to resulting string */
    char *ptr; /* Points at the next location we want to copy to */
    int result_length; /* Length of resulting string */

    if (zend_parse_parameters(argc TSRMLS_CC, "sl", &str, &str_len, &n) == FAILURE)
        return;

    /* Calculate length of result */
    result_length = (str_len * n);
    /* Allocate memory for result */
    result = (char *) emalloc(result_length + 1);
    /* Point at the beginning of the result */
    ptr = result;

    while (n--) {
        /* Copy str to the result */
        memcpy(ptr, str, str_len);
        /* Increment ptr to point at the next position we want to write to */
        ptr += str_len;
    }
/* Null terminate the result. Always null-terminate your strings even if they are binary strings */ *ptr = '\0'; /* Return result to the scripting engine without duplicating it*/ RETURN_STRINGL(result, result_length, 0); }

裏面的具體內容,就不在這裏說了,以後會慢慢寫到。

而後就是編譯,安裝。在util目錄下,命令以下(命令可能都須要加sudo):

phpize
./configure
make
make test
make install

而後配置生成的擴展文件,在php5.5版本中,進入到/etc/php5/mods-available目錄下,建立util.ini文件,寫入以下內容:

extension=util.so

而後enable util擴展

sudo php5enmod util

最後,重啓php-fpm

sudo service php5-fpm restart

建立一個php文件,測試一下,測試文件以下:

<?php
for ($i = 1; $i <= 3; $i++) {
    print util_str_repeat("CraryPrimitiveMan ", $i);
    print "\n";
}
?>

執行結果以下:

CraryPrimitiveMan 
CraryPrimitiveMan CraryPrimitiveMan 
CraryPrimitiveMan CraryPrimitiveMan CraryPrimitiveMan

這樣咱們就成功建立了一個包含簡單的PHP函數的擴展。

盜圖一張~~

今天就先到這裏~~

相關文章
相關標籤/搜索