一般我們自定義的擴展都是以動態編譯方式生成動態鏈接庫.so文件,通過在php.ini讀取擴展信息,php會將動態鏈接庫加載到內存;php的擴展可以分為兩類,一種是php擴展,需要實現zend_module_entry結構體,在php.ini中通過extension=xx.so加載擴展;一種是zend擴展,需要實現zend_extension,在php.ini通過zend_extension=xx.so加載擴展
以下主要記錄php擴展:zend_module_entry定義了擴展的全部信息:擴展名、擴展版本、擴展提供的函數列表以及PHP四個執行階段的hook函數等
### 第一步:生成骨架
位于源代碼的ext目錄下,執行
```c
./ext_skel --extname=hello --proto=hello.def
```
--proto用來指定函數原型,例如
```c
// hello.def
vi hello.def
int wcl(string filename) // 添加wcl函數原型,wcl用于統計文件行數
```
函數原型對應會生成如下代碼:
```c
PHP_FUNCTION(wcl)
{
char *filename = NULL;
int argc = ZEND_NUM_ARGS();
size_t filename_len;
if (zend_parse_parameters(argc, "s", &filename, &filename_len) == FAILURE)
return;
php_error(E_WARNING, "wcl: not yet implemented");
}
```
PHP_FUNCTION(wcl)即我們要實現功能代碼,如果不指定函數原型,需要自己手動添加該函數
### 第二步:config.m4配置編譯信息
config.m4主要用于配置編譯參數(PHP_ARG_ENABLE)和設置擴展的源文件(PHP_NEW_EXTENSION)
```c
PHP_ARG_ENABLE(hello, whether to enable hello support,
dnl Make sure that the comment is aligned:
[ --enable-hello Enable hello support])
```
PHP_ARG_ENABLE對應編譯時的--enable,表示是否啟用擴展,這里將dnl注釋去掉
```c
PHP_NEW_EXTENSION(hello, hello.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
```
- 第一個參數表示擴展名稱
- 第二個參數表示擴展源文件列表,如果有多個源文件,需要以空格隔開;換行時加上反斜杠“\”
### 第三步:實現功能
在ext/hello/hello.c文件中,PHP_FUNCTION實現相應的功能
```c
/* {{{ proto int wcl(string filename)
*/
PHP_FUNCTION(wcl)
{
char *filename = NULL;
int argc = ZEND_NUM_ARGS();
size_t filename_len;
char ch;
FILE *fp;
zend_long lcount = 0;
if (zend_parse_parameters(argc, "s", &filename, &filename_len) == FAILURE)
return;
if ((fp = fopen(filename, "r")) == NULL)
{
RETURN_FALSE;
}
while ((ch = fgetc(fp)) != EOF)
{
if (ch == '\n')
{
lcount++;
}
}
fclose(fp);
RETURN_LONG(lcount);
// php_error(E_WARNING, "wcl: not yet implemented");
}
/* }}} */
```
### 第四步:注冊函數
代碼為hello.c源文件:
```c
/* {{{ hello_functions[]
*
* Every user visible function must have an entry in hello_functions[].
*/
const zend_function_entry hello_functions[] =
{
PHP_FE(confirm_hello_compiled, NULL) /* For testing, remove later. */
PHP_FE(wcl, NULL)
PHP_FE_END /* Must be the last line in hello_functions[] */
};
/* }}} */
```
通過PHP_FE把函數注冊到zend_function_entry;每個擴展會注冊一個名confirm_xxx_compiled的函數用來輸出當前擴展是否已經被編譯到PHP,通過php -f hello.php,如果出現以下信息表示成功
```c
Functions available in the test extension:
confirm_hello_compiled
wcl
Congratulations! You have successfully modified ext/hello/config.m4. Module hello is now compiled into PHP.
```
### 第五步:編譯
```c
cd /opt/php7/php-7.2.10/ext/hello // 去到hello擴展目錄
/opt/php7/php/bin/phpize
./configure --with-php-config=/opt/php7/php/bin/php-config // 指定php-config文件
make
sudo make install
```
### 第六步:php.ini添加擴展
```c
/opt/php7/php/bin/php --ini // 找到配置文件位置
vi /opt/php7/php/ext/php.ini
extension=hello.so // 在文件末尾添加
```
### 配置項
以上一個基本的擴展已經完成,如果需要設置配置項,如在php.ini注冊一個ini配置項來控制是否計算空行,過程如下:
#### php_hello.h聲明擴展內的全局變量
添加代碼如下:
```c
ZEND_BEGIN_MODULE_GLOBALS(hello)
zend_long filter_blank;
ZEND_END_MODULE_GLOBALS(hello)
```
擴展內全局變量通過HELLO_G(v)讀取,源碼如下:
```c
#define HELLO_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(hello, v)
```
#### hello.c添加配置項
```c
ZEND_DECLARE_MODULE_GLOBALS(hello)
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("hello.filter_blank", "0", PHP_INI_ALL, OnUpdateLong, filter_blank, zend_hello_globals, hello_globals)
PHP_INI_END()
```
以上代碼為當前擴展注冊了一個配置項hello.filter_blank
#### 模塊初始化自動注冊當前擴展配置項
```c
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(hello)
{
/* If you have INI entries, uncomment these lines
REGISTER_INI_ENTRIES();
*/
REGISTER_INI_ENTRIES(); // 去掉注釋后
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(hello)
{
/* uncomment this line if you have INI entries
UNREGISTER_INI_ENTRIES();
*/
UNREGISTER_INI_ENTRIES(); // 去掉注釋后
return SUCCESS;
}
/* }}} */
```
#### php.ini添加配置

- php
- 編譯安裝
- 基本概念
- 垃圾回收機制
- 生命周期
- zval底層實現
- c擴展開發
- gdb調試工具
- 自定義擴展簡單demo
- 鉤子函數
- 讀取php.ini配置
- 數組
- 函數
- 類
- yaf擴展底層源碼
- swoole擴展底層源碼
- memoryGlobal內存池
- swoole協程使用記錄
- 單點登錄sso原理
- compser使用
- session實現機制
- c & linux
- gcc
- 指針
- 結構體,聯合和位字段
- 宏定義井號說明
- printf家族函數和可變參數
- 共享函數
- 靜態庫和動態庫
- makefile自動化構建
- 信號一
- 信號二
- inotify監控文件事件
- socket編程
- 簡介
- UNIX DOMAIN
- Internet DOMAIN
- TCP/IP
- 文件IO多路復用
- 內存管理
- 進程組,會話和控制終端
- daemon守護進程
- 多進程
- 多線程
- 常用進制轉換
- go
- 入門知識
- 字節和整數裝換
- python
- redis
- 應用場景
- 消息隊列
- 熱點數據
- 掃碼登錄
- 訂閱發布
- 次數限制
- 搶購超賣
- 持久化機制
- mysql
- 工作流程
- MyISAM和InnoDB區別
- 用戶和權限管理
- 執行計劃
- sql優化
- 事務和鎖
- 慢查詢日志
- case...when...then...end用法
- sql
- 參考
- linux
- 內核參數優化
- 防火墻設置
- docker
- docker入門知識
- 算法
- 多維數組合
- DFA算法
- 紅包金額分配