define() vs const 该如何选择?

使用 define(),除非考虑到可读性、类常量、或关注微优化

1、在 PHP 中是使用 define() 函数来定义常量,PHP 5.3.0 以后,PHP 中也能够使用 const 关键字来声明常量了,一个常量一旦被定义,就不能再改变或者取消定义

2、常量只能包含标量数据(booleanintegerfloatstring)。可以定义 resource 常量,但应尽量避免,因为会造成不可预料的结果

3、可以简单的通过指定其名字来取得常量的值,与变量不同,不应该在常量前面加上 $ 符号。如果常量名是动态的,也可以用函数 constant() 来获取常量的值。用 get_defined_constants() 可以获得所有已定义的常量列表

Note: 常量和(全局)变量在不同的名字空间中。这意味着例如 TRUE$TRUE 是不同的

 

常量和变量有如下不同

常量前面没有美元符号($);
常量只能用 define() 函数定义,而不能通过赋值语句;
常量可以不用理会变量的作用域而在任何地方定义和访问;
常量一旦定义就不能被重新定义或者取消定义;
常量的值只能是标量。

 define vs const

1、define() 在执行期定义常量,使用 const 关键字定义常量必须处于最顶端的作用区域,因为用此方法是在编译时定义的这就意味着不能在函数内,循环内以及 if 语句之内用 const 来定义常量,这样 const 就有轻微的速度优势, 但不值得考虑这个问题

2、define() 将常量放入全局作用域,虽然你可以在常量名中包含命名空间, 这意味着你不能使用 define() 定义类常量

3、define() 允许你在常量名和常量值中使用表达式,而 const 则都不允许,这使得 define() 更加灵活

示例

1、const不能使用条件表达式定义常量. 如果要定义一个全局常量,只能在表达式外面

if (...) {
    const FOO = 'BAR';    // invalid
}
// but
if (...) {
    define('FOO', 'BAR'); // valid
}

2、const 可以使用一个静态标量 (number, string or other constant like true, false, null, __FILE__), 反之define()没有限制.不过从 PHP 5.6 之后可以使用常量表达式

const BIT_5 = 1 << 5;    // valid since PHP 5.6, invalid previously
define('BIT_5', 1 << 5); // always valid

 3、const 使用一个普通的常量名称 反之define() 可以使用任意的常量表达式

for ($i = 0; $i < 32; ++$i) {
    define('BIT_' . $i, 1 << $i);
}

 4、const总是大小写敏感的, 反之define() 允许你定义一个大小写不敏感的常量通过第三个参数

define('FOO', 'BAR', true);
echo FOO; // BAR
echo foo; // BAR

5、const simply reads nicer. It's a language construct instead of a function and also is consistent with how you define constants in classes,const defines a constant in the current namespace, while define() has to be passed the full namespace name:

namespace A\B\C;
// To define the constant A\B\C\FOO:
const FOO = 'BAR';
define('A\B\C\FOO', 'BAR');

 6、Since PHP 5.6 const constants can also be arrays, while define() does not support arrays yet. However arrays will be supported for both cases in PHP 7.

const FOO = [1, 2, 3];    // valid in PHP 5.6
define('FOO', [1, 2, 3]); // invalid in PHP 5.6, valid in PHP 7.0

As consts are language constructs and defined at compile time they are a bit faster than define().

It is well known that PHP define() are slow when using a large number of constants. People have even invented things like apc_load_constants() and hidef to get around this.

consts make the definition of constants approximately twice as fast (on development machines with XDebug turned on even more). Lookup time on the other hand does not change (as both constant types share the same lookup table)

Finally, note that const can also be used within a class or interface to define a class constant or interface constant. define cannot be used for this purpose:

class Foo {
    const BAR = 2; // valid
}
// but
class Baz {
    define('QUX', 2); // invalid
}

 Summary

Unless you need any type of conditional or expressional definition, use consts instead of define()s - simply for the sake of readability!

 

<?php
// 来看看这两种方法如何处理 namespaces
namespace MiddleEarth\Creatures\Dwarves;
const GIMLI_ID = 1;
define('MiddleEarth\Creatures\Elves\LEGOLAS_ID', 2);

