首页 游戏 软件 资讯 排行榜 专题
首页
编程语言
PHP C++扩展从PHP5迁移至PHP7的完整升级指南

PHP C++扩展从PHP5迁移至PHP7的完整升级指南

热心网友
31
转载
2026-05-07

在没有怎么看明白php5 php7源码的情况下,接手一份基于php5写c++扩展,如何接手快速升级到php7环境下也能使用呢

这听起来像是个棘手的任务:对PHP5和PHP7的内核源码没有深入研究,却要接手一个用C++编写的、为PHP5设计的扩展,并让它平滑过渡到PHP7环境。通常,这意味着一场浩大的代码重写工程。但实际情况可能没想象中那么复杂。

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

一个高效的策略是,建立抽象层。与其深入每个API的差异,不如先为那些变化最大的核心操作——尤其是对象处理——创建统一的接口。这样一来,核心业务逻辑的代码几乎无需改动。

事实上,仅仅通过修改所引用的一个关键头文件,就成功实现了目标:扩展被顺利编译,并且运行正常。这其中的关键,就在于下面这个精巧的版本适配设计。

phpobj.h 文件,定义版本选择

#ifndef _PHP_OBJ_H_2017
#define _PHP_OBJ_H_2017

#include 

#if PHP_MAJOR_VERSION < 7
#include "php5obj.h"
#else
#include "php7obj.h"
#endif

#endif //_PHP_OBJ_H_2017

这个头文件是整个方案的“调度中心”。它的逻辑清晰而直接:通过预处理器判断当前的PHP主版本号,从而自动包含对应版本的具体实现头文件。这确保了代码库只需维护一份,却能同时兼容两个大相径庭的PHP运行时。

php5obj.h 旧的

#define SW_RETURN_STRINGL RETURN_STRINGL
#define SW_RETVAL_STRINGL RETVAL_STRING
#define SW_RETVAL_STRING RETVAL_STRING
#define SW_RETURN_STRING RETURN_STRING
#define SW_ZVAL_STRINGL ZVAL_STRINGL
#define SW_ZVAL_STRING ZVAL_STRING
#define SW_MAKE_STD_ZVAL(p) MAKE_STD_ZVAL(p)
#define PZVAL_IS_REF(len) ZVAL_IS_REF(len)

