Skip to content

Commit af8d07f

Browse files
committed
[WIP] Support 'static const CONST_NAME = expr;'
Currently, this is limited to the global scope. This can be thought of as similar to C: the static const can only be seen within the namespace block scope. Differences from const: - This can only be declared once per namespace block, and has can only be used by following statements in the namespace block. - Supporting any expression (function calls, expr, etc.) in the value is planned. **Not implemented yet** - Eagerly evaluated when the expression might be dynamic. This is meant to make accidental infinite recursion less likely. This may throw in the future. The planned implementation will be shorthand for: ``` if (!defined('UNIQUE_NAME_FOR_LOCAL_X')) { define('UNIQUE_NAME_FOR_LOCAL_X', "Local value"); } class Example { const X = \LOCAL_NAME_FOR_LOCAL_X; } ``` ```php static const LOCAL_X = "Local value"; class Example { const X = LOCAL_X; } ```
1 parent 0f89d40 commit af8d07f

File tree

5 files changed

+109
-1
lines changed

5 files changed

+109
-1
lines changed

Zend/zend_ast.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
15401540

15411541
case ZEND_AST_CONST_DECL:
15421542
case ZEND_AST_CLASS_CONST_DECL:
1543+
case ZEND_AST_STATIC_CONST_DECL:
15431544
smart_str_appends(str, "const ");
15441545
goto simple_list;
15451546
case ZEND_AST_NAME_LIST:

Zend/zend_ast.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ enum _zend_ast_kind {
5858
ZEND_AST_PROP_DECL,
5959
ZEND_AST_CONST_DECL,
6060
ZEND_AST_CLASS_CONST_DECL,
61+
ZEND_AST_STATIC_CONST_DECL,
6162
ZEND_AST_NAME_LIST,
6263
ZEND_AST_TRAIT_ADAPTATIONS,
6364
ZEND_AST_USE,

Zend/zend_compile.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ void zend_file_context_begin(zend_file_context *prev_context) /* {{{ */
362362
FC(current_namespace) = NULL;
363363
FC(in_namespace) = 0;
364364
FC(has_bracketed_namespaces) = 0;
365+
FC(namespace_id) = 0;
365366
FC(declarables).ticks = 0;
366367
zend_hash_init(&FC(seen_symbols), 8, NULL, NULL, 0);
367368
}
@@ -835,6 +836,29 @@ uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
835836
}
836837
/* }}} */
837838

