Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
28 changes: 28 additions & 0 deletions Zend/tests/class_modifiers_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--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 $c = "foo\n";

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation here.

}

echo A::$c;
A::display('Foo');

$a = new A;

?>
--EXPECTF--
foo
Hello Foo

Fatal error: Cannot instantiate abstract class %s in %s on line %d
24 changes: 24 additions & 0 deletions Zend/tests/class_modifiers_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Check if final abstract classes are indeed final
--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 $c = "foo\n";

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation here.

}

class B extends A
{
}

?>
--EXPECTF--
Fatal error: Class %s may not inherit from final class (%s) in %s on line %d
18 changes: 18 additions & 0 deletions Zend/tests/class_modifiers_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Check if final abstract classes enforces static methods
--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 function display($name)
{
echo "Hello $name\n";
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

}

?>
--EXPECTF--
Fatal error: Class %s contains non-static method (%s) and therefore cannot be declared 'abstract final' in %s on line %d
15 changes: 15 additions & 0 deletions Zend/tests/class_modifiers_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Check if final abstract classes enforces static properties
--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 $foo = 'bar';
}

?>
--EXPECTF--
Fatal error: Class %s contains non-static property declaration and therefore cannot be declared 'abstract final' in %s on line %d
15 changes: 15 additions & 0 deletions Zend/tests/class_modifiers_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Check if final abstract classes prevents abstract static methods
--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 abstract function display($name);
}

?>
--EXPECTF--
Fatal error: Class %s contains abstract static method (%s) and therefore cannot be declared 'abstract final' in %s on line %d
2 changes: 1 addition & 1 deletion Zend/zend_closures.c
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ void zend_register_closure_ce(TSRMLS_D) /* {{{ */

INIT_CLASS_ENTRY(ce, "Closure", closure_functions);
zend_ce_closure = zend_register_internal_class(&ce TSRMLS_CC);
zend_ce_closure->ce_flags |= ZEND_ACC_FINAL_CLASS;
zend_ce_closure->ce_flags |= ZEND_ACC_FINAL;
zend_ce_closure->create_object = zend_closure_new;
zend_ce_closure->serialize = zend_class_serialize_deny;
zend_ce_closure->unserialize = zend_class_unserialize_deny;
Expand Down
55 changes: 49 additions & 6 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 Expand Up @@ -3922,24 +3935,41 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
{
zend_class_entry *ce = CG(active_class_entry);
zend_bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0;
zend_bool in_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0;
zend_bool in_final = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
zend_bool in_trait = ZEND_CE_IS_TRAIT(ce);
zend_bool is_abstract = (op_array->fn_flags & ZEND_ACC_ABSTRACT) != 0;
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 *lcname;

if (in_abstract && in_final) {
if (is_abstract) {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains abstract static method (%s) and "
"therefore cannot be declared 'abstract final'", ce->name->val, name->val);
}

if (!is_static) {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains non-static method (%s) and "
"therefore cannot be declared 'abstract final'", ce->name->val, name->val);
}
}

if (in_interface) {
if ((op_array->fn_flags & ZEND_ACC_PPP_MASK) != ZEND_ACC_PUBLIC) {
zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface method "
"%s::%s() must be omitted", ce->name->val, name->val);
}

op_array->fn_flags |= ZEND_ACC_ABSTRACT;
} else if (is_static && (op_array->fn_flags & ZEND_ACC_ABSTRACT)) {
is_abstract = (op_array->fn_flags & ZEND_ACC_ABSTRACT) != 0;
} else if (is_static && is_abstract) {
zend_error(E_STRICT, "Static function %s::%s() should not be abstract",
ce->name->val, name->val);
}

if (op_array->fn_flags & ZEND_ACC_ABSTRACT) {
if (is_abstract) {
if (op_array->fn_flags & ZEND_ACC_PRIVATE) {
zend_error_noreturn(E_COMPILE_ERROR, "%s function %s::%s() cannot be declared private",
in_interface ? "Interface" : "Abstract", ce->name->val, name->val);
Expand Down Expand Up @@ -4215,17 +4245,30 @@ void zend_compile_func_decl(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */

void zend_compile_prop_decl(zend_ast *ast TSRMLS_DC) /* {{{ */
{
zend_class_entry *ce = CG(active_class_entry);
zend_ast_list *list = zend_ast_get_list(ast);
uint32_t flags = list->attr;
zend_class_entry *ce = CG(active_class_entry);
uint32_t i, children = list->children;
zend_string *doc_comment = NULL;

if (ce->ce_flags & ZEND_ACC_INTERFACE) {
zend_bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0;
zend_bool in_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0;
zend_bool in_final = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
zend_bool is_abstract = (flags & ZEND_ACC_ABSTRACT) != 0;
zend_bool is_final = (flags & ZEND_ACC_FINAL) != 0;
zend_bool is_static = (flags & ZEND_ACC_STATIC) != 0;

// Fail if abstract final class' properties are not static
if (in_abstract && in_final && !is_static) {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains non-static property declaration "
"and therefore cannot be declared 'abstract final'", ce->name->val);
}

if (in_interface) {
zend_error_noreturn(E_COMPILE_ERROR, "Interfaces may not include member variables");
}

if (flags & ZEND_ACC_ABSTRACT) {
if (is_abstract) {
zend_error_noreturn(E_COMPILE_ERROR, "Properties cannot be declared abstract");
}

Expand All @@ -4242,7 +4285,7 @@ void zend_compile_prop_decl(zend_ast *ast TSRMLS_DC) /* {{{ */
zend_string *name = zend_ast_get_str(name_ast);
zval value_zv;

if (flags & ZEND_ACC_FINAL) {
if (is_final) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, "
"the final modifier is allowed only for methods and classes",
ce->name->val, name->val);
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ typedef struct _zend_try_catch_element {
/* ZEND_ACC_EXPLICIT_ABSTRACT_CLASS denotes that a class was explicitly defined as abstract by using the keyword. */
#define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS 0x10
#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20
#define ZEND_ACC_FINAL_CLASS 0x40
#define ZEND_ACC_INTERFACE 0x80
#define ZEND_ACC_TRAIT 0x120

Expand Down Expand Up @@ -469,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
2 changes: 1 addition & 1 deletion Zend/zend_generators.c
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ void zend_register_generator_ce(TSRMLS_D) /* {{{ */

INIT_CLASS_ENTRY(ce, "Generator", generator_functions);
zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC);
zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS;
zend_ce_generator->ce_flags |= ZEND_ACC_FINAL;
zend_ce_generator->create_object = zend_generator_create;
zend_ce_generator->serialize = zend_class_serialize_deny;
zend_ce_generator->unserialize = zend_class_unserialize_deny;
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
&& !(parent_ce->ce_flags & ZEND_ACC_INTERFACE)) {
zend_error_noreturn(E_COMPILE_ERROR, "Interface %s may not inherit from class (%s)", ce->name->val, parent_ce->name->val);
}
if (parent_ce->ce_flags & ZEND_ACC_FINAL_CLASS) {
if (parent_ce->ce_flags & ZEND_ACC_FINAL) {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s may not inherit from final class (%s)", ce->name->val, parent_ce->name->val);
}

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_CLASS; }
| 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
Loading