常量,顾名思义是一个常态的量值。它与值只绑定一次,它的作用在于有肋于增加程序的可读性和可靠性。 在PHP中,常量的名字是一个简单值的标识符,在脚本执行期间该值不能改变。 和变量一样,常量默认为大小写敏感,但是按照我们的习惯常量标识符总是大写的。 常量名和其它任何 PHP 标签遵循同样的命名规则。合法的常量名以字母或下划线开始,后面跟着任何字母,数字或下划线。

  • 在设定以后,常量的值无法更改
  • 常量名不需要开头的美元符号 ($)
  • 作用域不影响对常量的访问
  • 常量值只能是字符串或数字

在这一小节我们一起看下常量与我们常见的变量有啥区别,它在执行期间的不可改变的特性是如何实现的以及常量的定义过程。

首先看下常量与变量的区别,常量是在变量的zval结构的基础上添加了一额外的元素。如下所示为PHP中常量的内部结构。

常量的内部结构

typedef struct _zend_constant {zval value; /* zval结构,PHP内部变量的存储结构,在第一小节有说明 */int flags;  /* 常量的标记如 CONST_PERSISTENT | CONST_CS */char *name; /* 常量名称 */uint name_len;  int module_number;  /* 模块号 */
} zend_constant;

在Zend/zend_constants.h文件的33行可以看到如上所示的结构定义。 在常量的结构中,除了与变量一样的zval结构,它还包括属于常量的标记,常量名以及常量所在的模块号。

在了解了常量的存储结构后,我们来看PHP常量的定义过程。一个例子。

define('NOWAMAGIC', 'www.nowamagic.net');

这是一个很常规的常量定义过程,它使用了PHP的内置函数define。常量名为NOWAMAGIC,值为一个字符串,存放在zval结构中。 从这个例子出发,我们看下define定义常量的过程实现。

define定义常量

define是PHP的内置函数,在Zend/zend_builtin_functions.c文件中定义了此函数的实现。如下所示为部分源码:

/* {{{ proto bool define(string constant_name, mixed value, boolean case_insensitive=false)Define a new constant */
ZEND_FUNCTION(define)
{if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name,&name_len, &val, &non_cs) == FAILURE) {return;}... // 类常量定义 此处不做介绍... // 值类型判断和处理c.value = *val;zval_copy_ctor(&c.value);if (val_free) {zval_ptr_dtor(&val_free);}c.flags = case_sensitive; /* non persistent */c.name = zend_strndup(name, name_len);c.name_len = name_len+1;c.module_number = PHP_USER_CONSTANT;if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) {RETURN_TRUE;} else {RETURN_FALSE;}
}
/* }}} */

上面的代码已经对对象和类常量做了简化处理, 其实现基本上是一个将传递的参数传递给新建的zend_constant结构,并将这个结构体注册到常量列表中的过程。 关于大小写敏感,函数的第三个参数表示是否大小不敏感,默认为false(大小写敏感)。这个参数最后会赋值给zend_constant结构体的flags字段。其在函数中实现代码如下:

zend_bool non_cs = 0;   //  第三个参数的临时存储变量
int case_sensitive = CONST_CS;  //  是否大小写敏感,默认为1if(non_cs) {    //  输入为真,大小写不敏感case_sensitive = 0;
}c.flags = case_sensitive; //     赋值给结构体字段

从上面的define函数的实现来看,PHP对于常量的名称在定义时其实是没有所谓的限制。如下所示代码:

define('^_^', 'smile');if (defined('^_^')) {echo 'yes';
}else{echo 'no';
}
//$var = ^_^;   //语法错误
$var = constant("^_^");

通过defined函数测试表示,‘^_^’这个常量已经定义好,这样的常量无法直接调用, 只能使用constant语句来使用, 否则在语法解析时会显示错误。 在上面的代码中有用到一个判断常量是否定义的函数,下面我们看看这个函数是如何实现的。

判断常量是否设置

