首頁 > 後端開發 > php教程 > 編譯PHP擴充的方法

編譯PHP擴充的方法

coldplay.xixi
發布: 2023-04-09 15:02:02
轉載
3308 人瀏覽過

編譯PHP擴充的方法

【相關學習推薦:php程式設計(影片)】

建構PHP擴充

你已經知道如何去編譯PHP本身,下一步我們將編譯外部擴充。我們將討論擴充的建置過程和可用的編譯選項。

載入共享擴充

在前一個章節你已經知道,PHP 擴充功能既能建構成靜態函式庫也可以建構成動態函式庫(.so)。大多數靜態函式庫是與PHP 捆綁在一起編譯的,動態函式庫可以明確地傳遞參數 --enable-EXTNAME=shared 或 --with-EXTNAME=shared 給 ./configure

靜態擴充預設是可用的,動態函式庫需要增加 extension 或 zend_extension 的 ini 配置。倆者可以是絕對路徑,也可以是相對路徑。

例如編譯 PHP 擴充用專案的設定項目:

~/php-src> ./configure --prefix=$HOME/myphp
                       --enable-debug --enable-maintainer-zts
                       --enable-opcache --with-gmp=shared
登入後複製

這個範例中 opcache 擴充和 GMP 擴充都被編譯為位於 modules/ 目錄中的共用物件。您可以透過更改extension_dir或透過傳遞絕對路徑來載入:

~/php-src> sapi/cli/php -dzend_extension=`pwd`/modules/opcache.so
                        -dextension=`pwd`/modules/gmp.so
# or
~/php-src> sapi/cli/php -dextension_dir=`pwd`/modules
                        -dzend_extension=opcache.so -dextension=gmp.so
登入後複製

在 make install 步驟中,這兩個 .so 文件會被移進PHP 安裝的擴充目錄,你使用 php-config --extension-dir 指令可能可以找到它。對於上面的建置選項,它將是 /home/myuser/myphp/lib/php/extensions/no-debug-non-zts-MODULE_API。這個值也是 extension_dir 配置選項的預設值,所以你無需明確地指定它,就可以直接載入進擴充功能:

~/myphp> bin/php -dzend_extension=opcache.so -dextension=gmp.so
登入後複製

這給我們留下了一個問題:你應該使用哪種機制?共享物件使你有一個基本的 PHP 二進位檔案並透過 php.ini 載入其他擴充功能。發行版透過原始的 PHP 軟體包和將擴充功能作為單獨的軟體包分發來利用此功能。另一方面,如果你編譯自己的 PHP 二進位文件,可能不需要這個,因為你已經知道需要哪些擴充。

根據經驗,你將對 PHP 本身捆綁的擴展使用靜態鏈接,並將共享擴展用於其他地方。原因很簡單,就像你稍後看到的,建立外部擴展為共享物件的更容易(或至少減少了侵入性)。另一個好處是你可以在不用重新建立 PHP 的情況下更新擴充功能。

注意

如果你需要有關擴充功能和 Zend 擴充功能之間差異的信息,你可以查閱專門章節。

從 PECL 安裝擴充功能

PECL,PHP 擴充社群庫,提供了大量的 PHP 擴充功能。當擴充功能從主 PHP 發行版中刪除,它們通常也會在 PECL中。同樣,現在與 PHP 捆綁一起的許多擴充功能以前都是 PECL 擴充功能。

除非你在 PHP 建置的設定步驟指定 --without-pear,否則 make install 將PECL 下載並安裝為 PEAR 的一部分。你可以在 $PREFIX/bin 目錄下找到 pecl 腳本。現在安裝擴充功能很簡單,就像執行 pecl install EXTNAME 一樣,例如:

~/myphp> bin/pecl install apcu
登入後複製

該指令將下載、編譯並安裝 APCu 擴充功能。結果會是 apcu.so 檔案在擴充目錄下,可以透過傳遞 extension=apcu.so 設定選項來載入此檔案。

雖然 pecl install 對終端使用者非常方便,但擴充開發人員對它沒什麼興趣。在下面,我們將說明兩種手動建立擴充功能的方式:透過將其匯入主要的 PHP 原始碼樹(允許靜態連結)或透過外部建置(僅共用)。

