PHP C++扩展从PHP5迁移至PHP7的完整升级指南
在没有怎么看明白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。版本间的巨大差异被隔离在这薄薄的一层抽象之下,使得升级工作从“重写”变成了“适配”,效率自然大幅提升。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
Ubuntu系统Python环境备份与恢复完整指南
在Ubuntu系统中备份Python环境,可以遵循以下步骤 无论是为了项目迁移、团队协作,还是防范系统意外,备份Python环境都是一项值得投入的基础工作。下面这几种方法,总有一款适合你的工作流。 方法一:使用pip freeze导出依赖包列表 打开终端:在Ubuntu中,可以通过快捷键Ctrl +
Ubuntu系统安装与配置Python环境详细教程
在 Ubuntu 上配置 Python 文档 想在 Ubuntu 系统里高效地查阅 Python 文档,摆脱对网络搜索的依赖?其实,无论是查看语言本身的说明,还是研究虚拟环境中第三方库的用法,都有现成且好用的本地化方案。下面就来梳理一下几种主流的方法。 一 安装本地 Python 文档 最直接的方式
Ubuntu系统Python环境监控配置详细教程
在 Ubuntu 上配置 Python 监控 当你的Python应用在Ubuntu服务器上跑起来之后,如何确保它健康、稳定地运行?一套有效的监控体系就是你的“眼睛”和“耳朵”。今天,我们就来聊聊如何从零开始,搭建一个既轻量又实用的Python监控方案。 一 监控目标与方案选型 首先,得明确我们要监控
Ubuntu系统Python库路径配置方法与步骤详解
在Ubuntu上配置Python库路径 在Ubuntu系统上工作,想让Python解释器顺利找到并导入第三方库,配置库路径是绕不开的一步。这事儿听起来有点技术性,但别担心,其实方法很清晰。下面咱们就来梳理几种最常用、也最有效的配置方式,你可以根据实际场景灵活选择。 方法一:使用环境变量 最直接的办法
Ubuntu系统下PHP-FPM数据备份操作指南
在Ubuntu系统中备份PHP-FPM数据:一份实用指南 对于运行在Ubuntu服务器上的PHP应用而言,PHP-FPM的配置和数据无疑是核心资产。一旦出现问题,一套清晰、可靠的备份方案就是救命的稻草。今天,我们就来系统地梳理一下,如何为你的PHP-FPM环境构建一个全面的数据备份策略。 1 备份
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

