PHP 扩展开发(VS2012)小记

admin 2022年5月17日11:27:33评论11 views字数 4748阅读15分49秒阅读模式

生成扩展模板

人性化的PHP项目组给出了一个模板,也就是ext目录下的skeleton

其实我们可以直接COPY过来用的,网上很多方法都是去执行sh脚本ext_skel或者php脚本ext_skel_win32.php

ext_skel的作用极速生成一个skeleton插件而已,而源码中一般的已经生成好了的。

所以我们只需要改写几句ext_skel_win32.php的脚本就能直接生成而不用安装CygWin

ext_skel_win32_virink.php

<?php
$extname = "YourExtName";
$skel = "skeleton";
$fp = fopen("$skel/skeleton.dsp", "rb");
if ($fp) {
    $dsp_file = fread($fp, filesize("$skel/skeleton.dsp"));
    fclose($fp);
    $dsp_file = str_replace("extname", $extname, $dsp_file);
    $dsp_file = str_replace("EXTNAME", strtoupper($extname), $dsp_file);
    $fp = fopen("$extname/$extname.dsp", "wb");
    if ($fp) {
        fwrite($fp, $dsp_file);
        fclose($fp);
    }
}
$fp = fopen("$extname/$extname.php", "rb");
if ($fp) {
    $php_file = fread($fp, filesize("$extname/$extname.php"));
    fclose($fp);

    $php_file = str_replace("dl('", "dl('php_", $php_file);
    $fp = fopen("$extname/$extname.php", "wb");
    if ($fp) {
        fwrite($fp, $php_file);
        fclose($fp);
    }
}
?>

模板说明

最核心的内容就是php_extname.hextname.c这两个文件,其他的都是辅助编译的。

php_extname.h

#ifndef PHP_EXTNAME_H
#define PHP_EXTNAME_H

上面两句没什么用

extern zend_module_entry extname_module_entry;
#define phpext_extname_ptr &extname_module_entry

这两句就是声明扩展模块的入口,给PHP调用的

#define PHP_EXTNAME_VERSION "0.1.0"

定义版本号

#ifdef PHP_WIN32
#   define PHP_EXTNAME_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#   define PHP_EXTNAME_API __attribute__ ((visibility("default")))
#else
#   define PHP_EXTNAME_API
#endif

判断操作系统以及编译器类型

#ifdef ZTS
#include "TSRM.h"
#endif

#ifdef ZTS
#define EXTNAME_G(v) TSRMG(extname_globals_id, zend_extname_globals *, v)
#else
#define EXTNAME_G(v) (extname_globals.v)
#endif

#endif  /* PHP_EXTNAME_H */

ZTS是是否启用线程安全模式,不定义就是Non Thread Safe

extname.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

没什么用,无视

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_extname.h"

头文件,你懂的~~

static int le_extname;

定义全局资源(全局变量),在线程安全模式(thread safety)下就不需要了

PHP_FUNCTION(confirm_extname_compiled)
{
    char *arg = NULL;
    int arg_len, len;
    char *strg;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
        return;
    }

    len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "extname", arg);
    RETURN_STRINGL(strg, len, 0);
}

定义一个php函数,这个函数是在php脚本里调用的。

PHP_FUNCTION是定义php函数的宏

// php.h
#define PHP_FUNCTION            ZEND_FUNCTION
// zend_API.h
#define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FN(name) zif_##name

函数名:confirm_extname_compiled,调用:<?php confirm_extname_compiled(); ?>

zend_parse_parameters,获取函数参数

ZEND_API int zend_parse_parameters(int num_args TSRMLS_DC, const char *type_spec, ...);

RETURN_STRINGL,返回处理的结果

// zend_API.h
#define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; }
#define RETVAL_STRINGL(s, l, duplicate)     ZVAL_STRINGL(return_value, s, l, duplicate)
#define ZVAL_STRINGL(z, s, l, duplicate) do {   \
    const char *__s=(s); int __l=l;         \
    zval *__z = (z);                        \
    Z_STRLEN_P(__z) = __l;                  \
    Z_STRVAL_P(__z) = (duplicate?estrndup(__s, __l):(char*)__s);\
    Z_TYPE_P(__z) = IS_STRING;              \
} while (0)

