從零開始寫 PHP 擴展(一)

PHP 是用 C 語言寫的。對於每一個 PHPer 來講,都有着心裏的一種但願寫擴展的衝動了吧。然而,缺少一個很好的切入點。Google 上搜 PHP 擴展開發,大部分都是複製品文章,甚至有些人連操做都沒有操做過就搬運在了本身的博客。不過也有幾篇好教程,可是都是 PHP 5 時代的產物,隱藏着很是多的坑。我會將我本身慢慢踩坑的過程記錄下來,也許這就成了其它人的「教程」了吧。php

生成一個擴展

想必不少人已經看到不少網上的教程了。大多都是教咱們執行這個命令:$ ./ext_skel --extname=extname。可是,當你 clone 了 PHP 源碼後會發現,master 分支下並無 ext/ext_skel 這個文件。因此,我總結了一下:數組

若是你是直接下載 PHP 的源碼,或者在已經 release 的版本分之下,你能夠執行這個命令bash

$ cd ext
$ ./ext_skel --extname=extname

若是你是直接在 master 分支下,只有 ext_skel.php 文件,這個時候你就直接能夠執行這個 PHP 文件函數

$ cd ext
$ php ext_skel.php --ext extname

因爲我是直接在 master 分支下開發的,因此後面的都是默認在 master 分之下的操做。測試

生成了擴展以後,咱們會看到四個文件和一個文件夾。如今這個階段,咱們只須要用到兩個文件,.c 文件和 .h 文件。翻譯

一個小坑

在咱們生成好擴展以後,咱們能夠試着編譯一下code

$ phpize
$ ./configure
$ make && make test

咱們會驚訝地發現,編譯的時候會有一個 warning。教程

warning: implicit declaration of function
      'ZEND_PARSE_PARAMETERS_NONE' is invalid in C99 [-Wimplicit-function-declaration]
        ZEND_PARSE_PARAMETERS_NONE();
        ^
1 warning generated.

而後你再執行 make test 發現有一個測試沒有經過。沒錯,腳本爲咱們生成好的文件,竟然通不過本身的測試。有沒有以爲很詭異。咱們看看 warning 的具體信息。找不到函數 ZEND_PARSE_PARAMETERS_NONE。看了一下文件,發如今第 15 行。看看這個函數名大概也能猜出來是什麼意思了。因而我去 PHP 源碼裏搜了一下。但是咱們發現了這樣一個宏定義。ci

#ifndef zend_parse_parameters_none
#define zend_parse_parameters_none()    \
        zend_parse_parameters(ZEND_NUM_ARGS(), "")
#endif

替換掉原來的大寫以後,就沒有 warning 了。這也算是官方給咱們挖了一個小坑吧。雖然大寫的有宏定義,可是爲何會報錯,我也不太清楚了。開發

定義一個函數

我想,大多數人寫擴展,確定至少但願實現一個函數,不會是要幾個全局變量就去寫個擴展的吧(霧

這裏 PHP 給咱們提供了一個有用的宏 PHP_FUNCTION。生成好的代碼裏也有定義好的兩個函數,能夠參照它的用法。這個宏最終會被翻譯成一個函數。例如 PHP_FUNCTION(name) 最終會被翻譯成 void zif_name(zend_execute_data *execute_data, zval *return_value)

同時咱們看到有定義了這麼一個數組

const zend_function_entry cesium_functions[] = {
    PHP_FE(cesium_test1,        arginfo_cesium_test1)
    PHP_FE(cesium_test2,        arginfo_cesium_test2)
    PHP_FE_END
};

咱們須要將新添加的函數添加到這個數組裏。像這樣

const zend_function_entry cesium_functions[] = {
    PHP_FE(cesium_test1,        arginfo_cesium_test1)
    PHP_FE(cesium_test2,        arginfo_cesium_test2)
    PHP_FE(name,           NULL)
    PHP_FE_END
};

記住,結尾不要加分號或者逗號。最後,咱們能夠個這個函數一個輸出

PHP_FUNCTION(name)
{
    php_printf("Hello\n");
}

編譯安裝完了以後咱們就能夠使用這個函數了

總結

本文僅僅是展現了從建立擴展開始到運行的全過程,本着能運行的心態來走完這些流程。

因爲做者水平有限,若有錯誤,敬請不吝賜教。

相關文章
相關標籤/搜索