【相关学习推荐: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 [ extension #27 apcu version 4.0.2 ] {

- INI {

Entry [ apc.enabled ]

Current = '1'

}

Entry [ apc.shm_segments ]

Current = '1'

}

# ...

}

- Constants [1] {

Constant [ boolean APCU_APC_FULL_BC ] { 1 }

}

- Functions {

Function [ function apcu_cache_info ] {

- Parameters [2] {

Parameter #0 [ $type ]

Parameter #1 [ $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 ]

如你所见, 该命令没有显示有用的信息。因为 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_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扩展的方法相关推荐

  1. 如何将内核静态库编译连接到驱动程序中去【转】

    转自:http://blog.csdn.net/ganjianfeng2003/article/details/8089551 如何将内核静态库编译连接到驱动程序中去 2010-12-07 08:27 ...

  2. 将静态库编译到动态库中

    最近做一个东西,需要采集硬件设备的音视频数据,然后做编码.以前做过编码部分直接拽过来使用,只写硬件采集部分即可.  调查发现,硬件采集驱动过于老旧,必须使用ffmpeg3的API,而编码部分则使用了f ...

  3. 动态库、静态库编译测试:含静态库链接动态库、静态库,动态库链接静态库、动态库

    本文的目的是测试各种类型库的编译后的使用效果,包括库又链接其他库的编译方法,使用方法,依赖性等. 太长不看版:请跳至文章最后的总结对比表. 一.内容包含: ①静态库libbb.a依赖静态库libaa. ...

  4. linux opencv编译静态库,使用openCV的静态库编译

    转载请注明出处: By 少侠阿朱 摘要: 本文主要讲述如何使用opencv静态库进行编译,生成脱离opencv环境可执行.exe文件. 实现的效果: 此方法生成的exe文件在其他没有配置openCV环 ...

  5. libcurl linux 静态链接库_Linux ubuntu OpenSSL + curl 静态库编译及使用

    下载源码 源码编译 解压之后,进入源码目录openssl-1.1.0f,执行如下命令.因为只需要编译静态库,也没有特殊要求,所以使用的编译选项配置很简单: ./config -fPIC no-shar ...

  6. [转]gcc下程序调用静态库编译命令:主文件必须在静态库前面!

    很容易犯的错误,转载以备注! 转载请注明文章地址,尊重作者赖半仙的劳动成果,谢谢支持: http://hi.baidu.com/mgqw/blog/item/0969c4230a2508559922e ...

  7. boost中静态库编译没有-fPIC选项的问题解决方案

    使用libboostpython.so动态链接是没有问题的,但是使用libboostpython.a静态链接,会产生如下错误(图片是线程,道理是一样的): 原因在于boost的编译工具bjam在编译b ...

  8. QT,SSH开发——QSSH库编译成功率最高的方法

    1.前言 QT做SSH开发,QSSH一定是一个绕不过去的方法.但是在库的编译上,却难倒了很多人.无法正确的把库文件编译出来. 我自己在开发基于QSSH的SSH时候也是遇到了很多的问题,踩了很多坑.所以 ...

  9. [iOS] 引用外部静态库时,(类别)目录方法无法加载问题

    [iOS] 引用外部静态库时,目录方法无法加载问题 这个 bug 在 xcode 4.3 以下会出现,4.3 以后已经修正了. 解决方法为:找到 target 的图标,更改其 Other Linker ...

最新文章

  1. [ASP.NET MVC 小牛之路]10 - Controller 和 Action (2)
  2. 基于问题导向与成果产出的教学模式:《大数据与城市规划》特色课程
  3. “ld: symbol(s) not found for architecture i386“错误解决方法
  4. linux 环境搭建
  5. 2021宿州市地区高考成绩排名查询,2021年宿州市所有的高中排名,宿州市高中高考成绩排名出炉...
  6. elasticsearch-6.0.1安装
  7. mdb新版本打不开_救命!!! 我打不开她给我的Pr工程……
  8. ArrayList常用函数
  9. 火星开发的价值_发现“火星”岩石密度比预想更低,火星探测开发的“九大价值”...
  10. 修改Idea的jdk版本
  11. 电脑版微信每天自动发送
  12. 2022年秋招提前批总结(海尔智家)
  13. 【第二十四期】golang 一年经验开发 富途
  14. 软件测试实验1 — Junit 安装与 triangle problem 的测试
  15. kubenetes入门学习-十-service
  16. Android应用该用H5开发还是原生开发?
  17. python 数据分析课程改革网_计算思维培养视域下《Python程序设计》课程的教学改革实践-最新教育资料...
  18. 关于 [ 新版 ] dubbo-admin登录失败这件事
  19. 使用office365邮箱的时候 O365: 451 5.7.3 STARTTLS is required to send mail
  20. 新媒体运营教程:用户运营5大步骤,用户运营具体步骤和需要注意

热门文章

  1. git每次操作提示输入密码问题解决
  2. PB程序“无法启动此程序,因为计算机中丢失PBvm90.dll。尝试重新安装该程序以解决此问题”的解决方法
  3. 主机ping不通ubuntu虚拟机的解决方法
  4. 如何禁用<textarea>的调整大小抓取器? [重复]
  5. 如何在Ruby中获得随机数
  6. LDAP配置+主从+双主
  7. 【WebAPI No.5】Core WebAPI中的自定义格式化
  8. 产品经理原型图神器有望超越Axure 的软件sketch 48.2 for Mac 中文破解版下载
  9. RPM包安装LAMP及httpd虚拟机SSL实现
  10. Java中的String字符串