) {
zend_class_entry *ce = CG(active_class_entry);
zend_bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0;
- zend_bool in_trait = (ce->ce_flags & ZEND_ACC_TRAIT) != 0;
+ zend_bool in_trait = ZEND_CE_IS_TRAIT(ce);
zend_bool is_public = (op_array->fn_flags & ZEND_ACC_PUBLIC) != 0;
zend_bool is_static = (op_array->fn_flags & ZEND_ACC_STATIC) != 0;
zend_string *name = Z_STR_P(zend_ast_get_zval(name_ast));
zval value_zv;
- if (ce->ce_flags & ZEND_ACC_TRAIT) {
+ if (ZEND_CE_IS_TRAIT(ce)) {
zend_error_noreturn(E_COMPILE_ERROR, "Traits cannot have constants");
return;
}
}
}
+void zend_compile_implements(znode *class_node, zend_ast *ast TSRMLS_DC) {
+ zend_uint i;
+ for (i = 0; i < ast->children; ++i) {
+ zend_ast *class_ast = ast->child[i];
+ zend_string *name = Z_STR_P(zend_ast_get_zval(class_ast));
+
+ zend_op *opline;
+
+ /* Traits can not implement interfaces */
+ if (ZEND_CE_IS_TRAIT(CG(active_class_entry))) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as interface on '%s' "
+ "since it is a Trait", name->val, CG(active_class_entry)->name->val);
+ }
+
+ if (!zend_is_const_default_class_ref(class_ast)) {
+ zend_error_noreturn(E_COMPILE_ERROR,
+ "Cannot use '%s' as interface name as it is reserved", name->val);
+ }
+
+ opline = emit_op(NULL, ZEND_ADD_INTERFACE, class_node, NULL TSRMLS_CC);
+ opline->extended_value = ZEND_FETCH_CLASS_INTERFACE;
+ opline->op2_type = IS_CONST;
+ opline->op2.constant = zend_add_class_name_literal(CG(active_op_array),
+ zend_resolve_class_name_ast(class_ast TSRMLS_CC) TSRMLS_CC);
+
+ CG(active_class_entry)->num_interfaces++;
+ }
+}
+
void zend_compile_class_decl(zend_ast *ast TSRMLS_DC) {
zend_ast_decl *decl = (zend_ast_decl *) ast;
zend_ast *extends_ast = decl->child[0];
zend_string *name = decl->name, *lcname, *import_name = NULL;
zend_class_entry *ce = emalloc(sizeof(zend_class_entry));
zend_op *opline;
- znode extends_node;
+ znode declare_node, extends_node;
if (CG(active_class_entry)) {
zend_error_noreturn(E_COMPILE_ERROR, "Class declarations may not be nested");
}
if (extends_ast) {
- if (ce->ce_flags & ZEND_ACC_TRAIT) {
+ if (ZEND_CE_IS_TRAIT(ce)) {
zend_error_noreturn(E_COMPILE_ERROR, "A trait (%s) cannot extend a class. "
"Traits can only be composed from other traits with the 'use' keyword. Error",
name->val);
zend_compile_class_ref(&extends_node, extends_ast TSRMLS_CC);
}
-
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->result.var = get_temporary_variable(CG(active_op_array));
opline->result_type = IS_VAR;
+ GET_NODE(&declare_node, opline->result);
+
+ // TODO.AST drop this
+ GET_NODE(&CG(implementing_class), opline->result);
+
opline->op2_type = IS_CONST;
LITERAL_STR(opline->op2, lcname);
CG(active_class_entry) = ce;
+ if (implements_ast) {
+ zend_compile_implements(&declare_node, implements_ast TSRMLS_CC);
+ }
+
zend_compile_stmt(stmt_ast TSRMLS_CC);
- // TODO.AST traits, interfaces, extends, etc
+ // TODO.AST validity checks
+
+ /* Check for traits and proceed like with interfaces.
+ * The only difference will be a combined handling of them in the end.
+ * Thus, we need another opcode here. */
+ if (ce->num_traits > 0) {
+ ce->traits = NULL;
+ ce->num_traits = 0;
+ ce->ce_flags |= ZEND_ACC_IMPLEMENT_TRAITS;
+
+ emit_op(NULL, ZEND_BIND_TRAITS, &declare_node, NULL TSRMLS_CC);
+ }
+
+ if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))
+ && (extends_ast || ce->num_interfaces > 0)
+ ) {
+ zend_verify_abstract_class(ce TSRMLS_CC);
+ if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) {
+ do_verify_abstract_class(TSRMLS_C);
+ }
+ }
+
+ /* Inherit interfaces; reset number to zero, we need it for above check and
+ * will restore it during actual implementation.
+ * The ZEND_ACC_IMPLEMENT_INTERFACES flag disables double call to
+ * zend_verify_abstract_class() */
+ if (ce->num_interfaces > 0) {
+ ce->interfaces = NULL;
+ ce->num_interfaces = 0;
+ ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES;
+ }
CG(active_class_entry) = NULL;
}
{ $$.u.ast = zend_ast_create_decl(ZEND_AST_CLASS, $1.EA, $1.u.op.opline_num,
CG(zend_lineno), LANG_SCNG(yy_text), $5.u.op.ptr,
Z_STR($2.u.constant), $3.u.ast, $4.u.ast, $7.u.ast); AS($$); }
- /*| interface_entry T_STRING
- { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); }
- interface_extends_list
- '{'
- class_statement_list
- '}' { AS($6); zend_do_end_class_declaration(&$1, NULL TSRMLS_CC); }*/
+ | interface_entry T_STRING interface_extends_list
+ { $$.u.op.ptr = CG(doc_comment); CG(doc_comment) = NULL; }
+ '{' class_statement_list '}'
+ { $$.u.ast = zend_ast_create_decl(ZEND_AST_CLASS, $1.EA, $1.u.op.opline_num,
+ CG(zend_lineno), LANG_SCNG(yy_text), $4.u.op.ptr,
+ Z_STR($2.u.constant), NULL, $3.u.ast, $6.u.ast); AS($$); }
;
| T_FINAL T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_FINAL_CLASS; }
;
+interface_entry:
+ T_INTERFACE { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_INTERFACE; }
+;
+
extends_from:
/* empty */ { $$.u.ast = NULL; }
| T_EXTENDS name { $$.u.ast = $2.u.ast; }
;
-interface_entry:
- T_INTERFACE { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_INTERFACE; }
+interface_extends_list:
+ /* empty */ { $$.u.ast = NULL; }
+ | T_EXTENDS name_list { $$.u.ast = $2.u.ast; }
;
-/*interface_extends_list:
- /* empty /
- | T_EXTENDS interface_list
-;*/
-
implements_list:
/* empty */ { $$.u.ast = NULL; }
| T_IMPLEMENTS name_list { $$.u.ast = $2.u.ast; }
| name { $$.u.ast = $1.u.ast; }
;
-fully_qualified_class_name:
- namespace_name { $$ = $1; }
- | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); }
- | T_NS_SEPARATOR namespace_name { zval tmp; ZVAL_NEW_STR(&tmp, STR_ALLOC(Z_STRLEN($2.u.constant)+1, 0)); Z_STRVAL(tmp)[0] = '\\'; memcpy(Z_STRVAL(tmp) + 1, Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); if (Z_DELREF($2.u.constant) == 0) {efree(Z_STR($2.u.constant));} Z_STR($2.u.constant) = Z_STR(tmp); $$ = $2; }
-;
-
-
class_name_reference:
class_name { $$.u.ast = $1.u.ast; }
| new_variable { $$.u.ast = $1.u.ast; }