Skip to content

Commit

Permalink
Added support for abstract final classes.
Browse files Browse the repository at this point in the history
  • Loading branch information
Guilherme Blanco committed Dec 1, 2014
1 parent 32949df commit f222edd
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 23 deletions.
2 changes: 1 addition & 1 deletion Zend/tests/access_modifiers_003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ final final class test {
echo "Done\n";
?>
--EXPECTF--
Parse error: %s error,%sexpecting %s in %s on line %d
Fatal error: Multiple final modifiers are not allowed in %s on line %d
24 changes: 24 additions & 0 deletions Zend/tests/class_modifiers_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Check if final abstract classes are supported
--SKIPIF--
<?php if (version_compare(zend_version(), '2.4.0', '<=')) die('skip ZendEngine 2.4 or later needed'); ?>
--FILE--
<?php

final abstract class A
{
public static function display($name)
{
echo "Hello $name\n";
}
}

A::display('Foo');

$a = new A;

?>
--EXPECTF--
Hello Foo

Fatal error: Cannot instantiate abstract class %s in %s on line %d
22 changes: 22 additions & 0 deletions Zend/tests/class_modifiers_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Check if final abstract classes are final indeed
--SKIPIF--
<?php if (version_compare(zend_version(), '2.4.0', '<=')) die('skip ZendEngine 2.4 or later needed'); ?>
--FILE--
<?php

final abstract class A
{
public static function display($name)
{
echo "Hello $name\n";
}
}

class B extends A
{
}

?>
--EXPECTF--
Fatal error: Class %s may not inherit from final class (%s) in %s on line %d
13 changes: 13 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,19 @@ void zend_do_free(znode *op1 TSRMLS_DC) /* {{{ */
}
/* }}} */

uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
{
uint32_t new_flags = flags | new_flag;
if ((flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flag & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
zend_error_noreturn(E_COMPILE_ERROR, "Multiple abstract modifiers are not allowed");
}
if ((flags & ZEND_ACC_FINAL) && (new_flag & ZEND_ACC_FINAL)) {
zend_error_noreturn(E_COMPILE_ERROR, "Multiple final modifiers are not allowed");
}
return new_flags;
}
/* }}} */

uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
{
uint32_t new_flags = flags | new_flag;
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ ZEND_API binary_op_type get_binary_op(int opcode);
void zend_stop_lexing(TSRMLS_D);
void zend_emit_final_return(zval *zv TSRMLS_DC);
zend_ast *zend_ast_append_str(zend_ast *left, zend_ast *right);
uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag);
uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag);
zend_ast *zend_ast_append_doc_comment(zend_ast *list TSRMLS_DC);
void zend_handle_encoding_declaration(zend_ast *ast TSRMLS_DC);
Expand Down
64 changes: 42 additions & 22 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,9 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%token T_POW_EQUAL "**= (T_POW_EQUAL)"

%type <ast> top_statement namespace_name name statement function_declaration_statement
%type <ast> class_declaration_statement use_declaration const_decl inner_statement
%type <ast> class_declaration_statement trait_declaration_statement
%type <ast> interface_declaration_statement interface_extends_list
%type <ast> use_declaration const_decl inner_statement
%type <ast> expr optional_expr while_statement for_statement foreach_variable
%type <ast> foreach_statement declare_statement finally_statement unset_variable variable
%type <ast> extends_from parameter optional_type argument expr_without_variable global_var
Expand All @@ -247,15 +249,16 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
%type <ast> echo_expr_list unset_variables catch_list parameter_list class_statement_list
%type <ast> implements_list interface_extends_list case_list if_stmt_without_else
%type <ast> implements_list case_list if_stmt_without_else
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
%type <ast> class_const_list name_list trait_adaptations method_body non_empty_for_exprs
%type <ast> ctor_arguments alt_if_stmt_without_else trait_adaptation_list lexical_vars
%type <ast> lexical_var_list encaps_list array_pair_list non_empty_array_pair_list
%type <ast> assignment_list