加入擴展到 PHP 原始碼樹

第三方擴充功能和捆綁在 PHP 的擴充之間沒有根本上的差異。因此你可以透過複製外部擴展到 PHP 原始碼樹,並且和通常的建置過程一樣來建構。我們以APCu 作為範例來示範。

首先,你要把擴充的原始碼放到 PHP 原始碼樹的 ext/EXTNAME 目錄。如果擴充功能可透過Git 取得,就像從ext/ 複製倉庫一樣簡單:

~/php-src/ext> git clone https://github.com/krakjoe/apcu.git
登入後複製

或你也可以下載原始碼壓縮套件並解壓縮它:

/tmp> wget http://pecl.php.net/get/apcu-4.0.2.tgz
/tmp> tar xzf apcu-4.0.2.tgz
/tmp> mkdir ~/php-src/ext/apcu
/tmp> cp -r apcu-4.0.2/. ~/php-src/ext/apcu
登入後複製

此擴充功能會包含一個 config.m4 文件,該文件指定autoconf檔案所使用的特定擴充建置指令。為了將它們包含在 /configure 腳本,你必須再次執行 ./buildconf。為了確保設定檔已經重新生成,建議事先刪除它:

~/php-src> rm configure && ./buildconf --force
登入後複製

現在你可以使用 ./config.nice 腳本將APCu 新增到你的現有配置,或是從全新的配置行開始:

~/php-src> ./config.nice --enable-apcu
# or
~/php-src> ./configure --enable-apcu # --other-options
登入後複製

最后,运行 make -jN 执行实际的构建。由于我们没有使用 --enable-apcu=shared,该扩展已经静态链接到 PHP 库,即不需要额外的操作即可使用它。显然,你也可以使用 make install 去安装最后的二进制文件。

使用 phpize 构建扩展

还可以通过使用构建 PHP章节提及到的 phpize 脚本与 PHP 分开构建。

phpize 的作用与 ./buildconf 用于 PHP 构建的脚本相似:第一,通过$PREFIX/lib/php/build 复制文件导入 PHP 构建系统到你的扩展中。这些文件是 acinclude.m4(PHP 的 M4宏)、phpize.m4(它会在你的扩展中重命名为 configure.in 并包含主要的构建说明)和 run-tests.php

然后 phpize 将调用 autoconf 生成 ./configure 文件,该文件可以自定义扩展构建。注意,没必要传递 --enable-apcu 给它,因为这是隐式假定的。相反,你应该使用 --with-php-config 指定你的 php-config 脚本路径:

/tmp/apcu-4.0.2> ~/myphp/bin/phpize
Configuring for:
PHP Api Version:         20121113
Zend Module Api No:      20121113
Zend Extension Api No:   220121113

/tmp/apcu-4.0.2> ./configure --with-php-config=$HOME/myphp/bin/php-config
/tmp/apcu-4.0.2> make -jN && make install
登入後複製

当你构建扩展时,你应该总是指定 --with-php-config 选项(除非你只有一个全局的 PHP 安装),否则 ./configure 无法确定要构建的 PHP 版本和标志。指定 php-config 脚本也确保了 make install 将移动生成的 .so 文件(可以在 modules/ 目录找到)到正确的扩展目录。

由于在 phpize 阶段还复制了 run-tests.php 文件,因此你可以使用 make test(或显示调用 run-tests)运行扩展测试。

删除已编译对象的 make clean 也是可用的,并且允许你增量构建失败时强制重新构建扩展。 另外 phpize 提供了一个清理选项 phpize --clean。该命令将删除所有 phpize 导入的文件和通过 /configure 脚本生成的文件。

显示关于扩展的信息

PHP CLI 二进制文件提供了几个选项来显示关于扩展的信息。你已经知道 -m,该命令会列出所有已经下载的扩展。你可以利用它来确定扩展是否正确下载了:

~/myphp/bin> ./php -dextension=apcu.so -m | grep apcu
apcu
登入後複製

还有其他一些以 --r 开头的参数都是具有 Reflection 功能。例如,你可以使用 --ri 去显示扩展的配置:

