從零開始寫 PHP 擴展(一)

2021-03-02 SegmentFault

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

生成一個擴展

想必很多人已經看到很多網上的教程了。大多都是教我們執行這個命令: $./ext_skel--extname=extname。但是,當你 clone 了 PHP 源碼後會發現,master 分支下並沒有 ext/ext_skel 這個文件。所以,我總結了一下:

如果你是直接下載 PHP 的源碼,或者在已經 release 的版本分之下,你可以執行這個命令

$ cd ext

$ ./ext_skel --extname=extname

如果你是直接在 master 分支下,只有 ext_skel.php 文件,這個時候你就直接可以執行這個 PHP 文件

$ cd ext

$ php ext_skel.php --ext extname

由於我是直接在 master 分支下開發的,所以後面的都是默認在 master 分之下的操作。

生成了擴展之後,我們會看到四個文件和一個文件夾。現在這個階段,我們只需要用到兩個文件, .c 文件和 .h 文件。

一個小坑

在我們生成好擴展之後,我們可以試著編譯一下

$ 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 源碼裡搜了一下。可是我們發現了這樣一個宏定義。

#ifndef zend_parse_parameters_none

#define zend_parse_parameters_none()    \

       zend_parse_parameters(ZEND_NUM_ARGS(), "")

#endif

替換掉原來的大寫之後,就沒有 warning 了。這也算是官方給我們挖了一個小坑吧。雖然大寫的有宏定義,但是為什麼會報錯,我也不太清楚了。

定義一個函數

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

這裡 PHP 給我們提供了一個有用的宏 PHP_FUNCTION。生成好的代碼裡也有定義好的兩個函數,可以參照它的用法。這個宏最終會被翻譯成一個函數。例如 PHP_FUNCTION(name) 最終會被翻譯成 voidzif_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");

}

編譯安裝完了之後我們就可以使用這個函數了

總結

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

由於作者水平有限,如有錯誤,敬請不吝賜教。

相關講堂推薦

