php文档对象已移动错误,由php中文文档一处错误引发的思考

PHP官方中文文档:

类常量这一节

bVbGWDa

说类常量不能为数学运算的结果。但我业务代码中这样写是完全可行的

class MyEvent

{

const READ = 1;

const WRITE = 1 << 1;

const ALL = self::READ | self::WRITE;

}

echo MyEvent::READ;

echo MyEvent::WRITE;

echo MyEvent::ALL;

我特意去看了源码,词法解析文件里面:

class_const_list:

class_const_list ',' class_const_decl { $$ = zend_ast_list_add($1, $3); }

| class_const_decl { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_CONST_DECL, $1); }

class_const_decl:

identifier '=' expr backup_doc_comment { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); }

;

expr:

variable

{ $$ = $1; }

| T_LIST '(' array_pair_list ')' '=' expr

{ $3->attr = ZEND_ARRAY_SYNTAX_LIST; $$ = zend_ast_create(ZEND_AST_ASSIGN, $3, $6); }

| '[' array_pair_list ']' '=' expr

{ $2->attr = ZEND_ARRAY_SYNTAX_SHORT; $$ = zend_ast_create(ZEND_AST_ASSIGN, $2, $5); }

| variable '=' expr

{ $$ = zend_ast_create(ZEND_AST_ASSIGN, $1, $3); }

| variable '=' '&' variable

{ $$ = zend_ast_create(ZEND_AST_ASSIGN_REF, $1, $4); }

| T_CLONE expr { $$ = zend_ast_create(ZEND_AST_CLONE, $2); }

| variable T_PLUS_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_ADD, $1, $3); }

| variable T_MINUS_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_SUB, $1, $3); }

| variable T_MUL_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_MUL, $1, $3); }

| variable T_POW_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_POW, $1, $3); }

| variable T_DIV_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_DIV, $1, $3); }

| variable T_CONCAT_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_CONCAT, $1, $3); }

| variable T_MOD_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_MOD, $1, $3); }

| variable T_AND_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_BW_AND, $1, $3); }

| variable T_OR_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_BW_OR, $1, $3); }

| variable T_XOR_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_BW_XOR, $1, $3); }

| variable T_SL_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_SL, $1, $3); }

| variable T_SR_EQUAL expr

{ $$ = zend_ast_create_assign_op(ZEND_SR, $1, $3); }

| variable T_COALESCE_EQUAL expr

{ $$ = zend_ast_create(ZEND_AST_ASSIGN_COALESCE, $1, $3); }

| variable T_INC { $$ = zend_ast_create(ZEND_AST_POST_INC, $1); }

| T_INC variable { $$ = zend_ast_create(ZEND_AST_PRE_INC, $2); }

| variable T_DEC { $$ = zend_ast_create(ZEND_AST_POST_DEC, $1); }

| T_DEC variable { $$ = zend_ast_create(ZEND_AST_PRE_DEC, $2); }

| expr T_BOOLEAN_OR expr

{ $$ = zend_ast_create(ZEND_AST_OR, $1, $3); }

| expr T_BOOLEAN_AND expr

{ $$ = zend_ast_create(ZEND_AST_AND, $1, $3); }

| expr T_LOGICAL_OR expr

{ $$ = zend_ast_create(ZEND_AST_OR, $1, $3); }

| expr T_LOGICAL_AND expr

{ $$ = zend_ast_create(ZEND_AST_AND, $1, $3); }

| expr T_LOGICAL_XOR expr

{ $$ = zend_ast_create_binary_op(ZEND_BOOL_XOR, $1, $3); }

| expr '|' expr { $$ = zend_ast_create_binary_op(ZEND_BW_OR, $1, $3); }

| expr '&' expr { $$ = zend_ast_create_binary_op(ZEND_BW_AND, $1, $3); }

| expr '^' expr { $$ = zend_ast_create_binary_op(ZEND_BW_XOR, $1, $3); }

| expr '.' expr { $$ = zend_ast_create_binary_op(ZEND_CONCAT, $1, $3); }

| expr '+' expr { $$ = zend_ast_create_binary_op(ZEND_ADD, $1, $3); }