%type <num> returns_ref function is_reference is_variadic class_type variable_modifiers
%type <num> returns_ref function is_reference is_variadic variable_modifiers
%type <num> method_modifiers trait_modifiers non_empty_member_modifiers member_modifier
%type <num> class_modifiers class_modifier

%type <str> backup_doc_comment

Expand Down Expand Up @@ -285,6 +288,8 @@ top_statement:
statement { $$ = $1; }
| function_declaration_statement { $$ = $1; }
| class_declaration_statement { $$ = $1; }
| trait_declaration_statement { $$ = $1; }
| interface_declaration_statement { $$ = $1; }
| T_HALT_COMPILER '(' ')' ';'
{ $$ = zend_ast_create(ZEND_AST_HALT_COMPILER,
zend_ast_create_zval_from_long(zend_get_scanned_file_offset(TSRMLS_C)));
Expand Down Expand Up @@ -337,8 +342,10 @@ inner_statement_list:

inner_statement:
statement { $$ = $1; }
| function_declaration_statement { $$ = $1; }
| class_declaration_statement { $$ = $1; }
| function_declaration_statement { $$ = $1; }
| class_declaration_statement { $$ = $1; }
| trait_declaration_statement { $$ = $1; }
| interface_declaration_statement { $$ = $1; }
| T_HALT_COMPILER '(' ')' ';'
{ $$ = NULL; zend_error_noreturn(E_COMPILE_ERROR,
"__HALT_COMPILER() can only be used from the outermost scope"); }
Expand Down Expand Up @@ -422,38 +429,51 @@ is_variadic:
;

class_declaration_statement:
class_type { $<num>$ = CG(zend_lineno); }
class_modifiers T_CLASS { $<num>$ = CG(zend_lineno); }
T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}'
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $<num>2, $6,
zend_ast_get_str($3), $4, $5, $8); }
| T_INTERFACE { $<num>$ = CG(zend_lineno); }
T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}'
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $<num>2, $5,
zend_ast_get_str($3), NULL, $4, $7); }
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $<num>3, $7, zend_ast_get_str($4), $5, $6, $9); }
| T_CLASS { $<num>$ = CG(zend_lineno); }
T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}'
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $<num>2, $6, zend_ast_get_str($3), $4, $5, $8); }
;

class_modifiers:
class_modifier { $$ = $1; }
| class_modifiers class_modifier { $$ = zend_add_class_modifier($1, $2); }
;

class_modifier:
T_ABSTRACT { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; }
| T_FINAL { $$ = ZEND_ACC_FINAL; }
;

class_type:
T_CLASS { $$ = 0; }
| T_ABSTRACT T_CLASS { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; }
| T_FINAL T_CLASS { $$ = ZEND_ACC_FINAL; }
| T_TRAIT { $$ = ZEND_ACC_TRAIT; }
trait_declaration_statement:
T_TRAIT { $<num>$ = CG(zend_lineno); }
T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}'
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $<num>2, $6, zend_ast_get_str($3), $4, $5, $8); }
;

interface_declaration_statement:
T_INTERFACE { $<num>$ = CG(zend_lineno); }
T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}'
{ $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $<num>2, $5, zend_ast_get_str($3), NULL, $4, $7); }
;

extends_from:
/* empty */ { $$ = NULL; }
| T_EXTENDS name { $$ = $2; }
;

interface_extends_list:
/* empty */ { $$ = NULL; }
| T_EXTENDS name_list { $$ = $2; }
;

implements_list:
/* empty */ { $$ = NULL; }
| T_IMPLEMENTS name_list { $$ = $2; }
;

interface_extends_list:
/* empty */ { $$ = NULL; }
| T_EXTENDS name_list { $$ = $2; }
;

foreach_variable:
variable { $$ = $1; }
| '&' variable { $$ = zend_ast_create(ZEND_AST_REF, $2); }
Expand Down

0 comments on commit f222edd

Please sign in to comment.