在Windows平台上开发和编译PHP扩展成DLL文件的详细指南
概述

我通常在Linux系统上开发php扩展,扩展是通过编译安装添加的。然而,在开发php-opencv扩展时,有扩展用户问我是否可以提供窗口扩展dll参考文件。所以最近一直在考虑支持window平台,总结一下自己在Windows下开发PHP扩展并编译成DLL的经验。
官方文档
事实上,PHP官方提供了扩展编译成window.dll的官方文档。当时没有仔细看官方文档,所以捣鼓了几天。
1.环境准备
这里我想详细说一下环境。官方文件中提到的环境要求。 Visual Studio环境可以直接用Visual Studio ide安装。我一直以为它指的是Visual Studio C++,所以运行phpsdk-vc15-x64时,会出现Could not deternine。 ‘vc15’目录错误,PHP编译成DLL的环境可以使用此IDE安装
1.1安装Visual Studio(IDE)
前往官网下载Visual Studio Install
1.2 添加编译环境
在Visual Studio Professional 2017下选择修改后,选择安装在相应的环境中
所需的环境已经准备好了。
2.编译前的准备,设置构建目录2.1 php-sdk-binary-tools
php-sdk-binary-tools是官方提供的工具。
1.下载php-sdk-binary-tools
git clone https://github.com/Microsoft/php-sdk-binary-tools
cd php-sdk-binary-tools
2. 调用启动脚本。比如这里是php7.2,对应的是64位版本的Visual Studio 2017,调用phpsdk-vc15-x64.bat。当你运行它时,你会发现cmd窗口的开头会变成一个$符号。
3. 运行 phpsdk_buildtree 批处理脚本,这将创建所需的目录结构:
phpsdk_buildtree phpdev
运行后会发现php-sdk-binary-tools中多了一个phpdev目录
phpsdk_buildtree脚本会根据当前使用的VC++版本创建路径并切换到新创建的目录
4. 将 PHP 源代码解压到:F:\www\php-sdk-binary-tools\phpdev\vc##\x##。在PHP源码解压的同一目录下,有一个deps目录。
5、进入解压后的php源码目录,运行phpsdk_deps -u或phpsdk_deps --update --branch master。 PHP会自动下载所需的依赖包(php7之前需要手动下载php-dsp文件)
3.编译php3.1并搭建配置好的环境
上面已经准备好了编译前的构建准备环境。在编译之前,调用 php-sdk-binary-tools 中的启动脚本自动设置所需构建配置的环境 phpsdk-vc15-x64.bat。
3.2 执行buildconf
切换到php源码根目录,执行buildconf命令。如果提示输入错误:没有文件扩展名“.js”的脚本引擎。
这里主要原因是系统安装IDE(Dreamwear、UltraEdit、EditPlus,我之前安装了phpstorm并修改过)后,修改了.js文件的默认打开方式。
当你想直接执行js脚本时就会出现这个错误。
按快捷键win+r,然后输入regedit,打开注册表编辑器,找到[HKEY_CLASSES_ROOT.js]项,双击默认值,将其更改为“JSFile”。如图所示:
然后就可以成功执行了
3.3 编译配置
执行以下命令编译php配置:
configure --disable-all --enable-cli
注:您可以自行适当调整。可以使用configure --help查看参数,如:
configure --disable-all --enable-cli --enable-debug --with-all-shared
执行成功后,会提示执行nmake。
执行此步骤将在主目录中生成config.w32.h文件。编译 PHP 扩展需要此文件,因此如果您正在编译 PHP 扩展,请执行下一步 nmake。如果您只是编译 PHP 扩展,则不需要执行`nmake。
3.4 执行nmake编译
然后执行nmake开始编译
等待几分钟后,您应该能够在 php-src 目录中找到一个多了一个编译位(x32 或 x64)的目录。目录下有一个Release_TS(或Release_NTS或Debug_TS)这样的目录。
如果编译正常,下面会出现一个php.exe文件,证明php文件编译成功。
运行编译好的php.exe -v 看看是否正常运行
4. 编译php扩展 4.1 创建php扩展
如果我们只编译一些已经打开的扩展源码,则不需要运行这一步。这里我们创建一个用于编译的演示扩展。
使用git bash进入源代码地址\ext目录并执行:
php.exe ext_skel_win32.php --extname==my_function
注意:这里之所以使用git bash工具,是因为git bash自带了模拟的Linux环境。 My_function 是我需要在此处创建的扩展。具体的可以根据自己的需要进行更改。
创建工具时,提示符“sh”不被识别为内部或外部命令,
git bash执行结果:
可以看到源码ext目录下多了一个my_function目录。这是我们刚刚使用 ext_skel_win32.php 命令创建的初始扩展代码。
4.2 修改源码
1.我们编辑my_function下的my_function.c文件。添加一个方法my_function_test方法,代码如下:
PHP_FUNCTION(my_function_test) {
php_printf("This is my function PHP extension! \n");
}
2. 然后将 PHP_FE(my_function_test, NULL) 添加到 const zend_function_entry my_function_functions[] 中,但放在 PHP_FE_END 之前:
4.3 编译扩展
切换回cmd窗口
1、进入php-sdk-binary-tools目录,使用phpsdk-vc15-x64.bat
2.切换到php源代码目录,运行buildconf
3、如果执行buildconf后执行configure命令,会提示类似如下错误:
打开提示文件,找到对应的行号,发现末尾多了多余的注释符号。这可能是工具问题,
解决方案:打开扩展源码的config.w32,找到注释 // 否则,使用ARG_ENABLE,将其删除,然后重新运行buildconf。注意:
运行configure --help,你会发现额外的--enable-my_function选项:
4. 运行configure --disable-all --enable-cli --enable-my_function=shared进行编译配置:
发现错误信息:
F:\www\php-sdk-binary-tools\phpdev\vc15\x64\php-7.2.12-src\configure.js(5490, 2) Microsoft JScript 运行时错误: 'PHP_EXTNAME_SHARED' 未定义
解决方案:重新打开扩展的config.w32文件,删除下面的图片,PHP_EXTNAME_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"
重启buildconf->运行编译配置命令。可以看到编译配置成功,提示启用扩展:我们的扩展已添加:
5. 运行nmake命令进行编译。等待几分钟到十分钟后,如果终端出现类似下面的信息:Compilation success,则表示编译成功。
而且我在源代码目录中发现了一个额外的x64目录。如果编译是32位的话就是x86。
里面有一个Release_TS(release表示non-debug,对应debug,TS表示线性安全,对应NTS),
里面的文件就是我们编译的php文件,我们发现php_my_function.dll文件就是我们编译的扩展文件。
6.配置php.ini引用编译后的扩展
进入编译目录,运行php.exe -v 测试编译是否正常。
当你运行php.exe --ini命令时,你会发现没有配置文件,当你运行php.exe -m时,你会发现PHP Modules中没有我们创建的my_function扩展模块。
将源码中的php.ini-development复制到编译目录下,改名为php.ini,然后修改原来的php.ini文件:
; On windows:
; extension_dir = "ext"
更改为:
; On windows:
extension_dir = "./"
和:
再次运行 php -m test
7.测试并添加测试文件test.php
my_function_test();
然后运行编译好的php目录\php.exe test.php
至此,window下编译php及php扩展已经成功。
清理干净
nmake clean 命令会清除编译后的文件,但不会删除目录和php.ini。
当修改只是修改扩展名时,不要运行nmake clean命令。只需要中央configure和nmake分别重新编译修改后的扩展,而不需要使用整个PHP扩展。这样可以大大提高开发扩展的效率。我不知道我之前已经运行过nmake。 clean命令,每次扩展修改一点代码,都要等10多分钟才能得到结果。
迭代参考