PHP官方中文文檔:php
https://www.php.net/manual/zh...shell
類常量這一節express
說類常量不能爲數學運算的結果。但我業務代碼中這樣寫是徹底可行的app
class MyEvent { const READ = 1; const WRITE = 1 << 1; const ALL = self::READ | self::WRITE; } echo MyEvent::READ; echo MyEvent::WRITE; echo MyEvent::ALL;
我特地去看了源碼,詞法解析文件裏面:ide
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 '<' 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; }
這樣寫是徹底可行的,但有的人就會問了,爲何我這樣寫是不行的呢?oop
class Test { const A = Test2::$a + 1; } class Test2 { public static $a = 1; }
你不是說只要符合expr裏的規則,詞法解析階段就沒有問題嗎? const A = Test2::$a + 1;
符合規則 expr '+' expr
啊,爲何報錯了。spa
別急,少年,先看看你的報錯是啥?.net
PHP Fatal error: Constant expression contains invalid operations in xxxx
看到沒有,是Fatal error
,而不是Parse error
,因此詞法解析階段是沒有問題的,問題出在哪裏?編譯階段出問題了,看如下代碼:scala
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
是在容許範圍內的。
因此官方中文文檔中的這句話應該改成:常量的值必須是一個常量表達式,不能是變量、類屬性或函數調用