【관련 학습 추천: php 프로그래밍(동영상)】
PHP 자체를 컴파일하는 방법을 이미 알고 계시다면, 다음으로 외부 확장을 컴파일하겠습니다. 확장된 빌드 프로세스와 사용 가능한 컴파일 옵션에 대해 논의하겠습니다.
이전 장에서 이미 알고 있듯이 PHP 확장은 정적 라이브러리나 동적 라이브러리(.so
)에 내장될 수 있습니다. 대부분의 정적 라이브러리는 PHP와 함께 번들로 컴파일되며 동적 라이브러리는 --enable-EXTNAME=shared
또는 --with-EXTNAME=shared
제공 매개변수를 명시적으로 전달할 수 있습니다. /구성
. .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,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 源码树,并和通常的构建过程一样来构建。我们以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
~/php-src> ./config.nice --enable-apcu # or ~/php-src> ./configure --enable-apcu # --other-options
modules/
디렉터리에 있는 공유 개체로 컴파일됩니다. extension_dir
을 변경하거나 절대 경로를 전달하여 로드할 수 있습니다: 🎜/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
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 -dextension=apcu.so -m | grep apcu apcu
🎜NOTE🎜🎜 Extension과 Zend Extension의 차이점에 대한 정보가 필요하면 해당 섹션을 확인하세요. 🎜🎜PECL에서 확장 설치 🎜🎜PECL, PHP 확장 커뮤니티 라이브러리는 다양한 PHP 확장을 제공합니다. 확장 기능이 기본 PHP 배포판에서 제거되면 일반적으로 여전히 PECL에 있습니다. 마찬가지로 현재 PHP와 함께 번들로 제공되는 많은 확장 기능은 이전에는 PECL 확장 기능이었습니다. 🎜🎜PHP 빌드의 구성 단계에서
--without-pear
를 지정하지 않는 한 make install
은 PEAR의 일부로 PECL을 다운로드하고 설치합니다. $PREFIX/bin
디렉토리에서 pecl
스크립트를 찾을 수 있습니다. 확장 프로그램 설치는 이제 pecl install EXTNAME
을 실행하는 것만큼 간단합니다. 예: 🎜~/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 # ...
extension=apcu.so
구성 옵션을 전달하여 로드할 수 있는 확장 디렉터리에 apcu.so
파일이 됩니다. 🎜🎜pecl install
은 최종 사용자에게는 매우 편리하지만 확장 프로그램 개발자에게는 거의 관심이 없습니다. 아래에서는 확장을 수동으로 빌드하는 두 가지 방법, 즉 확장을 기본 PHP 소스 트리로 가져오는 방법(정적 링크 허용) 또는 외부에서 빌드하는 방법(공유만)에 대해 설명합니다. 🎜🎜PHP 소스 트리에 확장 추가🎜🎜타사 확장과 PHP와 함께 번들로 제공되는 확장 사이에는 근본적인 차이가 없습니다. 따라서 외부 확장을 PHP 소스 트리에 복사하고 평소대로 빌드할 수 있습니다. 우리는 설명을 위해 APCu를 예로 사용합니다. 🎜🎜먼저 확장 소스 코드를 PHP 소스 트리의 ext/EXTNAME
디렉터리에 넣어야 합니다. Git을 통해 확장 프로그램을 사용할 수 있는 경우 ext/
에서 저장소를 복제하는 것만큼 간단합니다. 🎜~/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 ] } } # ... } }
~/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/> ]
config.m4
파일은 autoconf 파일에서 사용되는 특정 확장 빌드 지침을 지정합니다. 이를 /configure
스크립트에 포함하려면 ./buildconf
를 다시 실행해야 합니다. 구성 파일이 다시 생성되었는지 확인하려면 미리 삭제하는 것이 좋습니다. 🎜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 };
./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
可得到信息。
扩展对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_NO
、ZEND_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 중국어 웹사이트의 기타 관련 기사를 참조하세요!