// Register the class entry..
#define PHP5CPP_REGISTER_CLASS(name, obj_name) \
{ \
    zend_class_entry ce; \
    INIT_CLASS_ENTRY(ce, obj_name, php5cpp_ ## name ##_methods); \
    ce.create_object = php5cpp_object_new_ ## name; \
    php5cpp_ce_ ## name = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);\
    memcpy( &php5cpp_object_handlers_ ## name, \
        zend_get_std_object_handlers(), \
        sizeof(zend_object_handlers) ); \
    php5cpp_object_handlers_ ## name.clone_obj = NULL; \
    php5cpp_ce_ ## name->ce_flags |= ZEND_ACC_FINAL_CLASS; \
}

#define PHP5CPP_GET_THIS() \
    zval* object = getThis();

// Register resources. If we're using an object, put it into the object store.
#define PHP5CPP_REGISTER_RESOURCE(obj_type, return_value, res, le) \
{ \
    PHP5CPP_GET_THIS(); \
    int rsrc_id = ZEND_REGISTER_RESOURCE(object ? NULL : return_value, res, le); \
    if (object) { \
        php5cpp_obj *obj = (php5cpp_obj *)zend_object_store_get_object(object TSRMLS_CC); \
        obj->u.obj_type = res; \
        obj->rsrc_id = rsrc_id; \
        obj->type = is_ ## obj_type; \
    } \
}

// These are for parsing parameters and getting the actual object from the store.
#define PHP5CPP_SET_OBJ(type) \
{ \
    php5cpp_obj *obj = (php5cpp_obj *)zend_object_store_get_object( object TSRMLS_CC ); \
    type ## _instance = obj->u.type; \
}

// Deprecated
#define PHP5CPP_OBJ_PARAMS(type, params) \
{ \
    PHP5CPP_GET_THIS(); \
    if (object) { \
        if (params == FAILURE) { \
            RETURN_FALSE; \
        } \
        PHP5CPP_SET_OBJ(type) \
    } \
}

// Deprecated
#define PHP5CPP_OBJ_NO_PARAMS(type) \
{ \
    PHP5CPP_GET_THIS(); \
    if (object) { \
        if (ZEND_NUM_ARGS() != 0) { \
            php_error(E_WARNING, "didn't expect any arguments in %s()", get_active_function_name(TSRMLS_C)); \
        } \
        PHP5CPP_SET_OBJ(type) \
    } \
}

#define PHP5CPP_GET_OBJ(type) \
{ \
    PHP5CPP_GET_THIS(); \
    if (object) { \
        PHP5CPP_SET_OBJ(type) \
    } \
    if (type ## _instance == NULL) { \
        php_error(E_WARNING, "Get object fall in %s()", get_active_function_name(TSRMLS_C)); \
    } \
}

#define PHP5CPP_GET_OBJ_ZVAL(type, obj) \
{ \
    zval* object = obj; \
    if (object) { \
        PHP5CPP_SET_OBJ(type) \
    } \
    if (type ## _instance == NULL) { \
        php_error(E_WARNING, "Get object fall in %s()", get_active_function_name(TSRMLS_C)); \
    } \
}

//static void php5cpp_object_free_storage_ ## type(zend_object *object TSRMLS_DC)
#define PHP5CPP_OBJ_FREE_FUNCTION(type) \
static void php5cpp_object_free_storage_ ## type(void *object TSRMLS_DC) \
{ \
    php5cpp_obj *obj = (php5cpp_obj *) object; \
    zend_hash_destroy(obj->std.properties); \
    FREE_HASHTABLE(obj->std.properties); \
    if ( obj->u.type ) { \
        zend_list_delete(obj->rsrc_id); \
    } \
    efree(obj); \
}

#define PHP5CPP_OBJ_NEW_FUNCTION(type) \
static zend_object_value php5cpp_object_new_ ## type(zend_class_entry *class_type TSRMLS_DC) \
{ \
    zend_object_value retval; \
    zval *tmp; \
    php5cpp_obj *obj = (php5cpp_obj *)emalloc( sizeof(php5cpp_obj) ); \
    memset( obj, 0, sizeof(php5cpp_obj) ); \
    obj->std.ce = class_type; \
    ALLOC_HASHTABLE( obj->std.properties ); \
    zend_hash_init( obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0 ); \
    zend_hash_copy( obj->std.properties, \
        &class_type->properties_info, \
        (copy_ctor_func_t) zval_add_ref, \
        (void *) &tmp, \
        sizeof(zval *) ); \
    retval.handle = zend_objects_store_put( obj, \
        NULL, \
        (zend_objects_free_object_storage_t)php5cpp_object_free_storage_ ## type, \
        NULL TSRMLS_CC ); \
    retval.handlers = &php5cpp_object_handlers_ ## type; \
    return retval; \
}

#endif

这是PHP5时代的实现。可以看到,它严重依赖`zend_object_store_get_object`和`zend_objects_store_put`等API,并且内存管理和对象存储模型与PHP7有根本性不同。宏定义主要是对原有Zend API的简单包装,其内部逻辑是典型的PHP5扩展编写模式。

php7obj.h 新的

#define SW_RETURN_STRINGL(s, l, dup) RETURN_STRINGL(s, l)
#define SW_RETVAL_STRINGL(s, l, dup) do{ RETVAL_STRINGL(s, l); if (dup == 0) efree(s); }while(0)
#define SW_RETVAL_STRING(s, dup) do{ RETVAL_STRING(s); if (dup == 0) efree(s); }while(0)
#define SW_RETURN_STRING(val, duplicate) RETURN_STRING(val)
#define SW_ZVAL_STRINGL(z, s, l, dup) ZVAL_STRINGL(z, s, l)
#define SW_ZVAL_STRING(z,s,dup) ZVAL_STRING(z,s)
#define SW_MAKE_STD_ZVAL(p) zval _stack_zval_##p; p = &(_stack_zval_##p)
#define PZVAL_IS_REF(len) 0

static inline php5cpp_obj* php5obj_from_obj(zend_object *obj) {
    return (php5cpp_obj*)((char*)(obj) - XtOffsetOf(php5cpp_obj, std));
}
#define Z_PHP5OBJ_P(zv) php5obj_from_obj(Z_OBJ_P((zv)))

// Register the class entry..
#define PHP5CPP_REGISTER_CLASS(name, obj_name)\
{\
    zend_class_entry ce;\
    INIT_CLASS_ENTRY(ce, obj_name, php5cpp_##name##_methods);\
    ce.create_object = php5cpp_object_new_##name; \
    php5cpp_ce_##name = zend_register_internal_class_ex(&ce, NULL);\
    memcpy( &php5cpp_object_handlers_##name, zend_get_std_object_handlers(), sizeof(zend_object_handlers) );\
    php5cpp_object_handlers_##name.clone_obj = NULL;\
}

//#define ZEND_REGISTER_RESOURCE(return_value, result, le_result) ZVAL_RES(return_value, zend_register_resource(result, le_result))
#define PHP5CPP_GET_THIS() \
    zval* object = getThis();

// Register resources. If we're using an object, put it into the object store.
#define PHP5CPP_REGISTER_RESOURCE(obj_type, return_value, res, le) \
{\
    PHP5CPP_GET_THIS();\
    int rsrc_id =0;\
    /*zend_register_resource(res,le);*/\
    if (object) { \
        php5cpp_obj *obj = Z_PHP5OBJ_P(object);\
        obj->u.obj_type = res;\
        obj->rsrc_id = rsrc_id;\
        obj->type = is_##obj_type;\
    }\
}

// These are for parsing parameters and getting the actual object from the store.
#define PHP5CPP_SET_OBJ(type_name) \
{ \
    php5cpp_obj *obj = Z_PHP5OBJ_P(object) ; \
    type_name ## _instance = obj->u.type_name;\
}

// Deprecated
#define PHP5CPP_OBJ_PARAMS(type, params) \
{ \
    PHP5CPP_GET_THIS(); \
    if (object) { \
        if (params == FAILURE) { \
            RETURN_FALSE; \
        } \
        PHP5CPP_SET_OBJ(type) \
    } \
}

// Deprecated
#define PHP5CPP_OBJ_NO_PARAMS(type) \
{ \
    PHP5CPP_GET_THIS(); \
    if (object) { \
        if (ZEND_NUM_ARGS() != 0) { \
            php_error(E_WARNING, "didn't expect any arguments in %s()", get_active_function_name(TSRMLS_C)); \
        } \
        PHP5CPP_SET_OBJ(type) \
    } \
}

#define PHP5CPP_GET_OBJ(type) \
{\
    PHP5CPP_GET_THIS();\
    PHP5CPP_SET_OBJ(type);\
}

#define PHP5CPP_GET_OBJ_ZVAL(type, obj) \
{ \
    zval* object = obj; \
    if (object) { \
        PHP5CPP_SET_OBJ(type) \
    } \
    if (type ## _instance == NULL) { \
        php_error(E_WARNING, "Get object_zal fall in %s()", get_active_function_name(TSRMLS_C)); \
    } \
}

#define PHP5CPP_OBJ_FREE_FUNCTION(type) \
static void php5cpp_object_free_storage_ ## type(void *object TSRMLS_DC) \
{\
    php5cpp_obj *obj = Z_PHP5OBJ_P((zval *)object);\
    zend_object_std_dtor(&obj->std);\
}

#define PHP5CPP_OBJ_NEW_FUNCTION(type) \
static zend_object* php5cpp_object_new_ ## type(zend_class_entry *class_type TSRMLS_DC) \
{\
    int objsize = sizeof(php5cpp_obj);\
    php5cpp_obj *obj=(php5cpp_obj*)ecalloc(1,objsize); memset(obj,0,objsize);\
    php_error(E_WARNING, "create object %p in %s()", obj, get_active_function_name(TSRMLS_C));\
    zend_object_std_init(&obj->std, class_type TSRMLS_CC);\
    object_properties_init(&obj->std, class_type);\
    obj->std.handlers = &php5cpp_object_handlers_##type;\
    return &obj->std;\
}

#endif //_PHP7OBJ_H_

PHP7的实现则展现了完全不同的风貌。最核心的变化是对象模型的革新:PHP7中,`zend_object`被直接嵌入到自定义结构体中,通常放在首位。因此,获取自定义对象指针需要用到`Z_PHP5OBJ_P`这个关键宏,它通过`XtOffsetOf`计算偏移量来反向定位。

此外,字符串处理宏增加了对`dup`(复制)参数的处理逻辑,内存管理也转向使用`zend_object_std_init`和`object_properties_init`等新API。注册类时,不再需要传递`TSRMLS_CC`线程安全参数,这反映了PHP7在内部架构上的简化。

通过这样一组头文件的封装,C++扩展中的业务代码只需调用诸如`PHP5CPP_REGISTER_CLASS`、`PHP5CPP_GET_OBJ`这样的统一宏,而无需关心底层是PHP5还是PHP7。版本间的巨大差异被隔离在这薄薄的一层抽象之下,使得升级工作从“重写”变成了“适配”,效率自然大幅提升。

来源:https://blog.csdn.net/zhangtaolmq/article/details/54286685
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

PHP C++扩展从PHP5迁移至PHP7的完整升级指南
编程语言
PHP C++扩展从PHP5迁移至PHP7的完整升级指南

在没有怎么看明白php5 php7源码的情况下,接手一份基于php5写c++扩展,如何接手快速升级到php7环境下也能使用呢 这听起来像是个棘手的任务:对PHP5和PHP7的内核源码没有深入研究,却要接手一个用C++编写的、为PHP5设计的扩展,并让它平滑过渡到PHP7环境。通常,这意味着一场浩大的

热心网友
05.07
C++字符串分割到deque容器性能优化与实现方法对比
编程语言
C++字符串分割到deque容器性能优化与实现方法对比

std::deque因独特的内存分配模式,在字符串高频分割时性能不如std::vector,开销高出15–30%。高效分割的关键在于避免不必要的子串拷贝,可优先使用std::string_view并预分配容量。若需后续频繁进行双端队列操作或跨线程传递,std::deque仍有其适用场景,但需注意其内存不连续及调试模式下的性能陷阱。

热心网友
05.07
C++异步定时器实战教程使用stdjthread与stoptoken实现任务调度
编程语言
C++异步定时器实战教程使用stdjthread与stoptoken实现任务调度

std::jthread通过stop_token支持协作式中断,解决了传统线程定时器安全退出的难题。实现时需在循环中定期检查停止请求,并采用“先任务后睡眠”的顺序以避免响应延迟。同时需注意避免误用token副本、未检查请求即睡眠等常见陷阱。对于需延迟启动的场景,可先睡眠指定时间再进入周期循环。

热心网友
05.07
C++20线程自动汇合实现原理与jthread源码解析
编程语言
C++20线程自动汇合实现原理与jthread源码解析

C++20的jthread实现了线程自动汇合,析构时自动等待线程结束,无需手动join。但其不保证线程安全退出,若线程访问已销毁资源仍会崩溃。正确用法需配合stop_token实现协作式退出,线程函数需主动检查停止信号并响应。自动汇合仅是资源安全网,清晰主动的退出逻辑至关重要。

热心网友
05.07
C++20实战教程使用std::ranges::reverse_view反向遍历容器
编程语言
C++20实战教程使用std::ranges::reverse_view反向遍历容器

std::ranges::reverse_view是一个轻量级视图,它不修改容器数据,仅改变访问顺序。其要求底层范围支持双向迭代,否则编译失败。与std::reverse算法不同,视图构造开销低且不触碰数据内存,适用于只读倒序场景。使用时需注意原始容器的生命周期,避免迭代器失效。调试时可临时转换为vector或使用 base()方法查看底层数据。视图本身不延

热心网友
05.07

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

美国CLARITY法案最终版发布 全链网奖励机制细则正式出台
web3.0
美国CLARITY法案最终版发布 全链网奖励机制细则正式出台

《CLARITY法案》奖励机制文本公布,经协商达成折中:传统银行业获更多奖励限制,加密行业则确保美国用户仍可通过使用平台获得奖励,维护了用户参与和行业创新动力。此举有助于美国保持金融竞争力和国家安全利益。随着争议暂歇,法案将转向整体推进。

热心网友
05.07
Linux系统下Rust开发工具链安装与配置指南
编程语言
Linux系统下Rust开发工具链安装与配置指南

Linux 下的 Rust 工具链全景 想在 Linux 上愉快地写 Rust?一套趁手的工具链是关键。这份全景指南,帮你梳理从核心工具到开发辅助,再到环境配置的完整地图,让你快速上手,避开那些常见的“坑”。 一 核心工具链与用途 Rust 的工具链生态相当成熟,各司其职,共同构成了高效的工作流。

热心网友
05.07
Linux系统下Rust程序性能优化实用技巧指南
编程语言
Linux系统下Rust程序性能优化实用技巧指南

Rust 在 Linux 下的性能调优方法 想让你的 Rust 应用在 Linux 系统上飞起来?性能调优是个系统工程,从编译构建到系统层面,环环相扣。下面这份指南,将带你系统性地走完这个流程。 一 构建与编译优化 一切从构建开始。编译器的优化选项,是释放性能潜力的第一道闸门。 使用发布构建:这是基

热心网友
05.07
Linux下Rust网络编程入门与实践指南
编程语言
Linux下Rust网络编程入门与实践指南

在Linux中使用Rust进行网络编程 想在Linux环境下用Rust玩转网络编程?其实没那么复杂。跟着下面这几个清晰的步骤走,你就能快速搭建起一个可运行的基础框架。当然,这只是一个起点,Rust生态提供的工具远比这里展示的要强大。 1 安装Rust 万事开头先装环境。如果系统里还没有Rust,一

热心网友
05.07
Rust语言助力Linux系统跨平台开发与兼容性提升
编程语言
Rust语言助力Linux系统跨平台开发与兼容性提升

Rust为Linux系统带来跨平台能力的机制 想让同一套代码在Linux、Windows、macOS上都能顺畅运行?Rust给出的方案相当优雅。它通过一套统一的工具链、一个精心设计且可移植的标准库,再加上灵活的条件编译机制,让跨平台构建从理论变成了标准流程。更妙的是,基于LLVM的交叉编译体系和清晰

热心网友
05.07