资讯专栏INFORMATION COLUMN

如何把扩展从PHP5升级到PHP7

AWang / 2771人阅读

摘要:所以,分配内存的宏都被删掉了。为的轮询设计了一组宏,使用起来非常方便。

我在公司的生产环境已经升级了PHP7,大部分活跃的扩展都可以兼容PHP7或者有了PHP7的分支,但是处理protocolbuffers数据的扩展一直没有人来升级,我只能摸着石头过河,自己做一把升级了。目前已经编译通过,处在处理单测失败的case阶段,欢迎大家一起讨论。对PHP扩展升级可以先看一下官方的升级说明,https://wiki.php.net/phpng-upgrading,基本可以应对大部分场景,剩下的就需要自己读源代码了。

zval

zval结构体是Zend内核的非常核心的结构,在PHP5和PHP7之间的差别非常大,我给出2处文章供大家学习,基本上可以代表这块知识点最权威的介绍了。

深入理解PHP7之zval(鸟哥)
https://github.com/laruence/php7-internal/blob/master/zval.md

变量在 PHP7 内部的实现(Nikita Popov)中文版
http://0x1.im/blog/php/Internal-value-representation-in-PHP-7-part-1.html
http://0x1.im/blog/php/Internal-value-representation-in-PHP-7-part-2.html

PHP7不再使用zval的二级指针,大多数场景下出现的zval*变量都改成zval,相应的使用在这些变量上的宏Z_PP也需要改成Z_P。
在大部分场景下,PHP7是在栈上直接使用zval,不需要去堆上分配内存。这时,zval 就需要改成zval,宏也需要从Z__P改成Z_,创建宏从ZVAL_(var)转换成ZVAL_*(&var)。所以,分配zval内存的宏

ALLOC_ZVAL、ALLOC_INIT_ZVAL、MAKE_STD_ZVAL都被删掉了。
-  zval *zv;
-  MAKE_STD_ZVAL(zv);
-  array_init(zv);
+  zval zv;
+  array_init(&zv);

PHP7中zval的long和double类型是不需要引用计数的,所以相关的宏要做调整。

- Z_ADDREF_P(zv)
+ Z_TRY_ADDREF_P(zv);

PHP7中zval的类型,删除了IS_BOOL,增加了IS_TRUE和IS_FALSE。

- if (Z_TYPE_P(zv) == IS_BOOL) {
- }
+ if (Z_TYPE_P(zv) == IS_TRUE) {
+ } else if (Z_TYPE_P(zv) == IS_FALSE) {
+ }
zend_string

PHP7中增加了一个新的内置字符串类型zend_string,下面是Zend内核中的结构体定义。

struct _zend_string {
    zend_refcounted_h gc;     /* 垃圾回收结构体 */
    zend_ulong        h;      /* 字符串哈希值 */
    size_t            len;    /* 字符串长度 */
    char              val[1]; /* 字符串内容 */
};

gc是PHP7中的所有非标量结构都包含的垃圾回收结构体变量;h是字符串哈希值,作为HashTable的key时不需要每次都重新计算哈希值,提高了效率;len是字符串长度,同理每次使用到字符串的长度时不需要再计算,提高了效率;val[1]是C语言的黑科技,此处按照char *理解即可。这里有三个宏帮助我们方便的使用zend_string的变量。

#define ZSTR_VAL(zstr)  (zstr)->val
#define ZSTR_LEN(zstr)  (zstr)->len
#define ZSTR_H(zstr)    (zstr)->h

创建和销毁zend_string使用以下方法。

zend_string *zend_string_init(const char *str, size_t len, int persistent)
void zend_string_release(zend_string *s)

zend_string用来替代PHP5中使用char *和int的场景,尤其是很多API的参数和返回值都做了调整。

- int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
+ zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key)

- void zend_mangle_property_name(char **dest, int *dest_length, const char *src1, int src1_length, const char *src2, int src2_length, int internal);
+ zend_string *zend_mangle_property_name(const char *src1, size_t src1_length, const char *src2, size_t src2_length, int internal)
HashTable API

在PHP7中使用HashTable的API方法时,有了非常明显的变化。
查询方法,PHP5使用引用传参的方式,同时返回SUCCESS/FAILURE;PHP7直接返回结果,查询无结果时返回NULL。

- int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
+ zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key)

HashTable的API方法中的key,PHP5中使用char 和int代表的字符串;PHP7中使用zend_string代表的字符串,同时提供了对char 和int支持的一组方法,但是需要注意的是这里的字符串长度是不包括结尾的"0"的,在升级扩展时难免会碰到很多地方需要加减一。

- int zend_hash_exists(const HashTable *ht, const char *arKey, uint nKeyLength)
+ zend_bool zend_hash_exists(const HashTable *ht, zend_string *key)
+ zend_bool zend_hash_str_exists(const HashTable *ht, const char *str, size_t len)

PHP7为HashTable的value为指针时设计了一组API,在常规的API方法后添加后缀_ptr即可。

