Skip to content

Commit d5e097e

Browse files
committed
initial import
1 parent df87963 commit d5e097e

10 files changed

+1071
-10
lines changed

Zend/zend_API.c

+11-1
Original file line numberDiff line numberDiff line change
@@ -3606,7 +3606,7 @@ ZEND_API const char *zend_get_module_version(const char *module_name) /* {{{ */
36063606
}
36073607
/* }}} */
36083608

3609-
ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment) /* {{{ */
3609+
ZEND_API int zend_declare_typed_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_uchar optional_type, zend_string *optional_type_name) /* {{{ */
36103610
{
36113611
zend_property_info *property_info, *property_info_ptr;
36123612

@@ -3676,12 +3676,22 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, z
36763676
property_info->flags = access_type;
36773677
property_info->doc_comment = doc_comment;
36783678
property_info->ce = ce;
3679+
property_info->type = optional_type;
3680+
property_info->type_name = optional_type_name ?
3681+
zend_new_interned_string(optional_type_name) : NULL;
3682+
36793683
zend_hash_update_ptr(&ce->properties_info, name, property_info);
36803684

36813685
return SUCCESS;
36823686
}
36833687
/* }}} */
36843688

3689+
ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment) /* {{{ */
3690+
{
3691+
return zend_declare_typed_property_ex(ce, name, property, access_type, doc_comment, 0, NULL);
3692+
}
3693+
/* }}} */
3694+
36853695
ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type) /* {{{ */
36863696
{
36873697
zend_string *key = zend_string_init(name, name_length, ce->type & ZEND_INTERNAL_CLASS);

Zend/zend_API.h

+1
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ ZEND_API zend_bool zend_is_callable(zval *callable, uint check_flags, zend_strin
314314
ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_name);
315315
ZEND_API const char *zend_get_module_version(const char *module_name);
316316
ZEND_API int zend_get_module_started(const char *module_name);
317+
ZEND_API int zend_declare_typed_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_uchar type, zend_string *type_name);
317318
ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment);
318319
ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type);
319320
ZEND_API int zend_declare_property_null(zend_class_entry *ce, const char *name, size_t name_length, int access_type);

Zend/zend_ast.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,12 @@ enum _zend_ast_kind {
140140
ZEND_AST_TRY,
141141
ZEND_AST_CATCH,
142142
ZEND_AST_PARAM,
143-
ZEND_AST_PROP_ELEM,
144143
ZEND_AST_CONST_ELEM,
145144

146145
/* 4 child nodes */
147146
ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT,
148147
ZEND_AST_FOREACH,
148+
ZEND_AST_PROP_ELEM,
149149
};
150150

151151
typedef uint16_t zend_ast_kind;

Zend/zend_compile.c

+73-4
Original file line numberDiff line numberDiff line change
@@ -5298,6 +5298,40 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
52985298
}
52995299
/* }}} */
53005300

