Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Require abstract classes to be explicitly declared 'abstract', in ord…

…er to

avoid making developers traverse the entire class/interface hierarchy
before they can figure out whether a class is instantiable
(ok, so it makes sense :)
  • Loading branch information...
commit 0338111950edcec427cfa2f1610dd6efcd43a14c 1 parent af4aa97
@zsuraski zsuraski authored
View
21 Zend/zend_compile.c
@@ -2214,9 +2214,7 @@ void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znod
new_class_entry->num_interfaces = 0;
zend_initialize_class_data(new_class_entry, 1 TSRMLS_CC);
- if (class_token->u.constant.value.lval == T_INTERFACE) {
- new_class_entry->ce_flags |= ZEND_ACC_INTERFACE;
- }
+ new_class_entry->ce_flags |= class_token->u.constant.value.lval;
if (parent_class_name->op_type != IS_UNUSED) {
doing_inheritance = 1;
@@ -2250,12 +2248,27 @@ void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znod
}
-void zend_do_end_class_declaration(znode *class_token TSRMLS_DC)
+static do_verify_abstract_class(TSRMLS_D)
+{
+ zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+
+ opline->opcode = ZEND_VERIFY_ABSTRACT_CLASS;
+ opline->op1 = CG(implementing_class);
+}
+
+
+void zend_do_end_class_declaration(znode *class_token, znode *parent_token TSRMLS_DC)
{
do_inherit_parent_constructor(CG(active_class_entry));
+
if (CG(active_class_entry)->num_interfaces > 0) {
CG(active_class_entry)->interfaces = (zend_class_entry **) emalloc(sizeof(zend_class_entry *)*CG(active_class_entry)->num_interfaces);
}
+ if (!(CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE)
+ && !(CG(active_class_entry)->ce_flags & ZEND_ACC_ABSTRACT_CLASS)
+ && ((parent_token->op_type != IS_UNUSED) || (CG(active_class_entry)->num_interfaces > 0))) {
+ do_verify_abstract_class(TSRMLS_C);
+ }
CG(active_class_entry) = NULL;
}
View
22 Zend/zend_compile.h
@@ -89,19 +89,20 @@ typedef struct _zend_brk_cont_element {
} zend_brk_cont_element;
-#define ZEND_ACC_STATIC 0x01
-#define ZEND_ACC_ABSTRACT 0x02
-#define ZEND_ACC_FINAL 0x04
-#define ZEND_ACC_INTERFACE 0x08
+#define ZEND_ACC_STATIC 0x01
+#define ZEND_ACC_ABSTRACT 0x02
+#define ZEND_ACC_FINAL 0x04
+#define ZEND_ACC_INTERFACE 0x08
+#define ZEND_ACC_ABSTRACT_CLASS 0x10
/* The order of those must be kept - public < protected < private */
-#define ZEND_ACC_PUBLIC 0x10
-#define ZEND_ACC_PROTECTED 0x20
-#define ZEND_ACC_PRIVATE 0x40
+#define ZEND_ACC_PUBLIC 0x100
+#define ZEND_ACC_PROTECTED 0x200
+#define ZEND_ACC_PRIVATE 0x400
#define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE)
-#define ZEND_ACC_CHANGED 0x80
-#define ZEND_ACC_IMPLICIT_PUBLIC 0x100
+#define ZEND_ACC_CHANGED 0x800
+#define ZEND_ACC_IMPLICIT_PUBLIC 0x1000
char *zend_visibility_string(zend_uint fn_flags);
@@ -352,7 +353,7 @@ void zend_do_case_after_statement(znode *result, znode *case_token TSRMLS_DC);
void zend_do_default_before_statement(znode *case_list, znode *default_token TSRMLS_DC);
void zend_do_begin_class_declaration(znode *class_token, znode *class_name, znode *parent_class_name TSRMLS_DC);
-void zend_do_end_class_declaration(znode *class_token TSRMLS_DC);
+void zend_do_end_class_declaration(znode *class_token, znode *parent_token TSRMLS_DC);
void zend_do_declare_property(znode *var_name, znode *value, zend_uint access_type TSRMLS_DC);
void zend_do_declare_class_constant(znode *var_name, znode *value TSRMLS_DC);
@@ -663,6 +664,7 @@ int zendlex(znode *zendlval TSRMLS_DC);
#define ZEND_ADD_INTERFACE 144
#define ZEND_VERIFY_INSTANCEOF 145
+#define ZEND_VERIFY_ABSTRACT_CLASS 146
/* end of block */
View
18 Zend/zend_execute.c
@@ -3125,7 +3125,7 @@ int zend_switch_free_handler(ZEND_OPCODE_HANDLER_ARGS)
int zend_new_handler(ZEND_OPCODE_HANDLER_ARGS)
{
- if (EX_T(EX(opline)->op1.u.var).EA.class_entry->ce_flags & ZEND_ACC_ABSTRACT) {
+ if (EX_T(EX(opline)->op1.u.var).EA.class_entry->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_ABSTRACT_CLASS)) {
char *class_type;
if (EX_T(EX(opline)->op1.u.var).EA.class_entry->ce_flags & ZEND_ACC_INTERFACE) {
@@ -4016,6 +4016,21 @@ int zend_verify_instanceof_handler(ZEND_OPCODE_HANDLER_ARGS)
}
+int zend_verify_abstract_class(ZEND_OPCODE_HANDLER_ARGS)
+{
+ zend_class_entry *ce = EX_T(EX(opline)->op1.u.var).EA.class_entry;
+ zend_bool declared_abstract = ce->ce_flags & ZEND_ACC_ABSTRACT_CLASS;
+ zend_bool detected_abstract = ce->ce_flags & ZEND_ACC_ABSTRACT;
+
+ if ((ce->ce_flags & ZEND_ACC_ABSTRACT)
+ && !(ce->ce_flags & ZEND_ACC_ABSTRACT_CLASS)) {
+ zend_error(E_ERROR, "Class %s contains abstract methods and must be declared abstract", ce->name);
+ }
+
+ NEXT_OPCODE();
+}
+
+
void zend_init_opcodes_handlers()
{
zend_opcode_handlers[ZEND_NOP] = zend_nop_handler;
@@ -4194,6 +4209,7 @@ void zend_init_opcodes_handlers()
zend_opcode_handlers[ZEND_ADD_INTERFACE] = zend_add_interface_handler;
zend_opcode_handlers[ZEND_VERIFY_INSTANCEOF] = zend_verify_instanceof_handler;
+ zend_opcode_handlers[ZEND_VERIFY_ABSTRACT_CLASS] = zend_verify_abstract_class;
}
/*
View
7 Zend/zend_language_parser.y
@@ -294,13 +294,14 @@ unticked_class_declaration_statement:
implements_list
'{'
class_statement_list
- '}' { zend_do_end_class_declaration(&$1 TSRMLS_CC); }
+ '}' { zend_do_end_class_declaration(&$1, &$2 TSRMLS_CC); }
;
class_entry_type:
- T_CLASS { $$.u.constant.value.lval = T_CLASS; }
- | T_INTERFACE { $$.u.constant.value.lval = T_INTERFACE; }
+ T_CLASS { $$.u.constant.value.lval = 0; }
+ | T_ABSTRACT T_CLASS { $$.u.constant.value.lval = ZEND_ACC_ABSTRACT_CLASS; }
+ | T_INTERFACE { $$.u.constant.value.lval = ZEND_ACC_INTERFACE; }
;
namespace_declaration_statement:
Please sign in to comment.
Something went wrong with that request. Please try again.