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
12 changes: 12 additions & 0 deletions Zend/tests/access_modifiers_013.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Prevent abstract and final in the same class declaration
--FILE--
<?php

final abstract class C {
private function priv() { }
}

?>
--EXPECTF--
Fatal error: Cannot use the final modifier on an abstract class in %s on line %d
21 changes: 21 additions & 0 deletions Zend/tests/class_modifiers_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Check if static classes are supported
--FILE--
<?php
static class A
{
public static $c = "foo\n";
public static function display($name)
{
echo "Hello $name\n";
}
}
echo A::$c;
A::display('Foo');
$a = new A;
?>
--EXPECTF--
foo
Hello Foo

Fatal error: Cannot instantiate static class %s in %s on line %d
21 changes: 21 additions & 0 deletions Zend/tests/class_modifiers_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
Check if final static classes are supported
--FILE--
<?php

final static class A
{
public static $c = "foo\n";
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
17 changes: 17 additions & 0 deletions Zend/tests/class_modifiers_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Check if abstract static classes generates fatal error
--FILE--
<?php

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

?>
--EXPECTF--
Fatal error: Cannot use the static modifier on an abstract class in %s on line %d
16 changes: 16 additions & 0 deletions Zend/tests/class_modifiers_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Check if static classes enforces static methods
--FILE--
<?php

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

?>
--EXPECTF--
Fatal error: Class %s contains non-static method (%s) and therefore cannot be declared 'static' in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/class_modifiers_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Check if static classes enforces static properties
--FILE--
<?php

static class A
{
public $foo = 'bar';
}

?>
--EXPECTF--
Fatal error: Class %s contains non-static property declaration and therefore cannot be declared 'static' in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/class_modifiers_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Check if static classes prevents abstract static methods
--FILE--
<?php

final static class A
{
public static abstract function display($name);
}

?>
--EXPECTF--
Fatal error: Class %s contains abstract method (%s) and therefore cannot be declared 'static' in %s on line %d
15 changes: 10 additions & 5 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -1296,11 +1296,16 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties
* calling zend_merge_properties(). */
ZEND_API int _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties ZEND_FILE_LINE_DC TSRMLS_DC) /* {{{ */
{
if (class_type->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
char *what = (class_type->ce_flags & ZEND_ACC_INTERFACE) ? "interface"
:((class_type->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) ? "trait"
: "abstract class";
zend_error(E_ERROR, "Cannot instantiate %s %s", what, class_type->name->val);
if (class_type->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_STATIC|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
if (class_type->ce_flags & ZEND_ACC_INTERFACE) {
zend_error_noreturn(E_ERROR, "Cannot instantiate interface %s", class_type->name->val);
} else if (class_type->ce_flags & ZEND_ACC_STATIC) {
zend_error_noreturn(E_ERROR, "Cannot instantiate static class %s", class_type->name->val);
} else if ((class_type->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
zend_error_noreturn(E_ERROR, "Cannot instantiate trait %s", class_type->name->val);
} else {
zend_error_noreturn(E_ERROR, "Cannot instantiate abstract class %s", class_type->name->val);
}
}

zend_update_class_constants(class_type TSRMLS_CC);
Expand Down
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
58 changes: 52 additions & 6 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,25 @@ 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");
}
if ((new_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flags & ZEND_ACC_FINAL)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use the final modifier on an abstract class");
}
if ((new_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flags & ZEND_ACC_STATIC)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use the static modifier on an abstract class");
}
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 +3941,40 @@ 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_static = (ce->ce_flags & ZEND_ACC_STATIC) != 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_static) {
if (is_abstract) {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains abstract method (%s) and "
"therefore cannot be declared 'static'", 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 'static'", 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 +4250,28 @@ 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_static = (ce->ce_flags & ZEND_ACC_STATIC) != 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;

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

if (flags & ZEND_ACC_ABSTRACT) {
if (in_static && !is_static) {
zend_error_noreturn(E_COMPILE_ERROR, "Class %s contains non-static property declaration "
"and therefore cannot be declared 'static'", ce->name->val);
}

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

Expand All @@ -4242,7 +4288,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
Loading