5301+
5302+
/*
5303+
static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info)
5304+
{
5305+
if (ast->kind == ZEND_AST_TYPE) {
5306+
arg_info->type_hint = ast->attr;
5307+
} else {
5308+
zend_string *class_name = zend_ast_get_str(ast);
5309+
zend_uchar type = zend_lookup_builtin_type_by_name(class_name);
5310+
5311+
if (type != 0) {
5312+
if (ast->attr != ZEND_NAME_NOT_FQ) {
5313+
zend_error_noreturn(E_COMPILE_ERROR,
5314+
"Scalar type declaration '%s' must be unqualified",
5315+
ZSTR_VAL(zend_string_tolower(class_name)));
5316+
}
5317+
arg_info->type_hint = type;
5318+
} else {
5319+
uint32_t fetch_type = zend_get_class_fetch_type_ast(ast);
5320+
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
5321+
class_name = zend_resolve_class_name_ast(ast);
5322+
zend_assert_valid_class_name(class_name);
5323+
} else {
5324+
zend_ensure_valid_class_fetch_type(fetch_type);
5325+
zend_string_addref(class_name);
5326+
}
5327+
5328+
arg_info->type_hint = IS_OBJECT;
5329+
arg_info->class_name = class_name;
5330+
}
5331+
}
5332+
}
5333+
*/
5334+
53015335
void zend_compile_prop_decl(zend_ast *ast) /* {{{ */
53025336
{
53035337
zend_ast_list *list = zend_ast_get_list(ast);
@@ -5315,13 +5349,46 @@ void zend_compile_prop_decl(zend_ast *ast) /* {{{ */
53155349

53165350
for (i = 0; i < children; ++i) {
53175351
zend_ast *prop_ast = list->child[i];
5318-
zend_ast *name_ast = prop_ast->child[0];
5319-
zend_ast *value_ast = prop_ast->child[1];
5320-
zend_ast *doc_comment_ast = prop_ast->child[2];
5352+
zend_ast *type_ast = prop_ast->child[0];
5353+
zend_ast *name_ast = prop_ast->child[1];
5354+
zend_ast *value_ast = prop_ast->child[2];
5355+
zend_ast *doc_comment_ast = prop_ast->child[3];
53215356
zend_string *name = zend_ast_get_str(name_ast);
53225357
zend_string *doc_comment = NULL;
53235358
zval value_zv;
5359+
zend_uchar optional_type = 0;
5360+
zend_string *optional_type_name = NULL;
53245361

5362+
if (type_ast) {
5363+
if (type_ast->kind == ZEND_AST_TYPE) {
5364+
optional_type = type_ast->attr;
5365+
} else {
5366+
zend_string *class_name = zend_ast_get_str(type_ast);
5367+
zend_uchar type = zend_lookup_builtin_type_by_name(class_name);
5368+
5369+
if (type != 0) {
5370+
if (type_ast->attr != ZEND_NAME_NOT_FQ) {
5371+
zend_error_noreturn(E_COMPILE_ERROR,
5372+
"Scalar type declaration '%s' must be unqualified",
5373+
ZSTR_VAL(zend_string_tolower(class_name)));
5374+
}
5375+
optional_type = type;
5376+
} else {
5377+
uint32_t fetch_type = zend_get_class_fetch_type_ast(type_ast);
5378+
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
5379+
class_name = zend_resolve_class_name_ast(type_ast);
5380+
zend_assert_valid_class_name(class_name);
5381+
} else {
5382+
zend_ensure_valid_class_fetch_type(fetch_type);
5383+
zend_string_addref(class_name);
5384+
}
5385+
5386+
optional_type = IS_OBJECT;
5387+
optional_type_name = class_name;
5388+
}
5389+
}
5390+
}
5391+
53255392
/* Doc comment has been appended as last element in ZEND_AST_PROP_ELEM ast */
53265393
if (doc_comment_ast) {
53275394
doc_comment = zend_string_copy(zend_ast_get_str(doc_comment_ast));
@@ -5345,7 +5412,9 @@ void zend_compile_prop_decl(zend_ast *ast) /* {{{ */
53455412
}
53465413

53475414
name = zend_new_interned_string_safe(name);
5348-
zend_declare_property_ex(ce, name, &value_zv, flags, doc_comment);
5415+
if (optional_type) {
5416+
zend_declare_typed_property_ex(ce, name, &value_zv, flags, doc_comment, optional_type, optional_type_name);
5417+
} else zend_declare_property_ex(ce, name, &value_zv, flags, doc_comment);
53495418
}
53505419
}
53515420
/* }}} */

Zend/zend_compile.h

+2
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ typedef struct _zend_property_info {
299299
zend_string *name;
300300
zend_string *doc_comment;
301301
zend_class_entry *ce;
302+
zend_uchar type;
303+
zend_string *type_name;
302304
} zend_property_info;
303305

304306
#define OBJ_PROP(obj, offset) \

Zend/zend_language_parser.y

+4-4
Original file line numberDiff line numberDiff line change
@@ -798,10 +798,10 @@ property_list:
798798
;
799799

800800
property:
801-
T_VARIABLE backup_doc_comment
802-
{ $$ = zend_ast_create(ZEND_AST_PROP_ELEM, $1, NULL, ($2 ? zend_ast_create_zval_from_str($2) : NULL)); }
803-
| T_VARIABLE '=' expr backup_doc_comment
804-
{ $$ = zend_ast_create(ZEND_AST_PROP_ELEM, $1, $3, ($4 ? zend_ast_create_zval_from_str($4) : NULL)); }
801+
optional_type T_VARIABLE backup_doc_comment
802+
{ $$ = zend_ast_create(ZEND_AST_PROP_ELEM, $1, $2, NULL, ($3 ? zend_ast_create_zval_from_str($3) : NULL)); }
803+
| optional_type T_VARIABLE '=' expr backup_doc_comment
804+
{ $$ = zend_ast_create(ZEND_AST_PROP_ELEM, $1, $2, $4, ($5 ? zend_ast_create_zval_from_str($5) : NULL)); }
805805
;
806806

