商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 把php5的程序 升级为php7,将PHP C++扩展从php5升级到php7

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

  发布于2026-07-21 阅读(0)

扫一扫,手机访问

接手一个基于 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_initobject_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.hphp7obj.h 放在正确路径下。编译时根据 PHP 版本自动选择对应实现,整个过程几乎不需要理解 Zend 引擎的内部差异。当然,前提是原扩展没有使用 PHP5 独有的 API(比如 TSRMLS_CC 宏在 PHP7 中被移除,但这里通过宏定义巧妙避开了)。如果原扩展大量依赖 zend_list_deletezend_objects_store 等,还需要额外处理,但这个例子证明,很多时候改一个头文件就够了。

本文转载于:https://blog.csdn.net/weixin_39719018/article/details/115244794 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注