【php原理】大话php常量

我们先看一个php常量的定义方法

define(‘PRICE‘, 30);

之前,我一直把define和C中的宏定义理解一致,因此在使用的时候也只是将其当成简单地字符替换。后来研究了PHP内核以后,发现PHP中的常量和宏定义完全不是一回事。

在PHP脚本运行的过程中,zend引擎会维护一个常量列表,对于普通用户来说,可以对这个常量列表进行CRUD操作,api分别为

define():定义一个常量

defined(): 判断一个常量是否存在

constant(): 得到一个常量的值

我们来看下PHP内核中常量的定义

typedef struct _zend_constant{
	zval value;
	int flags;
	char *name;
	uint name_len;
	int module_number;
} _zend_constant;

其中value为普通的变量结构zval,在此基础上,常量还定义了标记,常量名和模块号三个属性。

1. 标记(flags)

首先看常量的标记属性flag,目前可供选择的几个可能值分别为

用户态(可通过define方法的第三个参数赋值):

1: case sensitive

0: case insensitive

内核态:

CONST_PERSISTANT: persistent

CONST_CT_SUBST: allow compile-time substitution

用户定义的常量的标记默认为case sensitive, 也可通过define函数的第三个参数进行修改。对于内核态标记,CONST_PERSISTANT代表这个常量在内存申请的时候需要持久化。

上图是PHP脚本运行的的生命周期,我们知道多个request共享一次MINIT和MSHUTDOWN过程,而每个request有自己的RINIT和RSHUTDOWN过程,因此在MINIT中初始化的变量会常驻内存当中。这样被标记为CONST_PERSISTANT的常量只会在MSHUTDOWN中才会被析构掉。这也就不难理解,在内核C代码中,一些字符串和数字作为代码的一部分也被定义为PHP内核中的常量,它们通常就会被标记为CONST_PERSISTENT常量。

而CONST_CT_SUBST目前在内核里只有5个(TRUE, FALSE, NULL, ZEND_THREAD_SAFE, ZEND_DEBUG_BUILD),

2.模块号(module_number)

模块号同样分为用户态和内核态,确切的说,模块号就是用来做此区分的。用户定义的常量均为PHP_USER_CONSTANT, 除此以外,还有一些PHP内置的标准常量,例如E_ALL, E_WARNING等。在zend引擎启动以后,zend会进行标准常量的注册工作(zend_register_standard_constants()),一般来说,这些标准常量都被标记为持久化常量,即CONST_PERSISTENT

3. define()函数

define是PHP的内置接口,用户会通过define来定义常量,其实该方法过程如下

4. 魔术常量

PHP中还提供了一种魔术常量,他们的值是随着外部环境的变化而变化的,例如

__LINE__ 当前文件的行号

__FILE__ 文件的完整路径

这些魔术常量不是真正的常量(_zend_constants),PHP内核在此法解析的时候就会将其替换掉。

最后附上define的源码(在Zend/zend_builtin_functions.c中):

/* {{{ proto bool define(string constant_name, mixed value, boolean case_insensitive=false)
   Define a new constant */
ZEND_FUNCTION(define)
{
	char *name;
	int name_len;
	zval *val;
	zval *val_free = NULL;
	zend_bool non_cs = 0;
	int case_sensitive = CONST_CS;
	zend_constant c;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name, &name_len, &val, &non_cs) == FAILURE) {
		return;
	}

	if(non_cs) {
		case_sensitive = 0;
	}

	/* class constant, check if there is name and make sure class is valid & exists */
	if (zend_memnstr(name, "::", sizeof("::") - 1, name + name_len)) {
		zend_error(E_WARNING, "Class constants cannot be defined or redefined");
		RETURN_FALSE;
	}

repeat:
	switch (Z_TYPE_P(val)) {
		case IS_LONG:
		case IS_DOUBLE:
		case IS_STRING:
		case IS_BOOL:
		case IS_RESOURCE:
		case IS_NULL:
			break;
		case IS_OBJECT:
			if (!val_free) {
				if (Z_OBJ_HT_P(val)->get) {
					val_free = val = Z_OBJ_HT_P(val)->get(val TSRMLS_CC);
					goto repeat;
				} else if (Z_OBJ_HT_P(val)->cast_object) {
					ALLOC_INIT_ZVAL(val_free);
					if (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) == SUCCESS) {
						val = val_free;
						break;
					}
				}
			}
			/* no break */
		default:
			zend_error(E_WARNING,"Constants may only evaluate to scalar values");
			if (val_free) {
				zval_ptr_dtor(&val_free);
			}
			RETURN_FALSE;
	}

	c.value = *val;
	zval_copy_ctor(&c.value);
	if (val_free) {
		zval_ptr_dtor(&val_free);
	}
	c.flags = case_sensitive; /* non persistent */
	c.name = IS_INTERNED(name) ? name : zend_strndup(name, name_len);
	if(c.name == NULL) {
		RETURN_FALSE;
	}
	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;
	}
}
/* }}} */

【php原理】大话php常量

时间: 2024-10-11 17:38:03

【php原理】大话php常量的相关文章

常量池之字符串常量池String.intern()

