当前位置:

首页 > 编程开发 > 把php5的程序 升级为php7,将PHP C++扩展从php5升级到php7

把php5的程序 升级为php7,将PHP C++扩展从php5升级到php7

本文目录

    通过修改核心头文件实现版本适配,根据PHP主版本号自动切换。分别准备php5obj.h和php7obj.h,将旧版宏映射到PHP7API,无需重写大量代码即可将PHP5的C++扩展高效升级至PHP7,兼容性强,支持自动条件编译,极大降低迁移成本。

    接手一个基于 PHP5 写的 C++ 扩展,需要快速升级到 PHP7 环境下使用,但之前对 PHP5 和 PHP7 的源码并没有深入理解,怎么办?很多人第一反应是要重写大量代码,其实不然——只需要修改一个核心头文件,扩展就能编译通过,运行正常。下面就把这个“偷懒”的方法拆开来看。

    phpobj.h 文件:版本选择

    整个思路的核心是做一个版本适配层。先看这个 phpobj.h,它根据 PHP 的主版本号来决定加载哪个具体实现:

    #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
    

    这样,扩展代码里只需要统一引用 phpobj.h,底层自动切换。剩下的工作就是分别准备两个版本的头文件,把旧版 PHP5 的宏调用映射到 PHP7 的 API 上。

    php5obj.h(旧版)

    保留原有的宏定义,确保在 PHP5 环境下能正常编译。注意,这里用了一堆 #define 来包装原来的函数/宏,目的是让上层代码不直接依赖 PHP 版本细节:

    #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
    

    php7obj.h(新版)

    PHP7 内部对象模型变化很大,原来的 zval 分配、zend_object_store 等机制都改了。新版头文件里主要做了三件事:

    • 把老的 RETURN_STRINGL 等宏映射到新 API(注意参数个数变化);
    • 用 XtOffsetOf 获取对象偏移量,实现从 zend_object 到自定义结构的转换;
    • 修改对象创建/销毁函数,改用 zend_object_std_init 和 object_properties_init。
    #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_
    

    整套方案下来,改动量其实很小——只需要在原有扩展代码中把 #include "php5obj.h" 换成 #include "phpobj.h",然后确保 php5obj.h 和 php7obj.h 放在正确路径下。编译时根据 PHP 版本自动选择对应实现,整个过程几乎不需要理解 Zend 引擎的内部差异。当然,前提是原扩展没有使用 PHP5 独有的 API(比如 TSRMLS_CC 宏在 PHP7 中被移除,但这里通过宏定义巧妙避开了)。如果原扩展大量依赖 zend_list_delete、zend_objects_store 等,还需要额外处理,但这个例子证明,很多时候改一个头文件就够了。

    本文内容来源于网友投稿,如有侵权请联系删除。
    作者最新文章
    编程开发
    相关文章 更多
    PHP递归性能优化技巧与迭代替代方案
    PHP递归性能优化技巧与迭代替代方案

    解析PHP递归函数在树形数据处理中的性能瓶颈,提供预加载数据消除I/O、使用显式栈替代深层递归的实战方案,帮助开发者在代码可读性与执行效率间做出合理取舍。

    Java测试中怎么使用Mockito模拟依赖对象
    Java测试中怎么使用Mockito模拟依赖对象

    详细讲解在Java单元测试中如何使用Mockito模拟依赖对象,包括引入依赖、创建Mock、打桩返回值、行为验证以及Mock与Spy的核心差异和常见陷阱排查。

    链表删除节点的时间复杂度是多少及其详细分析
    链表删除节点的时间复杂度是多少及其详细分析

    详细分析链表删除节点的时间复杂度,深入探讨单链表与双向链表在不同已知前提下的查找与删除开销,并结合完整代码与清晰图解进行对比总结。

    codex如何配置模型参数及文件设置教程
    codex如何配置模型参数及文件设置教程

    想知道如何让AI写出的代码更贴合你的习惯?本文手把手教你在VS Code中调整Codex相关模型参数,通过修改配置文件优化温度值和令牌限制,解决代码建议不准确或响应慢的问题。

    Claude Code AI编程工具实力揭秘与编程助手实测
    Claude Code AI编程工具实力揭秘与编程助手实测

    通过实测展示Claude Code在终端中如何理解自然语言指令、自动修改代码文件并处理复杂编程任务,帮助开发者评估其实际辅助能力。

    winforms教程自学入门与基础开发步骤详解
    winforms教程自学入门与基础开发步骤详解

    本教程详细讲解如何使用Visual Studio创建WinForms项目,通过添加按钮和标签控件并编写点击事件代码,实现一个基础的计数器功能,适合C#初学者快速上手Windows窗体应用开发。

    Cursor自动补全设置教程教你快速开启代码补全功能
    Cursor自动补全设置教程教你快速开启代码补全功能

    详解Cursor编辑器中自动补全功能的开启与优化设置,涵盖Tab触发机制、上下文窗口调整及模型切换,帮助开发者解决补全延迟、干扰大等问题,提升编码流畅度。

    pandas的数据格式怎么转换和设置方法教程
    pandas的数据格式怎么转换和设置方法教程

    详解Pandas中数据格式转换的核心方法,包括astype强制转换、to_numeric容错处理及日期解析技巧,解决常见类型错误并提升数据处理效率。

    VS Code中文设置方法 简体语言包安装与切换教程
    VS Code中文设置方法 简体语言包安装与切换教程

    详细介绍在Visual Studio Code中安装Chinese (Simplified)语言包的方法,包括通过扩展市场搜索、安装及自动重启切换至简体中文界面的完整步骤,帮助开发者快速将编辑器本地化。

    cursor安装过程无法更改安装位置的解决方法
    cursor安装过程无法更改安装位置的解决方法

    针对Cursor安装包默认锁定C盘且无路径选择界面的问题,提供通过手动移动文件并创建目录联结(Symbolic Link)的解决方案,实现将软件安装在其他磁盘分区。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件
    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    Mac软件 更多
    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

    灵活计算器
    灵活计算器
    macOS/iOS/Android

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    WINDOWS 更多
    3dmax(3ds max)
    3dmax(3ds max)
    Windows

    Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

    photoshop
    photoshop
    Windows、macOS 、 iPad

    Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

    Blender
    Blender
    Windows、macOS 和 Linux

    Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。