相關焦點

  • (基礎篇) PHP需要它—PHP擴展庫
    ----新來的同學可以查看歷史分享學習----從PHP 5開始,PHP即新增了內置的標準擴展庫,包括XML擴展庫-DOM、SimpleXML、SPL、SQLite等,而像在Windows下加載擴展庫,是通過修改php.ini文件來完成的。用戶也可以在腳本中通過使用dl()函數來動態加載。PHP擴展庫的DLL文件都具有php_前綴。很多擴展庫都內置於Windows版本的PHP中,要加載這些擴展庫不需要額外的DLL文件和extension配置指令。Windows下的PHP擴展庫列表列出了需要或曾經需要額外DLL文件的擴展庫。
  • PHP擴展開發:[1]簡單的HelloWord
    最近摸索CentOS7.0系統的操作比較多,而php對應的擴展部署加載也用的比較多,但是一直都沒有想去開發一個屬於自己的擴展,剛好最近需要用到這個擴展開發,開發一些小功能,所以就開始著手寫一個比較簡單的HelloWord擴展來記錄下。
  • 從0開始的PHP RASP的學習
    環境搭建開發流程php版本7.0.33,為了方便開發擴展,先下載源碼:wget https://github.com/php/php-src/archive/php-7.0.33.zip解壓後,在php源碼裡有一個代碼生成器ext_skel,位於php-src-php-7.0.33/ext,先構建擴展基本文件:.
  • PHP的Sodium加密擴展函數了解
    PHP的Sodium加密擴展函數了解這是本次加密擴展系列的最後一篇文章,也是我們要學習了解的最後一個 PHP 加密擴展。Sodium 出現的目的也是為了代替 Mcrypt 這個原來的加密擴展。在 PHP7.2 之後,Mcrypt 已經被移除,在 PHP7.1 時就已經被標記為過時。
  • 手把手擼PHP擴展 0x03: 理解PHP生命周期的過程
    修改這幾個函數的內容:PHP_MINIT_FUNCTION(study){ php_printf("MINIT\n"); return SUCCESS;}PHP_MSHUTDOWN_FUNCTION(study){ php_printf("MSHUTDOWN\n"); return SUCCESS;}
  • PHP的Mhash擴展函數的學習
    PHP的Mhash擴展函數的學習這次我們要學習的又是一個 Hash 加密擴展。不過這個擴展 Mhash 已經集成在了 Hash 擴展中。同時也需要注意的是,這個擴展已經不推薦使用了,我們應該直接使用 Hash 擴展中的函數來進行 Hash 加密操作。所以,我們今天仍然是以學習為目的的進行了解。
  • PHP的Hash信息摘要擴展框架
    PHP的Hash信息摘要擴展框架今天我們主要學習的是 PHP 中一些 Hash 散列加密相關的擴展函數的使用,而不是 Hash 算法,這種加密其實也只是一種更複雜一些的密鑰算法,與 Hash 算法類似的是,我們輸入的一串字符串,就像一個 Hash 表一樣有其對應的 Hash 散列值,本質上和普通的數據結構中的 Hash 鍵值映射是一個道理,
  • PHP中非常好玩的Calendar擴展學習
    PHP中非常好玩的Calendar擴展學習為什麼說這個 Calendar 擴展很好玩呢?因為你基本用不到它!這個擴展是一套關於日期曆法的擴展,但是對於我們來說,它沒有農曆的相關操作,所以對於我們中國人來說這個擴展並沒有什麼實際的作用。不過這並不妨礙我們去了解學習它。
  • 在MAC版MAMP環境下為PHP7.x安裝redis擴展的過程
    MAMP & MAMP Pro軟體是一款很好的在MAC下面運行的網站集成環境軟體,其由Apache+MySQL+PHP+動態DNS配置構成,PHP的版本可以動態切換到最新版,功能強大,配置簡單,十分便於本地調試。
  • php語言入門教程(PHP編程學習路線圖)
    php語言入門應該從哪裡開始學起呢?是先學html還是php?還是直接學習PHP框架?
  • PHP零基礎入門
    string htmlspecialchars(string $str [,int $flags = ENT_COMPAT)Itrim函數,用於實現刪除字符串開始位置的空格或其他字符,用於刪除開始的空格,後面不管。rtrim函數用於實現刪除字符串結束位置的空格,或者是其他字符。
  • php5.3以上版本連接sqlserver資料庫方法 sqlsrv擴展安裝
    sqlserver2008及以上版本,微軟廢棄了對php自帶擴展的支持,php5.3以上版本默認不再支持原有的sqlserver連接方式,需要安裝微軟官方提供的驅動以及sqlsrv、pdo_sqlsrv擴展。
  • Jsonnet-PHP v1.2.0 發布, PHP 支持 Jsonnet 擴展
    Jsonnet-PHP v1.2.0發布了,JsonNet-PHP是Google Jsonnet對PHP的支持擴展,該版本使用最新版本至v0.9.5的LibJsonnet。
  • 解密混淆的PHP程序
    PHP代碼混淆一般來說有兩種方法:本文我們主要講解無需PHP擴展的代碼混淆的解密。大多數的無需擴展的php代碼混淆原理上都是使用eval進行代碼的執行。所以我們可以通過寫一個簡單的PHP代碼,看能否在compile_string中獲取到eval參數的值 <?php     eval("phpinfo();"); ?
  • 給PHP開發者講講PHP源碼(2)
    如果你錯過了那篇文章,在你開始讀這篇文章之前也許你應該讀一下它。在這篇文章中,我們談論的是定位PHP內部函數的定義,以及理解它們的原理。如何找到函數的定義作為開始,讓我們嘗試找出strpos函數的定義。
  • php異步高並發擴展swoole-1.6.10版發布 - OSCHINA - 中文開源技術...
    簡介: swoole是一個php版本的異步、高並發擴展,是國人被php官方pecl包收錄的力作之一。
  • PHP程序守護進程化
    glibc裡有一個函數daemon。調用此函數,就可使當前進程脫離終端變成一個守護進程,具體內容參見man daemon。PHP中暫時沒有此函數,當然如果你有興趣的話,可以寫一個PHP的擴展函數來實現。
  • PHP基礎入門
    php//定義需要往返的次數,老外喜歡從0開始計數,所以系統也是從0開始$count=0;//while後面接布爾值判斷,為真執行,為假停止//$count小於100的時候執行,也就是$count為0至99的時候執行.
  • 工欲善其事必先利其器,盤點Github上那些優秀的PHP項目
    swoole實現了php的異步、多線程,正如官方所說「重新定義PHP」,也許這才是swool的精髓吧。對於phper來說,以前做web開發主要是寫業務邏輯,只需要php的知識即可勝任,使用swool編程則需要補齊多線程編程的知識、進程間通信的知識、網絡協議TCP/UDP的知識,否則很多地方理解起來會有難度。
  • php語言是什麼?學好php需要掌握什麼?
    php的語法有C、Java和Perl的特點也有自創的特點,經過了24年的發展php從最初的1.0發展7.0版本,php的功能已經發展非常強大,php是適合編程零基礎入手的一門程式語言,下面為大家介紹一下學習php需要經過哪些過程?