807807
class_const_list:

Zend/zend_vm_def.h

+54
Original file line numberDiff line numberDiff line change
@@ -1880,6 +1880,19 @@ ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
18801880
HANDLE_EXCEPTION();
18811881
}
18821882

1883+
/* not sure about this, sometimes container is reference to object */
1884+
if (Z_TYPE_P(container) == IS_OBJECT) {
1885+
zend_property_info *prop_info = zend_hash_find_ptr(&Z_OBJCE_P(container)->properties_info, Z_STR_P(property));
1886+
1887+
if (prop_info && prop_info->type) {
1888+
zend_throw_exception_ex(
1889+
zend_ce_type_error, prop_info->type,
1890+
"fetching reference to %s::$%s is disallowed",
1891+
ZSTR_VAL(Z_OBJCE_P(container)->name), Z_STRVAL_P(property));
1892+
HANDLE_EXCEPTION();
1893+
}
1894+
}
1895+
18831896
zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W);
18841897
FREE_OP2();
18851898
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
@@ -2041,6 +2054,18 @@ ZEND_VM_HANDLER(97, ZEND_FETCH_OBJ_UNSET, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
20412054

20422055
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
20432056

2057+
if (Z_TYPE_P(container) == IS_OBJECT && Z_TYPE_P(property) == IS_STRING) {
2058+
zend_property_info *prop_info = zend_hash_find_ptr(&Z_OBJCE_P(container)->properties_info, Z_STR_P(property));
2059+
2060+
if (prop_info && prop_info->type) {
2061+
zend_throw_exception_ex(zend_ce_type_error, prop_info->type,
2062+
"%s::$%s must not be unset",
2063+
ZSTR_VAL(Z_OBJCE_P(container)->name),
2064+
Z_STRVAL_P(property));
2065+
HANDLE_EXCEPTION();
2066+
}
2067+
}
2068+
20442069
zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET);
20452070
FREE_OP2();
20462071
if (OP1_TYPE == IS_VAR && READY_TO_DESTROY(free_op1)) {
@@ -2252,6 +2277,22 @@ ZEND_VM_C_LABEL(fast_assign_obj):
22522277
ZVAL_DEREF(value);
22532278
}
22542279

2280+
if (Z_TYPE_P(property_name) == IS_STRING) {
2281+
zend_property_info *prop_info = zend_hash_find_ptr(&Z_OBJCE_P(object)->properties_info, Z_STR_P(property_name));
2282+
2283+
if (prop_info && prop_info->type) {
2284+
if (!ZEND_SAME_FAKE_TYPE(prop_info->type, Z_TYPE_P(value))) {
2285+
zend_throw_exception_ex(zend_ce_type_error, prop_info->type,
2286+
"%s::$%s must be %s",
2287+
ZSTR_VAL(prop_info->ce->name),
2288+
Z_STRVAL_P(property_name),
2289+
(prop_info->type != IS_OBJECT) ?
2290+
zend_get_type_by_const(prop_info->type) : ZSTR_VAL(prop_info->type_name));
2291+
HANDLE_EXCEPTION();
2292+
}
2293+
}
2294+
}
2295+
22552296
Z_OBJ_HT_P(object)->write_property(object, property_name, value, (OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
22562297

22572298
if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
@@ -5845,6 +5886,19 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV)
58455886
break;
58465887
}
58475888
}
5889+
5890+
if (Z_TYPE_P(offset) == IS_STRING) {
5891+
zend_property_info *prop_info = zend_hash_find_ptr(&Z_OBJCE_P(container)->properties_info, Z_STR_P(offset));
5892+
5893+
if (prop_info && prop_info->type) {
5894+
zend_throw_exception_ex(zend_ce_type_error, prop_info->type,
5895+
"%s::$%s must not be unset",
5896+
ZSTR_VAL(Z_OBJCE_P(container)->name),
5897+
Z_STRVAL_P(offset));
5898+
HANDLE_EXCEPTION();
5899+
}
5900+
}
5901+
58485902
if (Z_OBJ_HT_P(container)->unset_property) {
58495903
Z_OBJ_HT_P(container)->unset_property(container, offset, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL));
58505904
} else {

0 commit comments

Comments
 (0)