php true to 1,php的true和TRUE引发的一些思考

最近在写php代码的时候突然很纠结一个问题,就是php里面的那些预定义常量也预定义变量到底有什么区别。比如说true和TRUE,

false和FALSE,null和NULL…

那么这些变量对到底有什么区别呢,作为一名热衷技术热衷死磕的码农,果断要测试一下。

var_dump(true === TRUE);

结果为bool(true),那意味着他们是没有区别的。那么问题来了,php为什么要放2个一毛一样的预定义变量呢?是有什么用意呢,还是本身语言设计的历史遗留问题?百度google一大圈

没有找到合适的答案,于是果断去看php的编译源代码,php的整个编译程序只有一个文件,都在zend/zend_compile.c里面,大概7000多行,看着眼花,没办法,也要慢慢看,结果还是找到了想要的答案,这里记录过程,以便查阅。

这里看看php字面量常量的编译

static int zend_add_const_name_literal(zend_op_array *op_array, zend_string *name, zend_bool unqualified)

{

zend_string *tmp_name;

int ret = zend_add_literal_string(op_array, &name);

size_t ns_len = 0, after_ns_len = ZSTR_LEN(name);

const char *after_ns = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));

if (after_ns) {

after_ns += 1;

ns_len = after_ns - ZSTR_VAL(name) - 1;

after_ns_len = ZSTR_LEN(name) - ns_len - 1;

/* lowercased namespace name & original constant name */

tmp_name = zend_string_init(ZSTR_VAL(name), ZSTR_LEN(name), 0);

zend_str_tolower(ZSTR_VAL(tmp_name), ns_len);

zend_add_literal_string(op_array, &tmp_name);

/* lowercased namespace name & lowercased constant name */

tmp_name = zend_string_tolower(name);

zend_add_literal_string(op_array, &tmp_name);

if (!unqualified) {

return ret;

}

} else {

after_ns = ZSTR_VAL(name);

}

/* original unqualified constant name */

tmp_name = zend_string_init(after_ns, after_ns_len, 0);

zend_add_literal_string(op_array, &tmp_name);

/* lowercased unqualified constant name */

tmp_name = zend_string_alloc(after_ns_len, 0);

zend_str_tolower_copy(ZSTR_VAL(tmp_name), after_ns, after_ns_len);

zend_add_literal_string(op_array, &tmp_name);

return ret;

}

看到其中一个片段

/* lowercased namespace name & lowercased constant name */

tmp_name = zend_string_tolower(name);

zend_add_literal_string(op_array, &tmp_name);

tmp_name = zend_string_tolower(name),这里全部转换成小写了

接着往下看,发现php的好多地方都是不区分大小写的,包括IF和if, 函数名,方法名…都是不区分大小写的