| expr '-' expr { $$ = zend_ast_create_binary_op(ZEND_SUB, $1, $3); }

| expr '*' expr { $$ = zend_ast_create_binary_op(ZEND_MUL, $1, $3); }

| expr T_POW expr { $$ = zend_ast_create_binary_op(ZEND_POW, $1, $3); }

| expr '/' expr { $$ = zend_ast_create_binary_op(ZEND_DIV, $1, $3); }

| expr '%' expr { $$ = zend_ast_create_binary_op(ZEND_MOD, $1, $3); }

| expr T_SL expr { $$ = zend_ast_create_binary_op(ZEND_SL, $1, $3); }

| expr T_SR expr { $$ = zend_ast_create_binary_op(ZEND_SR, $1, $3); }

| '+' expr %prec '~' { $$ = zend_ast_create(ZEND_AST_UNARY_PLUS, $2); }

| '-' expr %prec '~' { $$ = zend_ast_create(ZEND_AST_UNARY_MINUS, $2); }

| '!' expr { $$ = zend_ast_create_ex(ZEND_AST_UNARY_OP, ZEND_BOOL_NOT, $2); }

| '~' expr { $$ = zend_ast_create_ex(ZEND_AST_UNARY_OP, ZEND_BW_NOT, $2); }

| expr T_IS_IDENTICAL expr

{ $$ = zend_ast_create_binary_op(ZEND_IS_IDENTICAL, $1, $3); }

| expr T_IS_NOT_IDENTICAL expr

{ $$ = zend_ast_create_binary_op(ZEND_IS_NOT_IDENTICAL, $1, $3); }

| expr T_IS_EQUAL expr

{ $$ = zend_ast_create_binary_op(ZEND_IS_EQUAL, $1, $3); }

| expr T_IS_NOT_EQUAL expr

{ $$ = zend_ast_create_binary_op(ZEND_IS_NOT_EQUAL, $1, $3); }

| expr '

{ $$ = zend_ast_create_binary_op(ZEND_IS_SMALLER, $1, $3); }

| expr T_IS_SMALLER_OR_EQUAL expr

{ $$ = zend_ast_create_binary_op(ZEND_IS_SMALLER_OR_EQUAL, $1, $3); }

| expr '>' expr

{ $$ = zend_ast_create(ZEND_AST_GREATER, $1, $3); }

| expr T_IS_GREATER_OR_EQUAL expr

{ $$ = zend_ast_create(ZEND_AST_GREATER_EQUAL, $1, $3); }

| expr T_SPACESHIP expr

{ $$ = zend_ast_create_binary_op(ZEND_SPACESHIP, $1, $3); }

| expr T_INSTANCEOF class_name_reference

{ $$ = zend_ast_create(ZEND_AST_INSTANCEOF, $1, $3); }

| '(' expr ')' {

$$ = $2;

if ($$->kind == ZEND_AST_CONDITIONAL) $$->attr = ZEND_PARENTHESIZED_CONDITIONAL;

}

| new_expr { $$ = $1; }

| expr '?' expr ':' expr

{ $$ = zend_ast_create(ZEND_AST_CONDITIONAL, $1, $3, $5); }

| expr '?' ':' expr

{ $$ = zend_ast_create(ZEND_AST_CONDITIONAL, $1, NULL, $4); }

| expr T_COALESCE expr

{ $$ = zend_ast_create(ZEND_AST_COALESCE, $1, $3); }

| internal_functions_in_yacc { $$ = $1; }

| T_INT_CAST expr { $$ = zend_ast_create_cast(IS_LONG, $2); }

| T_DOUBLE_CAST expr { $$ = zend_ast_create_cast(IS_DOUBLE, $2); }

| T_STRING_CAST expr { $$ = zend_ast_create_cast(IS_STRING, $2); }

| T_ARRAY_CAST expr { $$ = zend_ast_create_cast(IS_ARRAY, $2); }

| T_OBJECT_CAST expr { $$ = zend_ast_create_cast(IS_OBJECT, $2); }

| T_BOOL_CAST expr { $$ = zend_ast_create_cast(_IS_BOOL, $2); }