和define一样, defined的实现也在Zend/zend_builtin_functions.c文件, 其实现是一个读取参数变量,调用 zend_get_constant_ex函数获取常量的值来判断常量是否存在的过程。 而zend_get_constant_ex函数不仅包括了常规的常规的常量获取,还包括类常量的获取, 最后是通过zend_get_constant函数获取常量的值。在zend_get_constant函数中,基本上是通过下面的代码来获取常量的值。

zend_hash_find(EG(zend_constants), name, name_len+1, (void **) &c)

除此之外,只是调用这个函数之前和之后对name有一些特殊的处理。

常量的初始化

以上通过define定义的常量的模块编号都是PHP_USER_CONSTANT,这表示是用户定义的常量。 除此之外我们在平时使用较多的,如在显示所有级别错误报告时使用的E_ALL常量就有点不同了。 这里我们以cgi模式为例说明标准常量的定义过程。 整个调用顺序如下所示:

[php_cgi_startup() -> php_module_startup() -> zend_startup() -> zend_register_standard_constants()]

void zend_register_standard_constants(TSRMLS_D)
{... //  若干常量以REGISTER_MAIN_LONG_CONSTANT设置,REGISTER_MAIN_LONG_CONSTANT("E_ALL", E_ALL, CONST_PERSISTENT | CONST_CS);...
}

REGISTER_MAIN_LONG_CONSTANT宏展开是以zend_register_long_constant实现。 zend_register_long_constant函数将常量中值的类型,值,名称及模块号赋值给新的zend_constant。 并调用zend_register_constant添加到全局的常量列表中。

[php_cgi_startup() -> php_module_startup() -> zend_startup() -> zend_register_standard_constants() -> zend_register_constant]

ZEND_API void zend_register_long_constant(const char *name, uint name_len,long lval, int flags, int module_number TSRMLS_DC)
{zend_constant c;c.value.type = IS_LONG;c.value.value.lval = lval;c.flags = flags;c.name = zend_strndup(name, name_len-1);c.name_len = name_len;c.module_number = module_number;zend_register_constant(&c TSRMLS_CC);
}

zend_register_constant函数首先根据常量中的c->flags判断是否区分大小写, 如果不区分,则名字统一为小写,如果包含"\\",也统一成小写。否则为定义的名字 然后将调用下面的语句将当前常量添加到EG(zend_constants)。 EG(zend_constants)是一个HashTable(这在前面的章节中说明), 下面的代码是将常量添加到这个HashTable中。

zend_hash_add(EG(zend_constants), name, c->name_len, (void *) c,sizeof(zend_constant), NULL)==FAILURE)

在php_module_startup函数中,除了zend_startup函数中有注册标准的常量, 它本身体通过宏REGISTER_MAIN_LONG_CONSTANT等注册了一些常量,如:PHP_VERSION,PHP_OS等。