~/myphp/bin> ./php -dextension=apcu.so --ri apcu
apcu

APCu Support => disabled
Version => 4.0.2
APCu Debugging => Disabled
MMAP Support => Enabled
MMAP File Mask =>
Serialization Support => broken
Revision => $Revision: 328290 $
Build Date => Jan  1 2014 16:40:00

Directive => Local Value => Master Value
apc.enabled => On => On
apc.shm_segments => 1 => 1
apc.shm_size => 32M => 32M
apc.entries_hint => 4096 => 4096
apc.gc_ttl => 3600 => 3600
apc.ttl => 0 => 0
# ...
登入後複製

--re 参数列出扩展添加的所有初始设置、常数、函数和类:

~/myphp/bin> ./php -dextension=apcu.so --re apcu
Extension [ <persistent> extension #27 apcu version 4.0.2 ] {
  - INI {
    Entry [ apc.enabled <SYSTEM> ]
      Current = '1'
    }
    Entry [ apc.shm_segments <SYSTEM> ]
      Current = '1'
    }
    # ...
  }

  - Constants [1] {
    Constant [ boolean APCU_APC_FULL_BC ] { 1 }
  }

  - Functions {
    Function [ <internal:apcu> function apcu_cache_info ] {

      - Parameters [2] {
        Parameter #0 [ <optional> $type ]
        Parameter #1 [ <optional> $limited ]
      }
    }
    # ...
  }
}
登入後複製

--re 参数仅适用普通扩展,Zend 扩展使用 --rz 代替。 你可以在 opcache 上尝试:

~/myphp/bin> ./php -dzend_extension=opcache.so --rz "Zend OPcache"
Zend Extension [ Zend OPcache 7.0.3-dev Copyright (c) 1999-2013 by Zend Technologies <http://www.zend.com/> ]
登入後複製

如你所见, 该命令没有显示有用的信息。因为 opcache 同时注册了普通扩展和 Zend 扩展, 前者包含所有初始配置、常量和函数。因此在这个特殊的案例中,你仍然需要使用 --re。其他 Zend 扩展通过 --rz 可得到信息。

扩展 API 兼容性

扩展对5个主要因素非常敏感。如果它们不合适,则该扩展将不会加载到 PHP中,并将无用:

  • PHP Api 版本
  • Zend 模块 Api 编号
  • Zend 扩展 Api 编号
  • 调试模式
  • 线程安全

phpize 工具可让你回想它们的一些信息。所以,如果你在调试模式下构建 PHP,并试图加载和使用非调试模式构建的扩展,那它将无法工作。其他检查也一样。

PHP Api 版本 是内部 API 版本号,Zend 模块 Api 编号 和 Zend 扩展 Api 编号 分别与 PHP 扩展和 Zend 扩展 API 有关。

那些编号随后作为 C 宏传递给正在构建的扩展,以便它本身可以检查那些参数,并在 C 预处理器  #ifdef 的基础上采用不同的代码路径。当那些编号作为宏传给扩展代码,它们会被写在扩展结构中,以便你每次尝试在 PHP 二进制文件中加载该扩展时,都将对照 PHP 二进制文件本身的编号进行检查。如果不匹配,那么该扩展不会被加载,并显示一条错误信息。

如果我们看一下扩展的 C 结构,它看起来像这样:

zend_module_entry foo_module_entry = {
    STANDARD_MODULE_HEADER,
    "foo",
    foo_functions,
    PHP_MINIT(foo),
    PHP_MSHUTDOWN(foo),
    NULL,
    NULL,
    PHP_MINFO(foo),
    PHP_FOO_VERSION,
    STANDARD_MODULE_PROPERTIES
};
登入後複製

至今,对我们来说有趣的是 STANDARD_MODULE_HEADER 宏。如果我们扩展它,我们可以看到:

#define STANDARD_MODULE_HEADER_EX sizeof(zend_module_entry), ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS
#define STANDARD_MODULE_HEADER STANDARD_MODULE_HEADER_EX, NULL, NULL
登入後複製

注意 ZEND_MODULE_API_NOZEND_DEBUG、 USING_ZTS 是如何使用的。

如果查看 PHP 扩展的默认目录,它应该像 no-debug-non-zts-20090626。如你所料,该目录由不同的部分组成:调试模式,其次是线程安全信息,然后是Zend 模块 Api 编号。所以默认情况下,PHP 试图帮你浏览扩展。

注意

通常,当你成为一位内部开发人员或扩展开发人员,必须使用调试参数,并且如果必须处理 Windows 平台,线程也会显示出来。你可以针对那些参数的多种情况多次编译同一扩展。
记住,每次新的 PHP 主要/次要版本都会更改参数,比如 PHP Api 版本,这就是为什么你需要针对新的 PHP 版本重新编译的原因。

> /path/to/php70/bin/phpize -v
Configuring for:
PHP Api Version:         20151012
Zend Module Api No:      20151012
Zend Extension Api No:   320151012

> /path/to/php71/bin/phpize -v
Configuring for:
PHP Api Version:         20160303
Zend Module Api No:      20160303
Zend Extension Api No:   320160303

> /path/to/php56/bin/phpize -v
Configuring for:
PHP Api Version:         20131106
Zend Module Api No:      20131226
Zend Extension Api No:   220131226
登入後複製

注意

Zend 模块 Api 编号 本身是使用 年 月 日 的日期格式构建。这是 API 更改和并被标记的日期。Zend 扩展 Api 编号 是 Zend 版本,其次是 Zend 模块 Api 编号

注意

数字太多?是的,一个 API 编号绑定一个 PHP 版本,对任何人来说都足够了,并且可以简化对 PHP 的理解。不幸的是,除了 PHP 版本本身,还增加了3种不同的 API 编号。你应该找哪一个?答案是任何一个:当 PHP 版本演变时,它们三种同时演变。由于历史原因,我们有三种不同编号。

但是,你是一位 C开发人员,不是吗?为什么不根据这些数字构建一个“兼容的”头文件?我们在我们的扩展中使用了类似这些:

#include "php.h"
#include "Zend/zend_extensions.h"

#define PHP_5_5_X_API_NO            220121212
#define PHP_5_6_X_API_NO            220131226

#define PHP_7_0_X_API_NO            320151012
#define PHP_7_1_X_API_NO            320160303
#define PHP_7_2_X_API_NO            320160731

#define IS_PHP_72          ZEND_EXTENSION_API_NO == PHP_7_2_X_API_NO
#define IS_AT_LEAST_PHP_72 ZEND_EXTENSION_API_NO >= PHP_7_2_X_API_NO

#define IS_PHP_71          ZEND_EXTENSION_API_NO == PHP_7_1_X_API_NO
#define IS_AT_LEAST_PHP_71 ZEND_EXTENSION_API_NO >= PHP_7_1_X_API_NO

#define IS_PHP_70          ZEND_EXTENSION_API_NO == PHP_7_0_X_API_NO
#define IS_AT_LEAST_PHP_70 ZEND_EXTENSION_API_NO >= PHP_7_0_X_API_NO

#define IS_PHP_56          ZEND_EXTENSION_API_NO == PHP_5_6_X_API_NO
#define IS_AT_LEAST_PHP_56 (ZEND_EXTENSION_API_NO >= PHP_5_6_X_API_NO && ZEND_EXTENSION_API_NO < PHP_7_0_X_API_NO)

#define IS_PHP_55          ZEND_EXTENSION_API_NO == PHP_5_5_X_API_NO
#define IS_AT_LEAST_PHP_55 (ZEND_EXTENSION_API_NO >= PHP_5_5_X_API_NO && ZEND_EXTENSION_API_NO < PHP_7_0_X_API_NO)

#if ZEND_EXTENSION_API_NO >= PHP_7_0_X_API_NO
#define IS_PHP_7 1
#define IS_PHP_5 0
#else
#define IS_PHP_7 0
#define IS_PHP_5 1
#endif
登入後複製

看见了?

或者更简单(更好)的是使用 PHP_VERSION_ID ,这你可能更熟悉:

#if PHP_VERSION_ID >= 50600
登入後複製

想了解更多编程学习,敬请关注php培训栏目!

以上是編譯PHP擴充的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:learnku.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板