在鸟哥《深入理解PHP原理之对象(一)》看到一段挺有意思的代码
<?php class Foo { private $_name = "laruence"; protected $_age = 28; } $foo = new Foo(); $arr = (array) $foo; var_dump($arr["\0Foo\0_name"]); var_dump($arr["\0*\0_age"]); //output: string(8) "laruence" int(28)至于这算不算BUG本文就不议论了,有兴趣的可以看看这里 (见: Bug #44273 access to private and protected class variables allowed when casting to array ): 下面有相关议论
那为什么对象转成数组以后能通过构建特殊的key去访问呢?直接看关键代码吧! (已下代码PHP版本为: 5.4.27)
/* Zend/zend_API.c +3361*/ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, const char *name, int name_length, zval *property, int access_type, const char *doc_comment, int doc_comment_len TSRMLS_DC) /* {{{ */ { ..... switch (access_type & ZEND_ACC_PPP_MASK) { case ZEND_ACC_PRIVATE: { char *priv_name; int priv_name_length; zend_mangle_property_name(&priv_name, &priv_name_length, ce->name, ce->name_length, name, name_length, ce->type & ZEND_INTERNAL_CLASS); property_info.name = priv_name; property_info.name_length = priv_name_length; } break; case ZEND_ACC_PROTECTED: { char *prot_name; int prot_name_length; zend_mangle_property_name(&prot_name, &prot_name_length, "*", 1, name, name_length, ce->type & ZEND_INTERNAL_CLASS); property_info.name = prot_name; property_info.name_length = prot_name_length; } break; case ZEND_ACC_PUBLIC: if (IS_INTERNED(name)) { property_info.name = (char*)name; } else { property_info.name = ce->type & ZEND_INTERNAL_CLASS ? zend_strndup(name, name_length) : estrndup(name, name_length); } property_info.name_length = name_length; break; } ..... zend_hash_quick_update(&ce->properties_info, name, name_length+1, h, &property_info, sizeof(zend_property_info), NULL); return SUCCESS; }
可以看到当属性是private和protected时候调用zend_mangle_property_name来构造property_info.name (转换后的key),而它们第3,4个参数也不一样,private传入的是类名,而protected传入的是'*'字符。在看看zend_mangle_property_name函数的实现就一目了然了。
/* Zend/zend_compile.c +5037 */ ZEND_API void zend_mangle_property_name(char **dest, int *dest_length, const char *src1, int src1_length, const char *src2, int src2_length, int internal) /* {{{ */ { char *prop_name; int prop_name_length; prop_name_length = 1 + src1_length + 1 + src2_length; prop_name = pemalloc(prop_name_length + 1, internal); prop_name[0] = '\0'; memcpy(prop_name + 1, src1, src1_length+1); memcpy(prop_name + 1 + src1_length + 1, src2, src2_length+1); *dest = prop_name; *dest_length = prop_name_length; } /* }}} */
所以对象的private属性我们可以强制转换为array后通过 $obj["\0类名\0属性名"] 来访问,而protected则是 $obj["\0*\0属性名"]
至于流程可以通过"zend_do_declare_property"关键字到 http://lxr.php.net/ 搜索, 大概就是 zend_do_declare_property->zend_declare_property_ex->zend_mangle_property_name
嗯,好像也没写什么的样子,一些细节比如property_info被zend_hash_quick_update, 类型的转换都没理解清楚。