二十五、PHP内核探索:常量的实现 ☞ 脱离C语言和数学讨论底层都是耍流氓相关推荐

  1. moran指数 r语言_白话空间统计之二十五:空间权重矩阵(四)R语言中的空间权重矩阵(2):不同空间关系对莫兰指数的影响...

    原标题:白话空间统计之二十五:空间权重矩阵(四)R语言中的空间权重矩阵(2):不同空间关系对莫兰指数的影响 上一篇,讲了R语言中的空间权重矩阵的结构,这一节讲讲R语言里面空间权重矩阵的自定义. 与Ar ...

  2. 白话空间统计之二十五:空间权重矩阵(四)R语言中的空间权重矩阵(5)完结篇:自然临近关系

    自然临近是R语言中spdep中内置的最后一种临近关系. 所谓的自然临近,指的是不进行任何的预设关系,通过其空间位置来判断是否属于相互临近,那么这个空间位置指的是什么呢?众所周知,在几何图形中,三角形是 ...

  3. 白话空间统计之二十五:空间权重矩阵(四)R语言中的空间权重矩阵(2)

    上一篇,讲了R语言中的空间权重矩阵的结构,这一节讲讲R语言里面空间权重矩阵的自定义. 与ArcGIS自定义空间权重矩阵一样,R语言的空间权重矩阵如果纯粹从零开始自定义生成,是非常麻烦的事情,所以我们一 ...

  4. 白话空间统计之二十五:空间权重矩阵(四)R语言中的空间权重矩阵(4):K临近

    前面几节已经将spdep定义空间关系和转换为空间权重矩阵的方法及原理给大家做了个简单的介绍,本章将spdep中的其他几种空间关系做一个简单介绍,就当资讯存档了. 除去触点连接和距离范围(上一节描述的, ...

  5. 未处理异常和C++异常——Windows核心编程学习手札之二十五

    未处理异常和C++异常 --Windows核心编程学习手札之二十五 当一个异常过滤器返回EXCEPTION_CONTINUE_SEARCH标识符时是告诉系统继续上溯调用树,寻找另外的异常过滤器,但当每 ...

  6. 开源操作系统 FreeDOS 二十五年演进史:因微软抛弃 MS-DOS 而来!

    [CSDN 编者按]1994 年,微软宣布停止支持 MS-DOS,而 FreeDOS 的作者 Jim Hall 作为 MS-DOS 的超级粉丝,决定自行设计一个 MS-DOS 的自由软件替代--这就有 ...

  7. OpenGL 图形库的使用(二十五)—— 高级OpenGL之帧缓冲Framebuffers

    https://www.jianshu.com/p/d7066d6a02cc OpenGL 图形库的使用(二十五)-- 高级OpenGL之帧缓冲Framebuffers  刀客传奇 关注 0.2 20 ...

  8. Android进阶知识(二十五):Bitmap简介及其高效加载

    Android进阶知识(二十五):Bitmap简介及其高效加载 一.Bitmap   Bitmap代表一个位图,在Android中指的是一张图片,可以是png.jpg等格式的图片.BitmapDraw ...

  9. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十五:SDHC模块

    实验二十五:SDHC模块 笔者曾经说过,SD卡发展至今已经衍生许多版本,实验二十四就是针对版本SDV1.×的SD卡.实验二十四也说过,CMD24还有CMD17会故意偏移地址29,让原本范围指向从原本的 ...

  10. 2021年大数据Hadoop(二十五):YARN通俗介绍和基本架构

    全网最详细的Hadoop文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 本系列历史文章 前言 YARN通俗介绍和基本架构 Yarn通俗介绍 Yarn基本 ...

最新文章

  1. 机器学习必知必会10大算法
  2. 关于Latex一个简单例子
  3. Mysql事务结合spring管理
  4. TechEmpower Web 框架性能第19轮测试结果正式发布,ASP.NET Core在主流框架中拔得头筹...
  5. asp.net mvc 包含了一个 html 的助手类在哪里,c# - 在App_Code中使用@HTML的ASP.NET MVC Razor Helper - 堆栈内存溢出...
  6. wine装通达信_【已解决】谁能给我一个可以运行通达信股票软件的wine
  7. 图片|视频|音频文件扩展名(后缀)
  8. SPI通信协议_02
  9. java mybatisplus Error parsing time stamp
  10. 服务器内存型号的后缀字母,收藏!CPU型号20种后缀字母是什么意思?不再被坑!...
  11. github的健步如飞
  12. C++中使用sizeof查看几种数据类型所占内存字节大小
  13. ubuntu16.04 独立显卡驱动安装
  14. JBoss 中间件漏洞
  15. 多个excel工作簿、工作表合并
  16. Mac缓解或关闭鼠标加速
  17. FFmpeg 录制桌面、麦克风、摄像头
  18. java改变图片颜色_使用java代码实现证件照换背景色
  19. 把你的Windows Media Player 打造成全能的播放器
  20. Adobe Audition CS6

热门文章

  1. mybatis pagehelper自定义count语句
  2. 超级干货:手把手教你如何实现数据可视化
  3. mysql 小于转义_MyBatis中大于和小于号的转义写法
  4. 四年前,我设计了一款纹胸小样儿……如图……
  5. 某宝双十一自动养猫,解放你的双手得喵币
  6. 在第一列前面、中间、后面插入字符串
  7. Android微信抢红包服务源码
  8. quickchm乱码问题
  9. vue swiper click失效
  10. Error: Network Error