839+
zend_string *zend_concat6(char *str1, size_t str1_len, char *str2, size_t str2_len, char *str3, size_t str3_len, char *str4, size_t str4_len, char* str5, size_t str5_len, char* str6, size_t str6_len) /* {{{ */
840+
{
841+
size_t len = str1_len + str2_len + str3_len + str4_len + str5_len + str6_len;
842+
zend_string *res = zend_string_alloc(len, 0);
843+
844+
char* ptr = ZSTR_VAL(res);
845+
memcpy(ptr, str1, str1_len);
846+
ptr += str1_len;
847+
memcpy(ptr, str2, str2_len);
848+
ptr += str2_len;
849+
memcpy(ptr, str3, str3_len);
850+
ptr += str3_len;
851+
memcpy(ptr, str4, str4_len);
852+
ptr += str4_len;
853+
memcpy(ptr, str5, str4_len);
854+
ptr += str5_len;
855+
memcpy(ptr, str6, str6_len);
856+
ptr[str6_len] = '\0';
857+
858+
return res;
859+
}
860+
/* }}} */
861+
838862
zend_string *zend_concat3(char *str1, size_t str1_len, char *str2, size_t str2_len, char *str3, size_t str3_len) /* {{{ */
839863
{
840864
size_t len = str1_len + str2_len + str3_len;
@@ -847,11 +871,27 @@ zend_string *zend_concat3(char *str1, size_t str1_len, char *str2, size_t str2_l
847871

848872
return res;
849873
}
874+
/* }}} */
850875

851876
zend_string *zend_concat_names(char *name1, size_t name1_len, char *name2, size_t name2_len) {
852877
return zend_concat3(name1, name1_len, "\\", 1, name2, name2_len);
853878
}
854879

880+
// Start with 0 byte to make this form easy to detect
881+
zend_string *zend_prefix_with_file(zend_string *name) {
882+
zend_string *filename = CG(compiled_filename);
883+
zend_string *namespace_id_str = zend_long_to_str(FC(namespace_id));
884+
zend_string *result = zend_concat6(
885+
"\0", 1,
886+
ZSTR_VAL(filename), ZSTR_LEN(filename),
887+
"@", 1,
888+
ZSTR_VAL(namespace_id_str), ZSTR_LEN(namespace_id_str),
889+
"\\", 1,
890+
ZSTR_VAL(name), ZSTR_LEN(name));
891+
zend_string_release_ex(namespace_id_str, 0);
892+
return result;
893+
}
894+
855895
zend_string *zend_prefix_with_ns(zend_string *name) {
856896
if (FC(current_namespace)) {
857897
zend_string *ns = FC(current_namespace);
@@ -6947,6 +6987,61 @@ void zend_compile_const_decl(zend_ast *ast) /* {{{ */
69476987
}
69486988
/* }}}*/
69496989

6990+
void zend_compile_static_const_elem(zend_ast *const_ast) { /* {{{ */
6991+
zend_ast *name_ast = const_ast->child[0];
6992+
zend_ast *value_ast = const_ast->child[1];
6993+
zend_string *unqualified_name = zend_ast_get_str(name_ast);
6994+
6995+
zend_string *name;
6996+
znode name_node, value_node;
6997+
zval *value_zv = &value_node.u.constant;
6998+
6999+
value_node.op_type = IS_CONST;
7000+
7001+
if (zend_get_special_const(ZSTR_VAL(unqualified_name), ZSTR_LEN(unqualified_name))) {
7002+
zend_error_noreturn(E_COMPILE_ERROR,
7003+
"Cannot redeclare static constant '%s'", ZSTR_VAL(unqualified_name));
7004+
}
7005+
7006+
// "static const" lasts for the life of a namespace block.
7007+
name = zend_prefix_with_file(unqualified_name);
7008+
name = zend_new_interned_string(name);
7009+
7010+
if (!zend_hash_add_ptr(zend_get_import_ht(ZEND_SYMBOL_CONST), unqualified_name, name)) {
7011+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare static const %s because "
7012+
"the name is already in use", ZSTR_VAL(unqualified_name));
7013+
}
7014+
7015+
if (0) {
7016+
// FIXME implement
7017+
// 0. Check if it can be evaluated at compile time.
7018+
// 1. JMPNZ if defined(CONST_NAME)
7019+
// 2. Call define(x, op1)
7020+
// 3. End pointer
7021+
} else {
7022+
zend_const_expr_to_zval(value_zv, value_ast);
7023+
zend_string_addref(name);
7024+
7025+
name_node.op_type = IS_CONST;
7026+
ZVAL_STR(&name_node.u.constant, name);
7027+
7028+
zend_emit_op(NULL, ZEND_DECLARE_CONST, &name_node, &value_node);
7029+
7030+
zend_register_seen_symbol(name, ZEND_SYMBOL_CONST);
7031+
}
7032+
}
7033+
/* }}} */
7034+
7035+
void zend_compile_static_const_decl(zend_ast *ast) /* {{{ */
7036+
{
7037+
zend_ast_list *list = zend_ast_get_list(ast);
7038+
uint32_t i;
7039+
for (i = 0; i < list->children; ++i) {
7040+
zend_compile_static_const_elem(list->child[i]);
7041+
}
7042+
}
7043+
/* }}}*/
7044+
69507045
void zend_compile_namespace(zend_ast *ast) /* {{{ */
69517046
{
69527047
zend_ast *name_ast = ast->child[0];
@@ -7011,6 +7106,7 @@ void zend_compile_namespace(zend_ast *ast) /* {{{ */
70117106
if (with_bracket) {
70127107
FC(has_bracketed_namespaces) = 1;
70137108
}
7109+
FC(namespace_id)++;
70147110

70157111
if (stmt_ast) {
70167112
zend_compile_top_stmt(stmt_ast);
@@ -8706,6 +8802,9 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */
87068802
case ZEND_AST_CONST_DECL:
87078803
zend_compile_const_decl(ast);
87088804
break;
8805+
case ZEND_AST_STATIC_CONST_DECL:
8806+
zend_compile_static_const_decl(ast);
8807+
break;
87098808
case ZEND_AST_NAMESPACE:
87108809
zend_compile_namespace(ast);
87118810
break;

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ typedef struct _zend_file_context {
115115
HashTable *imports_const;
116116

117117
HashTable seen_symbols;
118+
zend_long namespace_id;
118119
} zend_file_context;
119120

120121
typedef union _zend_parser_stack_elem {

Zend/zend_language_parser.y

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
244244
%type <ast> variable_class_name dereferencable_scalar constant dereferencable
245245
%type <ast> callable_expr callable_variable static_member new_variable
246246
%type <ast> encaps_var encaps_var_offset isset_variables
247-
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
247+
%type <ast> top_statement_list use_declarations const_list static_const_list inner_statement_list if_stmt
248248
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
249249
%type <ast> echo_expr_list unset_variables catch_name_list catch_list parameter_list class_statement_list
250250
%type <ast> implements_list case_list if_stmt_without_else
@@ -334,6 +334,7 @@ top_statement:
334334
| T_USE use_declarations ';' { $$ = $2; $$->attr = ZEND_SYMBOL_CLASS; }
335335
| T_USE use_type use_declarations ';' { $$ = $3; $$->attr = $2; }
336336
| T_CONST const_list ';' { $$ = $2; }
337+
| T_STATIC T_CONST static_const_list ';' { $$ = $3; }
337338
;
338339

339340
use_type:
@@ -403,6 +404,11 @@ const_list:
403404
| const_decl { $$ = zend_ast_create_list(1, ZEND_AST_CONST_DECL, $1); }
404405
;
405406

407+
static_const_list:
408+
static_const_list ',' const_decl { $$ = zend_ast_list_add($1, $3); }
409+
| const_decl { $$ = zend_ast_create_list(1, ZEND_AST_STATIC_CONST_DECL, $1); }
410+
;
411+
406412
inner_statement_list:
407413
inner_statement_list inner_statement
408414
{ $$ = zend_ast_list_add($1, $2); }

0 commit comments

Comments
 (0)