以前都是在全局空间(global space)下使用类名变量来实例化类。最近的需求里有一个在命名空间里面实例化一个类,既然是同一个命名空间,理所当然就想到直接用类名就好了,因为在同一命名空间里,用常规的new类名实例化类的时候也是不需要额外指定完整命名空间的。

结果发现,找不到类,由此看来,通过类名变量来实例化类的时候,是在全局变量里面查找类名的。

 

namespace text;
class User{
function say(){
echo "xxxxx";
}
}
//$f="User"; //error
$f="text\\User";
$o = new $f();
$o->say();

后来使用__NAMESPACE__常量来获取类所在的命名空间的时候发现直接在new里面连接字符串会出语法错误,可见php的语法解析在new这里只允许了变量和类名,不允许表达式。

$f="User";
$o = new (__NAMESPACE__ . '\\' .$f)();
$o->say();//error

为了证实这一点,到php的git 找下语法文件。Zend目录下zend_language_parser.y文件就是了。

%token T_NEW "new (T_NEW)"
new_expr:
 	T_NEW class_name_reference ctor_arguments
 	{ $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); }
 	| T_NEW anonymous_class
 	{ $$ = $2; }
class_name_reference:
 	class_name { $$ = $1; }
 	| new_variable { $$ = $1; }
 	;
new_variable:
 	simple_variable
 	{ $$ = zend_ast_create(ZEND_AST_VAR, $1); }
 	| new_variable '[' optional_expr ']'
 	{ $$ = zend_ast_create(ZEND_AST_DIM, $1, $3); }
 	| new_variable '{' expr '}'
 	{ $$ = zend_ast_create(ZEND_AST_DIM, $1, $3); }
 	| new_variable T_OBJECT_OPERATOR property_name
 	{ $$ = zend_ast_create(ZEND_AST_PROP, $1, $3); }
 	| class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable
 	{ $$ = zend_ast_create(ZEND_AST_STATIC_PROP, $1, $3); }
 	| new_variable T_PAAMAYIM_NEKUDOTAYIM simple_variable
 	{ $$ = zend_ast_create(ZEND_AST_STATIC_PROP, $1, $3); }
 	;

很明显可以看到,new的语法是new 后面加类名/各种变量/匿名类定义。

由此可见,php语法设计有很多地方缺乏一致性,这是因为早期设计比较简单,后面增加大量功能为了兼容性,又修修补补,导致一致性比较差。