当前位置: 首页 > 后端技术 > PHP

php中文文档一个错误引起的思考

时间:2023-03-30 05:54:19 PHP

PHP官方中文文档:https://www.php.net/manual/zh...类常量这一段说类常量不能是数学运算的结果.但是在我的业务代码中这样写是完全可行的classMyEvent{constREAD=1;常量写=1<<1;constALL=self::阅读|self::WRITE;}echoMyEvent::READ;echoMyEvent::WRITE;echoMyEvent::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'='exprbackup_doc_comment{$$=zend_ast_create(ZEND_AST_CONST_ELEM,$1,$3,($4?zend_ast_create_zval_from_str($4):NULL));};expr:变量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);}|变量'='expr{$$=zend_ast_create(ZEND_AST_ASSIGN,$1,$3);}|变量'=''&'变量{$$=zend_ast_create(ZEND_AST_ASSIGN_REF,$1,$4);}|T_CLONEexpr{$$=zend_ast_create(ZEND_AST_CLONE,$2);}|变量T_PLUS_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_ADD,$1,$3);}|变量T_MINUS_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_SUB,$1,$3);}|变量T_MUL_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_MUL,$1,$3);}|变量T_POW_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_POW,$1,$3);}|变量T_DIV_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_DIV,$1,$3);}|变量T_CONCAT_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_CONCAT,$1,$3);}|变量T_MOD_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_MOD,$1,$3);}|变量T_AND_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_BW_AND,$1,$3);}|变量T_OR_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_BW_OR,$1,$3);}|变量T_XOR_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_BW_XOR,$1,$3);}|变量T_SL_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_SL,$1,$3);}|变量T_SR_EQUALexpr{$$=zend_ast_create_assign_op(ZEND_SR,$1,$3);}|变量T_COALESCE_EQUALexpr{$$=zend_ast_create(ZEND_AST_ASSIGN_COALESCE,$1,$3);}|变量T_INC{$$=zend_ast_create(ZEND_AST_POST_INC,$1);}|T_INC变量{$$=zend_ast_create(ZEND_AST_PRE_INC,$2);}|变量T_DEC{$$=zend_ast_create(ZEND_AST_POST_DEC,$1);}|T_DEC变量{$$=zend_ast_create(ZEND_AST_PRE_DEC,$2);}|exprT_BOOLEAN_ORexpr{$$=zend_ast_create(ZEND_AST_OR,$1,$3);}|表达式T_BOOLEAN_AND表达式{$$=zend_ast_create(ZEND_AST_AND,$1,$3);}|exprT_LOGICAL_ORexpr{$$=zend_ast_create(ZEND_AST_OR,$1,$3);}|exprT_LOGICAL_ANDexpr{$$=zend_ast_create(ZEND_AST_AND,$1,$3);}|exprT_LOGICAL_XORexpr{$$=zend_ast_create_binary_op(ZEND_BOOL_XOR,$1,$3);}|表达式'|'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{$$=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);}|exprT_POWexpr{$$=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);}|exprT_SLexpr{$$=zend_ast_create_binary_op(ZEND_SL,$1,$3);}|exprT_SRexpr{$$=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);}|exprT_IS_IDENTICALexpr{$$=zend_ast_create_binary_op(ZEND_IS_IDENTICAL,$1,$3);}|exprT_IS_NOT_IDENTICALexpr{$$=zend_ast_create_binary_op(ZEND_IS_NOT_IDENTICAL,$1,$3);}|exprT_IS_EQUALexpr{$$=zend_ast_create_binary_op(ZEND_IS_EQUAL,$1,$3);}|exprT_IS_NOT_EQUALexpr{$$=zend_ast_create_binary_op(ZEND_IS_NOT_EQUAL,$1,$3);}|expr'<'expr{$$=zend_ast_create_binary_op(ZEND_IS_SMALLER,$1,$3);}|exprT_IS_SMALLER_OR_EQUALexpr{$$=zend_ast_create_binary_op(ZEND_IS_SMALLER_OR_EQUAL,$1,$3);}|expr'>'expr{$$=zend_ast_create(ZEND_AST_GREATER,$1,$3);}|exprT_IS_GREATER_OR_EQUALexpr{$$=zend_ast_create(ZEND_AST_GREATER_EQUAL,$1,$3);}|exprT_SPACESHIPexpr{$$=zend_ast_create_binary_op(ZEND_SPACESHIP,$1,$3);}|exprT_INSTANCEOFclass_name_reference{$$=zend_ast_create(ZEND_AST_INSTANCEOF,$1,$3);}|'('expr')'{$$=$2;如果($$->kind==ZEND_AST_CONDITIONAL)$$->attr=ZEND_PARENTHESIZED_CONDITIONAL;}|new_expr{$$=$1;}|表达式'?'expr':'expr{$$=zend_ast_create(ZEND_AST_CONDITIONAL,$1,$3,$5);}|表达式'?'':'expr{$$=zend_ast_create(ZEND_AST_CONDITIONAL,$1,NULL,$4);}|exprT_COALESCEexpr{$$=zend_ast_create(ZEND_AST_COALESCE,$1,$3);}|internal_functions_in_yacc{$$=$1;}|T_INT_CASTexpr{$$=zend_ast_create_cast(IS_LONG,$2);}|T_DOUBLE_CASTexpr{$$=zend_ast_create_cast(IS_DOUBLE,$2);}|T_STRING_CASTexpr{$$=zend_ast_create_cast(IS_STRING,$2);}|T_ARRAY_CASTexpr{$$=zend_ast_create_cast(IS_ARRAY,$2);}|T_OBJECT_CASTexpr{$$=zend_ast_create_cast(IS_OBJECT,$2);}|T_BOOL_CASTexpr{$$=zend_ast_create_cast(_IS_BOOL,$2);}|T_UNSET_CASTexpr{$$=zend_ast_create_cast(IS_NULL,$2);}|T_EXITexit_expr{$$=zend_ast_create(ZEND_AST_EXIT,$2);}|'@'expr{$$=zend_ast_create(ZEND_AST_SILENCE,$2);}|标量{$$=$1;}|'`'backticks_expr'`'{$$=zend_ast_create(ZEND_AST_SHELL_EXEC,$2);}|T_PRINTexpr{$$=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_YIELDexpr{$$=zend_ast_create(ZEND_AST_YIELD,$2,NULL);CG(extra_fn_flags)|=}|T_YIELDexprT_DOUBLE_ARROWexpr{$$=zend_ast_create(ZEND_AST_YIELD,$4,$2);CG(extra_fn_flags)|=ZEND_ACC_GENERATOR;}|T_YIELD_FROMexpr{$$=zend_ast_create(ZEND_AST_YIELD_FROM,$2);CG(extra_fn_flags)|=ZEND_ACC_GENERATOR;}|inline_function{$$=$1;}|T_STATICinline_function{$$=$2;((zend_ast_decl*)$$)->flags|=ZEND_ACC_STATIC;};很明显,只要符合expr的定义就没有问题classTest{constA=1+1;}这样写是完全可行的,但是有人会问,为什么不能这样写呢?classTest{constA=Test2::$a+1;}classTest2{publicstatic$a=1;}你不是说只要满足expr里面的规则,词法就没有问题分析阶段?constA=Test2::$a+1;符合规则expr'+'expr,为什么会报错。别担心,孩子,让我们看看你的错误是什么?PHPFatalerror:Constantexpressioncontainsinvalidoperationsinxxxx看看是不是Fatalerror,不是Parseerror,所以词法分析阶段没有问题。哪里有问题?编译阶段有问题,见如下代码:voidzend_compile_const_expr(zend_ast**ast_ptr)/*{{{*/{zend_ast*ast=*ast_ptr;如果(ast==NULL||ast->kind==ZEND_AST_ZVAL){返回;}if(!zend_is_allowed_in_const_expr(ast->kind)){zend_error_noreturn(E_COMPILE_ERROR,"常量表达式包含无效操作");}switch(ast->kind){caseZEND_AST_CLASS_CONST:zend_compile_const_expr_class_const(ast_ptr);休息;案例ZEND_AST_CLASS_NAME:zend_compile_const_expr_class_name(ast_ptr);休息;案例ZEND_AST_CONST:zend_compile_const_expr_const(ast_ptr);休息;案例ZEND_AST_MAGIC_CONST:zend_compile_const_expr_magic_const(ast_ptr);休息;默认值:zend_ast_apply(ast,zend_compile_const_expr);休息;}}看到触发这个错误信息的判断条件了吗?zend_is_allowed_in_const_expr(ast->kind),如果抽象语法树的类型不在允许范围内,即使符合词法分析规则,仍然会报错查看zend_is_allowed_in_const_expr的实现。zend_boolzend_is_allowed_in_const_expr(zend_ast_kindkind)/*{{{*/{returnkind==ZEND_AST_ZVAL||种类==ZEND_AST_BINARY_OP||种类==ZEND_AST_GREATER|||种类==ZEND_AST_OR||种类==ZEND_AST_UNARY_OP||种类==ZEND_AST_UNARY_PLUS||种类==ZEND_AST_UNARY_MINUS||种类==ZEND_AST_CONDITIONAL||种类==ZEND_AST_DIM||种类==ZEND_AST_ARRAY||==ZEND_AST_UNPACK||种类==ZEND_AST_CONST||种类==ZEND_AST_CLASS_CONST||种类==ZEND_AST_CLASS_NAME||种类==ZEND_AST_MAGIC_CONST||种类==ZEND_AST_COALESCE;静态变量ast->kind为ZEND_AST_STATIC_PROP,不在上述范围内,所以无法通过。如果将Test2中的静态变量替换为常量,编译会通过,因为ZEND_AST_CLASS_CONST在允许的范围内。所以官方中文文档中的这句话应该改为:常量的值必须是常量表达式,而不是变量、类属性或函数调用