void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_bool has_body) {

op_array->scope = ce;

op_array->function_name = zend_string_copy(name);

lcname = zend_string_tolower(name);

lcname = zend_new_interned_string(lcname);

....

方法名称也全部在编译的时候转换成小写了!

static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl) {

unqualified_name = decl->name;

op_array->function_name = name = zend_prefix_with_ns(unqualified_name);

lcname = zend_string_tolower(name);

...

函数名称在编译的时候也全部转换成小写了!

key = zend_build_runtime_definition_key(lcname, decl->lex_pos);

zend_hash_update_ptr(CG(function_table), key, op_array);

这里表示把编译好的函数通过key,op_array代码送入进CG(function_table)的全局函数符号表!

void zend_compile_class_decl(zend_ast *ast) {

name = zend_new_interned_string(name);

lcname = zend_string_tolower(name);

name = zend_generate_anon_class_name(decl->lex_pos);

lcname = zend_string_tolower(name);

...

编译类的定义,也全部把类名称转换成小写了,所以类的名字大小写也没关系!

继续往下看…

void zend_compile_use(zend_ast *ast)

{

zend_ast_list *list = zend_ast_get_list(ast);

uint32_t i;

zend_string *current_ns = FC(current_namespace);

uint32_t type = ast->attr;

HashTable *current_import = zend_get_import_ht(type);

zend_bool case_sensitive = type == T_CONST;

...

if (case_sensitive) {

lookup_name = zend_string_copy(new_name);

} else {

lookup_name = zend_string_tolower(new_name);

}

...

这段代码可以得出2个结论:

use 语法是不区分大小写的

define常量是严格区分大小写的

继续看…

if (type == T_CLASS && zend_is_reserved_class_name(new_name)) {

zend_error_noreturn(E_COMPILE_ERROR, "Cannot use %s as %s because '%s' "

"is a special class name", ZSTR_VAL(old_name), ZSTR_VAL(new_name), ZSTR_VAL(new_name));

}

类名字定义,不能是预定义类名字的限制逻辑!

reserved_class_name的定义如下:

static const struct reserved_class_name reserved_class_names[] = {

{ZEND_STRL("bool")},

{ZEND_STRL("false")},

{ZEND_STRL("float")},

{ZEND_STRL("int")},

{ZEND_STRL("null")},

{ZEND_STRL("parent")},

{ZEND_STRL("self")},

{ZEND_STRL("static")},

{ZEND_STRL("string")},

{ZEND_STRL("true")},

{ZEND_STRL("void")},

{ZEND_STRL("iterable")},

{NULL, 0}

};

所以,类名都不能是,上述预定义字符串!

接下来看看类的编译

static int zend_add_class_name_literal(zend_op_array *op_array, zend_string *name) {

{

/* Original name */

int ret = zend_add_literal_string(op_array, &name);

/* Lowercased name */

zend_string *lc_name = zend_string_tolower(name);

zend_add_literal_string(op_array, &lc_name);

zend_alloc_cache_slot(ret);

return ret;

}

void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_bool has_body)

{

zend_class_entry *ce = CG(active_class_entry);

zend_bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0;

zend_bool in_trait = (ce->ce_flags & ZEND_ACC_TRAIT) != 0;

zend_bool is_public = (op_array->fn_flags & ZEND_ACC_PUBLIC) != 0;

zend_bool is_static = (op_array->fn_flags & ZEND_ACC_STATIC) != 0;

zend_string *lcname;

if (in_interface) {

if (!is_public || (op_array->fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_ABSTRACT))) {

zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface method "

"%s::%s() must be omitted", ZSTR_VAL(ce->name), ZSTR_VAL(name));

}

op_array->fn_flags |= ZEND_ACC_ABSTRACT;

}

if (op_array->fn_flags & ZEND_ACC_ABSTRACT) {

if (op_array->fn_flags & ZEND_ACC_PRIVATE) {

zend_error_noreturn(E_COMPILE_ERROR, "%s function %s::%s() cannot be declared private",

in_interface ? "Interface" : "Abstract", ZSTR_VAL(ce->name), ZSTR_VAL(name));

}

if (has_body) {

zend_error_noreturn(E_COMPILE_ERROR, "%s function %s::%s() cannot contain body",

in_interface ? "Interface" : "Abstract", ZSTR_VAL(ce->name), ZSTR_VAL(name));

}

ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;

} else if (!has_body) {

zend_error_noreturn(E_COMPILE_ERROR, "Non-abstract method %s::%s() must contain body",

ZSTR_VAL(ce->name), ZSTR_VAL(name));

}

op_array->scope = ce;

op_array->function_name = zend_string_copy(name);

lcname = zend_string_tolower(name);

lcname = zend_new_interned_string(lcname);

这段代码有意思,php的什么和抽象类的修饰权限,全是在编译的时候检测的,而且,检测与否其实就是一个if

if (in_interface) {

if (!is_public || (op_array->fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_ABSTRACT))) {

zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface method "

"%s::%s() must be omitted", ZSTR_VAL(ce->name), ZSTR_VAL(name));

}

op_array->fn_flags |= ZEND_ACC_ABSTRACT;

}

如果是接口,但是修饰flag不是public就报错了!

这也可以说明在php里面,写个interface,然后写很多实现,这种模式不是很有必要,也就是欺骗一下编译器,运行的时候还是到CG(function_table)或者CG(active_class_entry)中取查找,会让编译过程变慢,所以特么opcache这么有用的很大原因之一!

最后献上PHP之父Rasmus在php巴黎会议的一段话

Rasmus in a PHP conference in Paris saying more or less: "I'm definitely not a good programmer, in terms of following strict coding rules or standards,

but I can say that if you rely on case sensitivity to recognize one function name from another, you're in kind of serious trouble

这里简单翻译一下:“我绝对不是一个好的程序员,至少在遵循严格的编码规则和标准方面, 但是我不得不说,如果你要依靠大小写来区分自己写的另一个函数,那你将会非常麻烦”

到此为止,这估计是php不区分大小写的最好的理由了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值