void *zend_hash_find_ptr(const HashTable *ht, zend_string *key)
void *zend_hash_update_ptr(HashTable *ht, zend_string *key, void *pData)

PHP7为HashTable的轮询设计了一组宏,使用起来非常方便。

ZEND_HASH_FOREACH_VAL(ht, val)
ZEND_HASH_FOREACH_KEY(ht, h, key)
ZEND_HASH_FOREACH_PTR(ht, ptr)
ZEND_HASH_FOREACH_NUM_KEY(ht, h)
ZEND_HASH_FOREACH_STR_KEY(ht, key)
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val)
ZEND_HASH_FOREACH_KEY_VAL(ht, h, key, val)
自定义对象

这里有点复杂,我直接附上我的代码,结合代码来做详细说明。

typedef struct{
    int max;
    int offset;
    zend_object zo;
} php_protocolbuffers_unknown_field_set;

static zend_object_handlers php_protocolbuffers_unknown_field_set_object_handlers;

static void php_protocolbuffers_unknown_field_set_free_storage(php_protocolbuffers_unknown_field_set *object TSRMLS_DC)
{
    php_protocolbuffers_unknown_field_set *unknown_field_set;
    unknown_field_set = (php_protocolbuffers_unknown_field_set*)((char *) object - XtOffsetOf(php_protocolbuffers_unknown_field_set, zo));

    zend_object_std_dtor(&unknown_field_set->zo TSRMLS_CC);
}
zend_object *php_protocolbuffers_unknown_field_set_new(zend_class_entry *ce TSRMLS_DC)
{
    php_protocolbuffers_unknown_field_set *intern;
    intern = ecalloc(1, sizeof(php_protocolbuffers_unknown_field_set) + zend_object_properties_size(ce));
    zend_object_std_init(&intern->zo, ce);
    object_properties_init(&intern->zo, ce);
    intern->zo.handlers = &php_protocolbuffers_unknown_field_set_object_handlers;
    
    intern->max    = 0; 
    intern->offset = 0;
    
    return &intern->zo;
}
void php_protocolbuffers_unknown_field_set_class(TSRMLS_D)
{
// 此处有省略
    php_protocol_buffers_unknown_field_set_class_entry->create_object = php_protocolbuffers_unknown_field_set_new;
    memcpy(&php_protocolbuffers_unknown_field_set_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
    php_protocolbuffers_unknown_field_set_object_handlers.offset = XtOffsetOf(php_protocolbuffers_unknown_field_set, zo);
    php_protocolbuffers_unknown_field_set_object_handlers.free_obj = php_protocolbuffers_unknown_field_set_free_storage;
}

我们想自定义一个php_protocolbuffers_unknown_field_set的对象,在它的结构体里面除了zend_object,还有自定义的max和offset,务必把zend_object放在最后。
实际生成对象的地方基本就是标准写法,先分配内存,包括php_protocolbuffers_unknown_field_set结构体的内存和对象属性的内存;然后对zend_object的handlers赋值;最后再对自己自定义的变量初始化。
实际生成对象handler的地方也是标准写法,先分配内存,offset是必须设置的,可选的设置项有free_obj,dtor_obj,clone_obj。
想取到zend_object,需要(STRUCT_NAME )((char )OBJECT - XtOffsetOf(STRUCT_NAME, zo))

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21598.html

相关文章

  • 项目切换PHP7.1.15

    摘要:也可以接入项目打包测试流程做代码检测。扩展替换以后废弃了和扩展,项目中使用的使用的类使用的是已经废弃的扩展使用扩展做兼容替换。测试方案和大部分公司差不多,项目组划分了线下开发环境预发布环境和生产环境三个环境。 项目由PHP5.5切换至PHP7.1.15 背景 从2015年鸟哥的技术分享,我们知道PHP7是对底层实现得一次完全重构,函数调用机制和内存管理等很多方便做了优化,使PHP性能有...

    tuniutech 评论0 收藏0
  • php7默认不安装bcmath的扩展,调用bcmath函数会报错

    摘要:可以理解为的吧升级到之后,测试环境测试的时候发现直接调用库函数的地方报错了。问题解决查看了代码和资料才知道原来上是默认安装的,而上的默认安装是不安装的扩展的,是会默认支持的,就是可以直接调用。参考资料官网说明其实是第一条的用户评论里面 问题描述:     原本公司使用的是php5.6,然后因为一些原因要升级到php7,然后代码中有一些地方使用到了bcmath(就是用来进行任意精度的计算...

    Ververica 评论0 收藏0
  • Badoo 告诉你切换 PHP7 节省了 100 万美元

    摘要:我们为了处理这些挑战,提出了一个新的引用测试框架当然,也是开源的,并且在整个过程中节省了上百万美元。另一方面,被证实有一些严重的缺点部署困难而且慢。在缓存刷新期间,当可用于别的进程的已缓存的文件字节码在此时损坏,就会导致崩溃。 How Badoo saved one million dollars switching to PHP7 我们成功的把我们的应用迁移到了php7上面(数百台机...

    biaoxiaoduan 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<