Skip to content

Commit

Permalink
Enforced properties and methods to be all static.
Browse files Browse the repository at this point in the history
  • Loading branch information
Guilherme Blanco committed Dec 1, 2014
1 parent f222edd commit 5e90b58
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 7 deletions.
4 changes: 4 additions & 0 deletions Zend/tests/class_modifiers_001.phpt
Expand Up @@ -7,18 +7,22 @@ Check if final abstract classes are supported

final abstract 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 abstract class %s in %s on line %d
4 changes: 3 additions & 1 deletion Zend/tests/class_modifiers_002.phpt
@@ -1,12 +1,14 @@
--TEST--
Check if final abstract classes are final indeed
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";
Expand Down
18 changes: 18 additions & 0 deletions Zend/tests/class_modifiers_003.phpt
@@ -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";
}
}

?>
--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
@@ -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
@@ -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
42 changes: 36 additions & 6 deletions Zend/zend_compile.c
Expand Up @@ -3935,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 @@ -4228,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 @@ -4255,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

0 comments on commit 5e90b58

Please sign in to comment.