php的引用类型底层解析
时间:2022-07-24
本文章向大家介绍php的引用类型底层解析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
我们来先看一段代码
<?php
$a = "string";
$b = &$a;
echo $a;
echo $b;
$b = "hello";
echo $b;
echo $a;
unset($b);
echo $b;
echo $a;
?>
输出结果为
string
string
hello
hello
(空)
hello
为什么会输出这样的结果呢?我们来分析一下 首先我们看一下引用类型的结构
struct _zend_reference {
zend_refcounted_h gc;
zval val;
};
我们可以看到,引用类型是一个变量zval和一个zend_refcounted_h组成 先看第一段的 a = "string";
(gdb) p *z
$1 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
w2 = 32767}}, u1 = {v = {type = 10 'n', type_flags = 4 ' 04', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 1034}, u2 = {next = 0, cache_slot = 0, lineno = 0,
num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
//我们可以看到$a的u1的type为10,所以说明$a已经是引用类型了,对应的内存地址为0x7ffff5a020a8
(gdb) p *$1.value.ref
$2 = {gc = {refcount = 2, u = {v = {type = 10 'n', flags = 0 ' 00', gc_info = 0}, type_info = 10}}, val = {value = {lval = 17733632, dval = 8.7615783471909966e-317,
counted = 0x10e9800, str = 0x10e9800, arr = 0x10e9800, obj = 0x10e9800, res = 0x10e9800, ref = 0x10e9800, ast = 0x10e9800, zv = 0x10e9800, ptr = 0x10e9800, ce = 0x10e9800,
func = 0x10e9800, ww = {w1 = 17733632, w2 = 0}}, u1 = {v = {type = 6 ' 06', type_flags = 0 ' 00', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 6}, u2 = {
next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776, fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
//我们可以看到在$a的引用内部 是由gc和val组成,而且val就是一个zval,对应的type是6,字符串类型
(gdb) p *$1.value.ref.val.value.str
$3 = {gc = {refcount = 1, u = {v = {type = 6 ' 06', flags = 7 'a', gc_info = 0}, type_info = 1798}}, h = 9223378990886268924, len = 6, val = "s"}
(gdb) p *$1.value.ref.val.value.str.val@6
$4 = "string"
//对应的打印出ref中的str类型的字符串
(gdb) p z
$5 = (zval *) 0x7ffff5a14090
(gdb) p *z
$6 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
w2 = 32767}}, u1 = {v = {type = 10 'n', type_flags = 4 ' 04', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 1034}, u2 = {next = 0, cache_slot = 0, lineno = 0,
num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
//我们可以看到$b的u1的type为10,所以说明$b已经是引用类型了,对应的内存地址为0x7ffff5a020a8 和$a共用一个地址
(gdb) p $6.value.ref
$7 = (zend_reference *) 0x7ffff5a020a8
(gdb) p *$6.value.ref
$8 = {gc = {refcount = 2, u = {v = {type = 10 'n', flags = 0 ' 00', gc_info = 0}, type_info = 10}}, val = {value = {lval = 17733632, dval = 8.7615783471909966e-317,
counted = 0x10e9800, str = 0x10e9800, arr = 0x10e9800, obj = 0x10e9800, res = 0x10e9800, ref = 0x10e9800, ast = 0x10e9800, zv = 0x10e9800, ptr = 0x10e9800, ce = 0x10e9800,
func = 0x10e9800, ww = {w1 = 17733632, w2 = 0}}, u1 = {v = {type = 6 ' 06', type_flags = 0 ' 00', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 6}, u2 = {
next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776, fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
//$b中的ref也是由gc和zval组成,而且对应的zval中的u1的type为6,是字符串类型
(gdb) p *$6.value.ref.val.value.str
$9 = {gc = {refcount = 1, u = {v = {type = 6 ' 06', flags = 7 'a', gc_info = 0}, type_info = 1798}}, h = 9223378990886268924, len = 6, val = "s"}
(gdb) p *$6.value.ref.val.value.str.val@6
$10 = "string"
//打印出字符串
接下来我们看看 $b = "hello"; echo $b; echo $a;
(gdb) p *z
$11 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
w2 = 32767}}, u1 = {v = {type = 10 'n', type_flags = 4 ' 04', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 1034}, u2 = {next = 0, cache_slot = 0, lineno = 0,
num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
//我们可以看到$b的u1的type为10,所以说明$b已经是引用类型了,对应的内存地址为0x7ffff5a020a8
(gdb) p *$11.value.ref
$12 = {gc = {refcount = 2, u = {v = {type = 10 'n', flags = 0 ' 00', gc_info = 0}, type_info = 10}}, val = {value = {lval = 140737314679872, dval = 6.9533472271273708e-310,
counted = 0x7ffff5a5ec40, str = 0x7ffff5a5ec40, arr = 0x7ffff5a5ec40, obj = 0x7ffff5a5ec40, res = 0x7ffff5a5ec40, ref = 0x7ffff5a5ec40, ast = 0x7ffff5a5ec40,
zv = 0x7ffff5a5ec40, ptr = 0x7ffff5a5ec40, ce = 0x7ffff5a5ec40, func = 0x7ffff5a5ec40, ww = {w1 = 4121291840, w2 = 32767}}, u1 = {v = {type = 6 ' 06',
type_flags = 0 ' 00', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 6}, u2 = {next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776,
fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
//$b中的ref是由gc和zval组成,而且对应的zval中的u1的type为6,是字符串类型
(gdb) p *$11.value.ref.val.value.str
$13 = {gc = {refcount = 0, u = {v = {type = 6 ' 06', flags = 2 ' 02', gc_info = 0}, type_info = 518}}, h = 9223372247569412249, len = 5, val = "h"}
(gdb) p *$11.value.ref.val.value.str.val@5
$14 = "hello"
//打印出对应的字符串
(gdb) p *z
$15 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
w2 = 32767}}, u1 = {v = {type = 10 'n', type_flags = 4 ' 04', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 1034}, u2 = {next = 0, cache_slot = 0, lineno = 0,
num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
//我们可以看到$a的u1的type为10,所以说明$a已经是引用类型了,对应的内存地址为0x7ffff5a020a8 和b一样
(gdb) p *$15.value.ref
$16 = {gc = {refcount = 2, u = {v = {type = 10 'n', flags = 0 ' 00', gc_info = 0}, type_info = 10}}, val = {value = {lval = 140737314679872, dval = 6.9533472271273708e-310,
counted = 0x7ffff5a5ec40, str = 0x7ffff5a5ec40, arr = 0x7ffff5a5ec40, obj = 0x7ffff5a5ec40, res = 0x7ffff5a5ec40, ref = 0x7ffff5a5ec40, ast = 0x7ffff5a5ec40,
zv = 0x7ffff5a5ec40, ptr = 0x7ffff5a5ec40, ce = 0x7ffff5a5ec40, func = 0x7ffff5a5ec40, ww = {w1 = 4121291840, w2 = 32767}}, u1 = {v = {type = 6 ' 06',
type_flags = 0 ' 00', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 6}, u2 = {next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776,
fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
//$a中的ref是由gc和zval组成,而且对应的zval中的u1的type为6,是字符串类型
(gdb) p *$15.value.ref.val.value.str
$17 = {gc = {refcount = 0, u = {v = {type = 6 ' 06', flags = 2 ' 02', gc_info = 0}, type_info = 518}}, h = 9223372247569412249, len = 5, val = "h"}
(gdb) p *$15.value.ref.val.value.str.val@5
$18 = "hello"
打印字符串
接下来我们再来看看 unset(
b; echo $a;
(gdb) p *z
$1 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
w2 = 32767}}, u1 = {v = {type = 0 ' 00', type_flags = 0 ' 00', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 0}, u2 = {next = 0, cache_slot = 0, lineno = 0,
num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
//大家其实可以看到 在unset($b)的操作过程中,仅仅是把b中的u1的type改为了0,为null类型,其余的地址等信息都未改变,所以对应的$a是不会有任何改变的
所以后面在打印$a的过程中,一切都是正常的,以下为$a的打印过程
(gdb) p *z
$2 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
w2 = 32767}}, u1 = {v = {type = 10 'n', type_flags = 4 ' 04', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 1034}, u2 = {next = 0, cache_slot = 0, lineno = 0,
num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
(gdb) p *$2.value.ref
$3 = {gc = {refcount = 1, u = {v = {type = 10 'n', flags = 0 ' 00', gc_info = 0}, type_info = 10}}, val = {value = {lval = 140737314679872, dval = 6.9533472271273708e-310,
counted = 0x7ffff5a5ec40, str = 0x7ffff5a5ec40, arr = 0x7ffff5a5ec40, obj = 0x7ffff5a5ec40, res = 0x7ffff5a5ec40, ref = 0x7ffff5a5ec40, ast = 0x7ffff5a5ec40,
zv = 0x7ffff5a5ec40, ptr = 0x7ffff5a5ec40, ce = 0x7ffff5a5ec40, func = 0x7ffff5a5ec40, ww = {w1 = 4121291840, w2 = 32767}}, u1 = {v = {type = 6 ' 06',
type_flags = 0 ' 00', const_flags = 0 ' 00', reserved = 0 ' 00'}, type_info = 6}, u2 = {next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776,
fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
(gdb) p *$2.value.ref.val.value.str
$4 = {gc = {refcount = 0, u = {v = {type = 6 ' 06', flags = 2 ' 02', gc_info = 0}, type_info = 518}}, h = 9223372247569412249, len = 5, val = "h"}
(gdb) p *$2.value.ref.val.value.str.val@5
$5 = "hello"
- PDF.NET数据开发框架实体类操作实例
- 利用Burp Suite对OWASP Juice Shop进行渗透测试
- Java同步问题面试知识学习
- Android UI控件系列:LinearLayout(线性布局)
- 使用操作符重载,生成ORM实体类的SQL条件语句
- Dance In Heap(四):一些堆利用的方法(下)
- Mac系统的Proton恶意软件:卷!土!重!来!
- Erlang语言学习入门
- Android使用Ant进行apk多渠道打包
- Dance In Heap(三):一些堆利用的方法(中)
- Android性能优化篇:使用软引用和弱引用
- 使用泛型委托,构筑最快的通用属性访问器
- 记录容易忘记的方法
- 无需重新编译代码,在线修改表单
- php概述
- php教程
- php环境搭建
- PHP书写格式
- php变量
- php常量
- PHP注释
- php数组
- php字符串 string
- PHP整型 integer
- PHP浮点型 float
- php布尔型
- php数据类型之数组
- php数据类型之对象
- php数据类型之null
- php数据类型之间的转换
- php运算符
- php表达式
- PHP循环控制
- PHP流程控制
- php函数
- php全局变量
- PHP魔术变量
- php命名空间
- php 日期
- PHP包含文件
- php文件
- PHP 文件上传
- php Cookies
- php Sessions
- php email
- php安全email
- php错误处理
- PHP异常处理
- php过滤器
- PHP 高级过滤器
- php json
- php 表单
- PHP MySQL 简介
- PHP 连接 MySQL
- php创建数据库
- php 创建表
- php mysq 插入数据
- PHP MySQL 插入多条数据
- PHP MySQL 预处理语句
- php mysql 读取数据
- php mysql where
- PHP MySQL Order By
- PHP MySQL Update
- PHP MySQL Delete
- php ODBC