| T_UNSET_CAST expr { $$ = zend_ast_create_cast(IS_NULL, $2); }

| T_EXIT exit_expr { $$ = zend_ast_create(ZEND_AST_EXIT, $2); }

| '@' expr { $$ = zend_ast_create(ZEND_AST_SILENCE, $2); }

| scalar { $$ = $1; }

| '`' backticks_expr '`' { $$ = zend_ast_create(ZEND_AST_SHELL_EXEC, $2); }

| T_PRINT expr { $$ = zend_ast_create(ZEND_AST_PRINT, $2); }

| T_YIELD { $$ = zend_ast_create(ZEND_AST_YIELD, NULL, NULL); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }

| T_YIELD expr { $$ = zend_ast_create(ZEND_AST_YIELD, $2, NULL); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }

| T_YIELD expr T_DOUBLE_ARROW expr { $$ = zend_ast_create(ZEND_AST_YIELD, $4, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }

| T_YIELD_FROM expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; }

| inline_function { $$ = $1; }

| T_STATIC inline_function { $$ = $2; ((zend_ast_decl *) $$)->flags |= ZEND_ACC_STATIC; }

;

很明显,只要符合expr的定义,在词法解析阶段就不会有问题。

class Test

{

const A = 1 + 1;

}

这样写是完全可行的,但有的人就会问了,为什么我这样写是不行的呢?

class Test

{

const A = Test2::$a + 1;

}

class Test2

{

public static $a = 1;

}

你不是说只要符合expr里的规则,词法解析阶段就没有问题吗?

const A = Test2::$a + 1;符合规则 expr '+' expr啊,为什么报错了。

别急,少年,先看看你的报错是啥?

PHP Fatal error: Constant expression contains invalid operations in xxxx

看到没有,是Fatal error,而不是Parse error,所以词法解析阶段是没有问题的,问题出在哪里?编译阶段出问题了,看以下代码:

void zend_compile_const_expr(zend_ast **ast_ptr) /* {{{ */

{

zend_ast *ast = *ast_ptr;

if (ast == NULL || ast->kind == ZEND_AST_ZVAL) {

return;

}

if (!zend_is_allowed_in_const_expr(ast->kind)) {

zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");

}

switch (ast->kind) {

case ZEND_AST_CLASS_CONST:

zend_compile_const_expr_class_const(ast_ptr);

break;

case ZEND_AST_CLASS_NAME:

zend_compile_const_expr_class_name(ast_ptr);

break;

case ZEND_AST_CONST:

zend_compile_const_expr_const(ast_ptr);

break;

case ZEND_AST_MAGIC_CONST:

zend_compile_const_expr_magic_const(ast_ptr);

break;

default:

zend_ast_apply(ast, zend_compile_const_expr);

break;

}

}

看到触发这条错误信息的判断条件了吗?zend_is_allowed_in_const_expr(ast->kind),如果抽象语法树的类型不在允许范围内,就算符合词法解析规则,依然会报错。

看看zend_is_allowed_in_const_expr的实现.

zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */

{

return kind == ZEND_AST_ZVAL || kind == ZEND_AST_BINARY_OP

|| kind == ZEND_AST_GREATER || kind == ZEND_AST_GREATER_EQUAL

|| kind == ZEND_AST_AND || kind == ZEND_AST_OR

|| kind == ZEND_AST_UNARY_OP

|| kind == ZEND_AST_UNARY_PLUS || kind == ZEND_AST_UNARY_MINUS

|| kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM

|| kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM

|| kind == ZEND_AST_UNPACK

|| kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST

|| kind == ZEND_AST_CLASS_NAME

|| kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE;

}

清楚了,刚才的例子之所以通不过,是因为类的静态变量ast->kind为ZEND_AST_STATIC_PROP,并不在上面范围里面,所以通不过。如果你把Test2中的静态变量换成常量,编译就可以通过,因为ZEND_AST_CLASS_CONST是在允许范围内的。

所以官方中文文档中的这句话应该改为:

常量的值必须是一个常量表达式,不能是变量、类属性或函数调用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值