echo(\MiddleEarth\Creatures\Dwarves\GIMLI_ID);  // 1
echo(\MiddleEarth\Creatures\Elves\LEGOLAS_ID);  // 2; 注意:我们使用了 define()

// Now let's declare some bit-shifted constants representing ways to enter Mordor.
define('TRANSPORT_METHOD_SNEAKING', 1 << 0); // OK!
const TRANSPORT_METHOD_WALKING = 1 << 1; //Compile error! const can't use expressions as values

// 接下来, 条件常量。
define('HOBBITS_FRODO_ID', 1);

if($isGoingToMordor){
    define('TRANSPORT_METHOD', TRANSPORT_METHOD_SNEAKING); // OK!
    const PARTY_LEADER_ID = HOBBITS_FRODO_ID // 编译错误: const 不能用于 if 块中
}

// 最后, 类常量
class OneRing{
    const MELTING_POINT_DEGREES = 1000000; // OK!
    define('SHOW_ELVISH_DEGREES', 200); // 编译错误: 在类内不能使用 define()
}
?>

 无论你选择哪一种,请保持一致

看一下define在内核中如何实现的

/* {{{ 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;
	}
}
/* }}} */


/* {{{ proto bool defined(string constant_name)
   Check whether a constant exists */
ZEND_FUNCTION(defined)
{
	char *name;
	int name_len;
	zval c;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
		return;
	}
	
	if (zend_get_constant_ex(name, name_len, &c, NULL, ZEND_FETCH_CLASS_SILENT TSRMLS_CC)) {
		zval_dtor(&c);
		RETURN_TRUE;
	} else {
		RETURN_FALSE;
	}
}
/* }}} */

 const

ZEND_API int zend_register_constant(zend_constant *c TSRMLS_DC)
{
	char *lowercase_name = NULL;
	char *name;
	int ret = SUCCESS;
	ulong chash = 0;

#if 0
	printf("Registering constant for module %d\n", c->module_number);
#endif

	if (!(c->flags & CONST_CS)) {
		/* keep in mind that c->name_len already contains the '\0' */
		lowercase_name = estrndup(c->name, c->name_len-1);
		zend_str_tolower(lowercase_name, c->name_len-1);
		lowercase_name = (char*)zend_new_interned_string(lowercase_name, c->name_len, 1 TSRMLS_CC);
		name = lowercase_name;
		chash = IS_INTERNED(lowercase_name) ? INTERNED_HASH(lowercase_name) : 0;
	} else {
		char *slash = strrchr(c->name, '\\');
		if(slash) {
			lowercase_name = estrndup(c->name, c->name_len-1);
			zend_str_tolower(lowercase_name, slash-c->name);
			lowercase_name = (char*)zend_new_interned_string(lowercase_name, c->name_len, 1 TSRMLS_CC);
			name = lowercase_name;
			
			chash = IS_INTERNED(lowercase_name) ? INTERNED_HASH(lowercase_name) : 0;
		} else {
			name = c->name;
		}
	}
	if (chash == 0) {
		chash = zend_hash_func(name, c->name_len);
	}

	/* Check if the user is trying to define the internal pseudo constant name __COMPILER_HALT_OFFSET__ */
	if ((c->name_len == sizeof("__COMPILER_HALT_OFFSET__")
		&& !memcmp(name, "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1))
		|| zend_hash_quick_add(EG(zend_constants), name, c->name_len, chash, (void *) c, sizeof(zend_constant), NULL)==FAILURE) {
		
		/* The internal __COMPILER_HALT_OFFSET__ is prefixed by NULL byte */
		if (c->name[0] == '\0' && c->name_len > sizeof("\0__COMPILER_HALT_OFFSET__")
			&& memcmp(name, "\0__COMPILER_HALT_OFFSET__", sizeof("\0__COMPILER_HALT_OFFSET__")) == 0) {
			name++;
		}
		zend_error(E_NOTICE,"Constant %s already defined", name);
		str_free(c->name);
		if (!(c->flags & CONST_PERSISTENT)) {
			zval_dtor(&c->value);
		}
		ret = FAILURE;
	}
	if (lowercase_name && !IS_INTERNED(lowercase_name)) {
		efree(lowercase_name);
	}
	return ret;
}

 

参考

posted @ 2015-09-16 00:35  踏雪无痕SS  阅读(776)  评论(0编辑  收藏  举报