运行时常量池是方法区(PermGen)的一部分. 需要提前了解: 1. JVM内存模型. 2. JAVA对象在JVM中内存分配 常量池的好处 常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享. - Java的自动装箱中其实就使用到了运行时常量池.详见:Java 自动装箱与拆箱的实现原理 - 还有字符串常量池. 字符串进入到常量池的两种方法: 1. new String()的实例调用intern()方法. ????执行intern()方法时,若常量池中不存在等值的字符串,JV

【Java基本功】一文读懂String及其包装类的实现原理

String作为Java中最常用的引用类型,相对来说基本上都比较熟悉,无论在平时的编码过程中还是在笔试面试中,String都很受到青睐,然而,在使用String过程中,又有较多需要注意的细节之处. String的连接 @Testpublic void contact () { //1连接方式 String s1 = "a"; String s2 = "a"; String s3 = "a" + s2; String s4 = "a&qu

Java基础2:基本数据类型与常量池

Java基础2:基本数据类型与常量池 基本数据类型的大小 int 32位 4字节  short 16位float 32位double 64位long 64位char 16位byte 8位boolean 1位//自动拆箱和装箱的意思就是,计算数值时,integer会自动转为int进行计算.//而当int传入类型为integer的引用时,int数值又会被包装为integer. 自动拆箱和装箱 //基本数据类型的常量池是-128到127之间.// 在这个范围中的基本数据类的包装类可以自动拆箱,比较时直

Java中String的基础知识

Java中String的基础知识 ==与equal的区别 基本数据类型,指的是java中的八种基本数据结构(byte,short,char,int,long,float,double,boolean),一般的比较是使用的 ==,比较的是他们的值. 复合数据类型(类) ==比较的是两个对象的引用,可以理解为在内存中的地址,除非是同一个new出来的对象,他们的 ==为true,否则,都为false. equal是object中的方法.object中的实现如下,内部还是使用==实现,也就是说,如果一个

ThinkPHP 中使用 IS_AJAX 判断原生 JS 中的 Ajax 出现问题

问题: 在 ThinkPHP 中使用原生 js 发起 Ajax 请求的时候.在控制器无法使用 IS_AJAX 进行判断.而使用 jQuery 中的 ajax 是没有问题的. 在ThinkPHP中.有一个判断是 ajax 请求的常量 IS_AJAX: Ajax 请求常用的有两种情况:一种是原生 js 的 ajax 请求.一种是 jQuery 的 ajax 请求. 分析: 先看看使用 jQuery 中使用 ajax 发送请求的时候的头信息: Accept: application/json, tex

Java 常用API(二)

目录 Java 常用API(二) 1. Object类 2. Date类 概述 构造方法和成员方法 3. DateFormat类 概述 SimpleDateFormat类 练习 4. Calendar类 5. System类 6. StringBuilder类 String类的原理 StringBuilder类的原理 StringBuilder的构造方法 StringBuilder的常用方法 7. 包装类 概述 装箱与拆箱(以 int 类型为例) 自动装箱与自动拆箱 基本类型与字符串类型之间的相

Java虚拟机原理图解-- 1.2.2、Class文件中的常量池详解(上)[转]

NO1.常量池在class文件的什么位置? 我的上一篇文章<Java虚拟机原理图解> 1.class文件基本组织结构中已经提到了class的文件结构,在class文件中的魔数.副版本号.主版本之后,紧接着就是常量池的数据区域了,如下图用红线包括的位置: 知道了常量池的位置后,然后让我们来揭秘常量池里究竟有什么东西吧- NO2.常量池的里面是怎么组织的? 常量池的组织很简单,前端的两个字节占有的位置叫做常量池计数器(constant_pool_count),它记录着常量池的组成元素  常量池项(

Java虚拟机原理图解-- 1.2、class文件中的常量池

了解JVM虚拟机原理 是每一个Java程序员修炼的必经之路.但是由于JVM虚拟机中有很多的东西讲述的比较宽泛,在当前接触到的关于JVM虚拟机原理的教程或者博客中,绝大部分都是充斥的文字性的描述,很难给人以形象化的认知,看完之后感觉还是稀里糊涂的.感于以上的种种,我打算把我在学习JVM虚拟机的过程中学到的东西,结合自己的理解,总结成<Java虚拟机原理图解> 这个系列,以图解的形式,将抽象的JVM虚拟机的知识具体化,希望能够对想了解Java虚拟机原理的的Java程序员 提供点帮助. 上一章节&l

《Java虚拟机原理图解》 1.2.2、Class文件里的常量池具体解释(上)

[last updated:2014/11/27] NO1.常量池在class文件的什么位置? 我的上一篇文章<Java虚拟机原理图解> 1.class文件基本组织结构中已经提到了class的文件结构,在class文件里的魔数.副版本号号.主版本号之后,紧接着就是常量池的数据区域了,例如以下图用红线包含的位置: 知道了常量池的位置后,然后让我们来揭秘常量池里到底有什么东西吧- NO2.常量池的里面是怎么组织的? 常量池的组织非常easy,前端的两个字节占有的位置叫做常量池计数器(constan