PHP代码实现2 [从变量和数据的角度] 2
常量
常量的数据结构
1 |
typedef struct _zend_constant {
|
PHP对于常量的名称在定义时其实是没有所谓的限制
1 |
define('^_^', 'smile');
if (defined('^_^')) {
|
通过defined函数测试表示,^_^这个常量已经定义好,这样的常量无法直接调用, 只能使用constant()方法来获取到,否则在语法解析时会报错,因为它不是一个合法的标示符。
常量的等级
除了CONST_CS标记,常量的flags字段通常还可以用CONST_PERSISTENT和CONST_CT_SUBST。
CONST_PERSISTENT表示这个常量需要持久化。这里的持久化内存申请时的持久化是一个概念, 非持久常量会在请求结束时释放该常量,如果读者还不清楚PHP的生命周期,可以参考, PHP生命周期这一小节,也就是说, [如果是非持久常量,会在RSHUTDOWN阶段就将该常量释放,否则只会在MSHUTDOWN阶段将内存释放], 在用户空间,也就是用户定义的常量都是非持久化的,通常扩展和内核定义的常量会设置为持久化, 因为如果常量被释放了,而下次请求又需要使用这个常量,该常量就必须在请求时初始化一次, 而对于常量这些不变的量来说就是个没有意义的重复计算。
通过define()函数定义的常量的模块编号都是PHP_USER_CONSTANT,这表示是用户定义的常量。 除此之外我们在平时使用较多的常量:如错误报告级别E_ALL, E_WARNING等常量就有点不同了。 这些是PHP内置定义的常量,他们属于标准常量。
标准常量注册操作: php_module_startup() -> zend_startup() -> zend_register_standard_constants()]
魔术常量 随着代码的位置而改变
[PHP已经在词法解析时将这些常量换成了对应的值]
几个 PHP 的“魔术常量”
名称 说明
__LINE__ 文件中的当前行号
__FILE__ 文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名。自 PHP 4.0.2 起,FILE 总是包含一个绝对路径(如果是符号连接,则是解析后的绝对路径),而在此之前的版本有时会包含一个相对路径。
__DIR__ 文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录。它等价于 dirname(FILE)。除非是根目录,否则 目录中名不包括末尾的斜杠。(PHP 5.3.0中新增)
__FUNCTION__ 函数名称(PHP 4.3.0 新加)。自 PHP 5 起本常量返回该函数被定义时的名字(区分大小写)。在 PHP 4 中该值总是小写> 字母的
__CLASS__ 类的名称(PHP 4.3.0 新加)。自 PHP 5 起本常量返回该类被定义时的名字(区分大小写)。在 PHP 4 中该值总是小写字母的
__METHOD__ 类的方法名(PHP 5.0.0 新加)。返回该方法被定义时的名字(区分大小写)。
__NAMESPACE__ 当前命名空间的名称(大小写敏感)。这个常量是在编译时定义的(PHP 5.3.0 新增)
前面有个比较特殊的地方,当func_name不存在时,FUNCTION被替换成空字符串, 你可能会想,怎么会有变量名不存在的方法呢,这里并不是匿名方法,匿名方法的function_name 并不是空的,而是:”{closure}”, 有兴趣的读者可以去代码找找在那里给定义了。
预定义变量
在PHP脚本执行的时候,用户全局变量(在用户空间显式定义的变量)会保存在一个HashTable数据类型的符号表(symbol_table)中, 而我们用得非常多的在全局范围内有效的变量却与这些用户全局变量不同。 例如:$_GET,$_POST,$_SERVER,$_FILES等变量,我们并没有在程序中定义这些变量,并且这些变量也同样保存在符号表中, 从这些表象我们不难得出结论:[PHP是在脚本运行之前就将这些特殊的变量加入到了符号表。] 在请求初始化阶段 RINIT
变量赋值
赋值左值存在引用 且左值不等于右值 MMP 这是COW 写时复制啊
1 |
$a = 10; |
此时Zend engine的实现行动
1 |
if (PZVAL_IS_REF(variable_ptr)) { // 如果is_ref_gc != 0
|
COW介绍: 这是一种推迟内存复制带来的内存管理优化,而当变量的值发生变化时,才会进行重新开辟内存空间,这个机制我们称为写时复制机制
EG:
1 |
$i = 4; //内核创建一个zval指针,并且为其以堆的方式开辟空间,让指针指向这个空间,将zval中的成员引用计数置为1,类型标记为整形,并且申请一个zvalue_value指针,同样以堆的方式以其开辟空间,同时将该联合体中的lval赋值为4,并且在symbal_table的hash表中记录变量i和zval指针的映射关系 |
赋值的左值不存在引用,左值的引用计数为1,左值等于右值
1 |
$a = 10; |
Zend engine的行为:
1 |
if (Z_DELREF_P(variable_ptr)==0) { // 引用计数减一操作
|
赋值的左值不存在引用,左值的引用计数为1,右值存在引用
1 |
$a = 10; |
这里的$c = $a;的操作就是我们所示的第三种情况。 对于这种情况,ZEND内核直接创建一个新的zval容器,左值的值为右值,并且左值的引用计数为1。 也就是说,这种情形$c不会与$a指向同一个zval。
赋值的左值不存在引用,左值的引用计数为1,右值不存在引用
1 |
$a = 10; |
这时,右值的引用计数加上,一般情况下,会对左值进行垃圾收集操作,将其移入垃圾缓冲池。垃圾缓冲池的功能是在PHP5.3后才有的。
情况五:赋值的左值不存在引用,左值的引用计数为大于0,右值存在引用,并且引用计数大于0
1 |
$a = 10; |
最后一个操作就是我们的情况五。 使用xdebug看引用计数发现,最终$a变量的引用计数为1,$va变量的引用计数为2,并且$va存在引用。
变量销毁
unset()是一个语法结构, 根据变量不同出发不同的操作
程序会先获取目标符号表,这个符号表是一个HashTable,然后将我们需要unset掉的变量从这个HashTable中删除。 如果对HashTable的元素删除操作成功,程序还会对EX(CVs)内存储的值进行清空操作。 以缓存机制来解释,在删除原始数据后,程序也会删除相对应的缓存内容,以免用户获取到脏数据。
变量作用域
对于全局变量,Zend引擎有一个_zend_executor_globals结构,该结构中的symbol_table就是全局符号表, 其中保存了在顶层作用域中的变量。
同样,函数或者对象的方法在被调用时会创建active_symbol_table来保存局部变量。
函数中的局部变量就存储在_zend_execute_data的symbol_table中,在执行当前函数的op_array时, 全局zend_executor_globals中的active_symbol_table会指向当前_zend_execute_data中的symbol_table。
数据类型转换
直接的变量赋值操作
运算式结果对变量的赋值操作
强制类型转换
- 允许进行强制类型转换的类型
(int), (integer) 转换为整型
(bool), (boolean) 转换为布尔类型
(float), (double) 转换为浮点类型
(string) 转换为字符串
(array) 转换为数组
(object) 转换为对象
(unset) 转换为NULL
(unset)\$a(仅仅是类型转换为了null) != unset($a)
PHP代码实现2 [从变量和数据的角度] 2的更多相关文章
- PHP代码实现2 [从变量和数据的角度] 1
PHP代码实现2 [从变量和数据的角度] 1 数据类型 1.静态类型语言,比如:C/Java等,在静态语言类型中,类型的检查是在<编译>(compile-time)确定的, 也就是说在运行 ...
- 019 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 13 数据类型转换的代码示例
019 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 13 数据类型转换的代码示例 本文知识点:Java中的数据类型转换案例 学习视频有误,导致没法写文,文章内容 ...
- java中静态的代码块,静态变量,静态方法
简单了解一下java虚拟机--jvm几个内存区域: 方法区:在java的虚拟机中有一块专门用来存放已经加载的类信息.常量.静态变量以及方法代码的内存区域, 常量池:常量池是方法区的一部分,主要用来存放 ...
- R语言通过loess去除某个变量对数据的影响
当我们想研究不同sample的某个变量A之间的差异时,往往会因为其它一些变量B对该变量的固有影响,而影响不同sample变量A的比较,这个时候需要对sample变量A进行标准化之后才能进行比较.标 ...
- R语言通过loess去除某个变量对数据的影响--CNV分析
当我们想研究不同sample的某个变量A之间的差异时,往往会因为其它一些变量B对该变量的固有影响,而影响不同sample变量A的比较,这个时候需要对sample变量A进行标准化之后才能进行比较.标准化 ...
- 线程系列07,使用lock语句块或Interlocked类型方法保证自增变量的数据同步
假设多个线程共享一个静态变量,如果让每个线程都执行相同的方法每次让静态变量自增1,这样的做法线程安全吗?能保证自增变量数据同步吗?本篇体验使用lock语句块和Interlocked类型方法保证自增变量 ...
- 在activity之间通过静态变量传递数据
在activity之间通过静态变量传递数据 一.简介 主要作用:解决intent不能传递非序列化的对象 评价:简单方便,但是容易发生内存泄露,所以要及时回收内存 二.具体操作 1.在传输数据的页面弄好 ...
- java静态类、静态方法、静态代码块,静态变量及实例方法,实例变量初始化顺序及内存管理,机制
1.当一个类被第一次使用时,它需要被类加载器加载,而加载过程涉及以下两点: (1)在加载一个类时,如果它的父类还未被加载,那么其父类必须先被加载: (2)当类加载到内存之后,按照在代码中的出现顺序执行 ...
- 【黑马JavaSE】1.1JavaSE、环境变量、CMD使用、常量、变量、数据类型转换(自动/强制)、ASCII码表、Unicode万国码表
文章目录 SUN公司,詹姆斯.劳瑟琳,Java祖师爷 Java语言开发环境搭建 把Java添加到环境变量的方法 命令行CMD里一些报的错误 命令控制行常用操作的代码展示 Notepad++.注释.标识 ...
随机推荐
- HDU 6045 17多校2 Is Derek lying?
题目传送:http://acm.hdu.edu.cn/showproblem.php?pid=6045 Time Limit: 3000/1000 MS (Java/Others) Memory ...
- 行为参数化和Lambda表达式
行为参数化是指拿出一个代码块把他准备好却不执行它.这个代码块以后可以被程序的其他部分调用,意味着你可以推迟这块代码的执行.方法接受多种行为作为参数,并在内部使用来完成不同的行为.行为参数话的好处在于可 ...
- python 和 matlab的caffe读数据细节
(1).prototxt中的输入表示一样,如 dim: 10 dim: 3 dim: 227 dim: 227 (2)代码喂入数据不一样: python: input_blob = np.ze ...
- post-image.sh hacking
#********************************************************************************* #* post-image.sh ...
- 【编程基础】编译器 cc、gcc、g++、CC 的区别
参考 1. 编译器 cc.gcc.g++.CC 的区别; 完
- POJO,JaveBean,VO,DTO
POJO - POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称. 使用POJO名称是为了避免和EJ ...
- Unity背包/商城物品逐个显示缓动效果-依次显示
道具栏/商城中物品逐个显示效果 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分 ...
- hdu--1029 编程之美 在数组a中 (元素个数n n是奇数)找一个数字 它出现的次数大于(n+1)/2
我为什么总是犯这些愚蠢错误啊,还是自己逻辑不够严谨. 努力ing...... #include <iostream> #include <cstdio> #include &l ...
- 腾讯云JavaWeb环境配置
腾讯云服务器Centos7系统配置javaWeb运行环境 java1.8 运行命令列表 yum list java-* 安装相应版本的jdk,一般含有devel的就是真正的jdk,如:java-1.8 ...
- C++学习(二十)(C语言部分)之 函数1
函数 printf 输出的函数 scanf 输入的函数函数是什么 怎么写 是一组一起执行一个任务的语句 一个程序的基本组成单位是函数 只需要知道函数名字和括号里面要填的内容 就可以调用函数 1.如果代 ...