各种宏很多的~~想要知道的更多,自己去读源码咯

PHP_MINIT_FUNCTION(extname)
{
    /* If you have INI entries, uncomment these lines 
    REGISTER_INI_ENTRIES();
    */
    return SUCCESS;
}

Zend Engine加载了该模块时执行的函数,如果模块有ini配置,则需要使用REGISTER_INI_ENTRIES();注册

PHP_MSHUTDOWN_FUNCTION(extname)
{
    /* uncomment this line if you have INI entries
    UNREGISTER_INI_ENTRIES();
    */
    return SUCCESS;
}

ZE结束的时候执行此函数,最后关闭自己的核心子系统

PHP_RINIT_FUNCTION(extname)
{
    return SUCCESS;
}

PHP收到请求的时候都执行该函数

PHP_RSHUTDOWN_FUNCTION(extname)
{
    return SUCCESS;
}

PHP脚本执行完毕后执行该函数

PHP_MINFO_FUNCTION(extname)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "extname support", "enabled");
    php_info_print_table_end();

    /* Remove comments if you have entries in php.ini
    DISPLAY_INI_ENTRIES();
    */
}

phpinfo()中显示相关信息

const zend_function_entry extname_functions[] = {
    PHP_FE(confirm_extname_compiled,    NULL)
    PHP_FE_END
};

声明自定义的php函数,供Zend Engine获取以便调用。

zend_module_entry extname_module_entry = {
    STANDARD_MODULE_HEADER,
    "extname",
    extname_functions,
    PHP_MINIT(extname),
    PHP_MSHUTDOWN(extname),
    PHP_RINIT(extname),
    PHP_RSHUTDOWN(extname),
    PHP_MINFO(extname),
    PHP_EXTNAME_VERSION,
    STANDARD_MODULE_PROPERTIES
};

实现扩展模块的入口

#ifdef COMPILE_DL_EXTNAME
ZEND_GET_MODULE(extname)
#endif

编译为zend扩展

加载工程

0x01

打开VS2012,选择”文件”–“新建”–“从现有代码创建目录”

选择C++类型的项目,下一步

然后,选择你的php扩展文件夹路径,并且给项目命名,下一步

选择动态链接库项目。

0x02

直接用vs2012打开extname.dsp文件,升级工程

配置解决方案

右键项目属性,配置列表选择所有配置,然后选择C/C++,常规,附加包含目录,编辑

加入以下几个php源码目录(实际目录以开发者自己的目录为准):

E:\php-x.x.x-src
E:\php-x.x.x-src\main
E:\php-x.x.x-src\TSRM
E:\php-x.x.x-src\Zend

然后选择预处理器,预处理器定义,编辑,加入以下变量:

  • ZEND_DEBUG=0
  • PHP_EXTENSION
  • PHP_WIN32
  • ZEND_WIN32
  • HAVE_EXTNAME=1
  • COMPILE_DL_EXTNAME
  • ZTS(这一个变量加上是开启线程安全,不加是关闭线程安全)

(这里EXTNAME,要改成你的扩展名称,不改成你的扩展名,php会不识别)

config.w32.h

E:\php-x.x.x-src\win32\build\文件夹里找到”config.w32.h.in”,

将这个文件复制到E:\php-x.x.x-src\main\文件夹里,去掉后面的”.in”

然后修改该文件里面的PHP_COMPILER_ID

#define PHP_COMPILER_ID "VC11" //编译器版本号

项目属性,链接器,输入,附加依赖项,编辑,将php5.lib的路径放进去

php5.lib

这个文件在编译好了的php程序目录dev里面有

结束语

导致了基本上好了,编译吧~~最简单的Demo扩展

然后,你可以根据PHP的各种API去编写你心仪的扩展啦

FROM : virzz.com | Author:Virink

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月17日11:27:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PHP 扩展开发(VS2012)小记http://cn-sec.com/archives/1013015.html

发表评论

匿名网友 填写信息