Maison  >  Article  >  développement back-end  >  Comment compiler des extensions PHP

Comment compiler des extensions PHP

coldplay.xixi
coldplay.xixiavant
2020-09-03 16:21:053214parcourir

Comment compiler des extensions PHP

[Recommandations d'apprentissage associées : programmation php (vidéo)]

Créer des extensions PHP

Vous savez déjà Comment compiler PHP lui-même, nous compilerons ensuite les extensions externes. Nous discuterons du processus de construction étendu et des options de compilation disponibles.

Chargement des extensions partagées

Comme vous le savez déjà dans le chapitre précédent, les extensions PHP peuvent être construites soit sous forme de bibliothèques statiques, soit sous forme de bibliothèques dynamiques (.so). La plupart des bibliothèques statiques sont compilées avec PHP et les bibliothèques dynamiques peuvent transmettre explicitement les paramètres --enable-EXTNAME=shared ou --with-EXTNAME=shared à ./configure.

Les extensions statiques sont disponibles par défaut, et les bibliothèques dynamiques doivent ajouter une extension ou une configuration ini zend_extension. Les deux peuvent être des chemins absolus ou relatifs.

Par exemple, les éléments de configuration pour compiler un projet d'extension PHP :

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

Dans cet exemple, l'extension opcache et l'extension GMP sont compilées en objets partagés situés dans le répertoire modules/ . Vous pouvez le charger en modifiant extension_dir ou en passant le chemin absolu :

~/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

A l'étape make install, ces deux .so fichiers seront déplacés dans le répertoire extensions de votre installation PHP, que vous utilisez la commande php-config --extension-dir pour pouvoir le trouver. Pour les options de construction ci-dessus, ce serait /home/myuser/myphp/lib/php/extensions/no-debug-non-zts-MODULE_API. Cette valeur est également la valeur par défaut pour l'option de configuration extension_dir, vous pouvez donc la charger directement dans l'extension sans la spécifier explicitement :

~/myphp> bin/php -dzend_extension=opcache.so -dextension=gmp.so

Cela nous laisse avec la question : quel mécanisme devez-vous utiliser ? Les objets partagés vous permettent d'avoir un binaire PHP de base et de charger des extensions supplémentaires via php.ini. Les distributions profitent de cette fonctionnalité à la fois via le package PHP d'origine et en distribuant les extensions sous forme de packages séparés. D'un autre côté, si vous compilez vos propres binaires PHP, vous n'en aurez probablement pas besoin puisque vous savez déjà quelles extensions sont requises.

En règle générale, vous utiliserez des liens statiques pour les extensions fournies avec PHP lui-même et utiliserez des extensions partagées pour une utilisation ailleurs. La raison est simple, comme vous le verrez plus tard, il est plus facile (ou du moins moins intrusif) de créer des extensions externes aux objets partagés. Un autre avantage est que vous pouvez mettre à jour les extensions sans reconstruire PHP.

REMARQUE

Si vous avez besoin d'informations sur les différences entre les extensions et les extensions Zend, vous pouvez consulter la section dédiée.

Installer les extensions de PECL

PECL, la Bibliothèque communautaire d'extensions PHP, fournit un grand nombre d'extensions PHP. Lorsque les extensions sont supprimées de la distribution PHP principale, elles sont généralement toujours dans PECL. De même, la plupart des extensions désormais fournies avec PHP étaient auparavant des extensions PECL.

Sauf si vous spécifiez --without-pear dans l'étape de configuration de la version PHP, make install télécharge et installe PECL dans le cadre de PEAR. Vous pouvez trouver le script $PREFIX/bin dans le répertoire pecl. Installer des extensions est désormais aussi simple que d'exécuter pecl install EXTNAME, par exemple :

~/myphp> bin/pecl install apcu

Cette commande téléchargera, compilera et installera l'extension APCu. Le résultat sera un fichier apcu.so dans le répertoire d'extension, qui pourra être chargé en passant l'option de configuration extension=apcu.so.

Bien que pecl install soit très pratique pour les utilisateurs finaux, il suscite peu d'intérêt parmi les développeurs d'extensions. Ci-dessous, nous expliquons deux manières de créer une extension manuellement : en l'important dans l'arborescence des sources PHP principale (permettant des liens statiques) ou en la construisant en externe (partage uniquement).

Ajout d'extensions à l'arborescence des sources PHP

Il n'y a pas de différence fondamentale entre les extensions tierces et les extensions fournies avec PHP. Vous pouvez donc copier l'extension externe dans l'arborescence des sources PHP et la construire comme d'habitude. Nous utilisons APCu comme exemple pour démontrer.

Tout d'abord, vous devez placer le code source de l'extension dans le répertoire ext/EXTNAME de l'arborescence des sources PHP. Si l'extension est disponible via Git, c'est aussi simple que de cloner le dépôt depuis ext/ :

~/php-src/ext> git clone https://github.com/krakjoe/apcu.git

Ou vous pouvez télécharger l'archive tar source et la décompresser :

/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

L'extension contiendra un config.m4 fichier qui spécifie les instructions de construction d'extension spécifiques utilisées par le fichier autoconf. Afin de les inclure dans le script /configure, vous devez réexécuter ./buildconf. Pour vous assurer que le fichier de configuration a été régénéré, il est recommandé de le supprimer au préalable :

~/php-src> rm configure && ./buildconf --force

Vous pouvez maintenant utiliser le script ./config.nice pour ajouter APCu à votre configuration existante, ou repartir d'une nouvelle ligne de configuration :

~/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培训栏目!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer