Skip to content

Commit

Permalink
Add support for final class constants
Browse files Browse the repository at this point in the history
RFC: https://wiki.php.net/rfc/final_class_const

Co-authored-by: Nikita Popov <nikita.ppv@gmail.com>
  • Loading branch information
kocsismate and nikic committed Jul 6, 2021
1 parent c6d4f60 commit a5360e8
Show file tree
Hide file tree
Showing 28 changed files with 340 additions and 94 deletions.
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ PHP 8.1 UPGRADE NOTES
. Added support for intersection types.
They cannot be combined with union types.
RFC: https://wiki.php.net/rfc/pure-intersection-types
. Added support for the final modifier for class constants.
RFC: https://wiki.php.net/rfc/final_class_const

- Fileinfo:
. The fileinfo functions now accept and return, respectively, finfo objects
Expand Down
5 changes: 3 additions & 2 deletions Zend/tests/bug49472.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ class FooBar extends Foo implements ia {
new FooBar;

?>
--EXPECTF--
Fatal error: Cannot inherit previously-inherited or override constant c from interface ia in %s on line %d
===DONE===
--EXPECT--
===DONE===
13 changes: 13 additions & 0 deletions Zend/tests/constants/final_constants/final_const1.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Class constants support the final modifier
--FILE--
<?php

class Foo
{
final const A = "foo";
final public const B = "foo";
}

?>
--EXPECT--
22 changes: 22 additions & 0 deletions Zend/tests/constants/final_constants/final_const10.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Interface constants inherited from other interfaces can be redeclared
--FILE--
<?php

interface I1
{
const C = 1;
}

interface I2
{
const C = 2;
}

interface I3 extends I1, I2
{
const C = 3;
}

?>
--EXPECT--
22 changes: 22 additions & 0 deletions Zend/tests/constants/final_constants/final_const11.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Class constants cannot be inherited from both a class and an interface
--FILE--
<?php

class C
{
const C = 1;
}

interface I
{
const C = 1;
}

class C2 extends C implements I
{
}

?>
--EXPECTF--
Fatal error: Class C2 inherits both C::C and I::C, which is ambiguous in %s on line %d
22 changes: 22 additions & 0 deletions Zend/tests/constants/final_constants/final_const12.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Interface constants cannot be inherited from other interfaces
--FILE--
<?php

interface I1
{
const C = 1;
}

interface I2
{
const C = 2;
}

interface I3 extends I1, I2
{
}

?>
--EXPECTF--
Fatal error: Class I3 inherits both I1::C and I2::C, which is ambiguous in %s on line %d
18 changes: 18 additions & 0 deletions Zend/tests/constants/final_constants/final_const2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Final class constants cannot be overridden
--FILE--
<?php

class Foo
{
final const A = "foo";
}

class Bar extends Foo
{
const A = "bar";
}

?>
--EXPECTF--
Fatal error: Bar::A cannot override final constant Foo::A in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/constants/final_constants/final_const3.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Private class constants cannot be final
--FILE--
<?php

class Foo
{
private final const A = "foo";
}

?>
--EXPECTF--
Fatal error: Private constant Foo::A cannot be final as it is not visible to other classes in %s on line %d
17 changes: 17 additions & 0 deletions Zend/tests/constants/final_constants/final_const4.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Interface constants can be overridden directly
--FILE--
<?php

interface I
{
const X = 1;
}

class C implements I
{
const X = 2;
}

?>
--EXPECT--
18 changes: 18 additions & 0 deletions Zend/tests/constants/final_constants/final_const5.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Final interface constants cannot be overridden directly
--FILE--
<?php

interface I
{
final public const X = 1;
}

class C implements I
{
const X = 2;
}

?>
--EXPECTF--
Fatal error: C::X cannot override final constant I::X in %s on line %d
16 changes: 16 additions & 0 deletions Zend/tests/constants/final_constants/final_const6.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Final interface constants can be inherited
--FILE--
<?php

interface I
{
final public const X = 1;
}

class C implements I
{
}

?>
--EXPECT--
19 changes: 19 additions & 0 deletions Zend/tests/constants/final_constants/final_const7.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
Interface constants can be overridden indirectly
--FILE--
<?php

interface I
{
const X = 1;
}

class C implements I {}

class D extends C
{
const X = 2;
}

?>
--EXPECT--
22 changes: 22 additions & 0 deletions Zend/tests/constants/final_constants/final_const8.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Class constants cannot be inherited from two different interfaces
--FILE--
<?php

interface I1
{
const C = 1;
}

interface I2
{
const C = 1;
}

class C implements I1, I2
{
}

?>
--EXPECTF--
Fatal error: Class C inherits both I1::C and I2::C, which is ambiguous in %s on line %d
22 changes: 22 additions & 0 deletions Zend/tests/constants/final_constants/final_const9.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Class constants inherited from interfaces can be redeclared
--FILE--
<?php

interface I1
{
const C = 1;
}

interface I2
{
const C = 2;
}

class C implements I1, I2
{
const C = 3;
}

?>
--EXPECT--
2 changes: 1 addition & 1 deletion Zend/tests/errmsg_025.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ class test implements test1, test2 {
echo "Done\n";
?>
--EXPECTF--
Fatal error: Cannot inherit previously-inherited or override constant FOO from interface test2 in %s on line %d
Fatal error: Class test inherits both test1::FOO and test2::FOO, which is ambiguous in %s on line %d
2 changes: 1 addition & 1 deletion Zend/tests/errmsg_038.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ class test {
echo "Done\n";
?>
--EXPECTF--
Fatal error: Cannot declare property test::$var final, the final modifier is allowed only for methods and classes in %s on line %d
Fatal error: Cannot declare property test::$var final, the final modifier is allowed only for methods, classes, and class constants in %s on line %d
18 changes: 0 additions & 18 deletions Zend/tests/inter_01.phpt

This file was deleted.

6 changes: 3 additions & 3 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -4331,12 +4331,12 @@ ZEND_API void zend_declare_property_stringl(zend_class_entry *ce, const char *na
}
/* }}} */

ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment) /* {{{ */
ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int flags, zend_string *doc_comment) /* {{{ */
{
zend_class_constant *c;

if (ce->ce_flags & ZEND_ACC_INTERFACE) {
if (access_type != ZEND_ACC_PUBLIC) {
if (!(flags & ZEND_ACC_PUBLIC)) {
zend_error_noreturn(E_COMPILE_ERROR, "Access type for interface constant %s::%s must be public", ZSTR_VAL(ce->name), ZSTR_VAL(name));
}
}
Expand All @@ -4356,7 +4356,7 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c
c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
}
ZVAL_COPY_VALUE(&c->value, value);
ZEND_CLASS_CONST_FLAGS(c) = access_type;
ZEND_CLASS_CONST_FLAGS(c) = flags;
c->doc_comment = doc_comment;
c->attributes = NULL;
c->ce = ce;
Expand Down
11 changes: 9 additions & 2 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -7270,7 +7270,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z

if (flags & ZEND_ACC_FINAL) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, "
"the final modifier is allowed only for methods and classes",
"the final modifier is allowed only for methods, classes, and class constants",
ZSTR_VAL(ce->name), ZSTR_VAL(name));
}

Expand Down Expand Up @@ -7358,10 +7358,17 @@ void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr
zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
zval value_zv;

if (UNEXPECTED(flags & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_FINAL))) {
if (UNEXPECTED(flags & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT))) {
zend_check_const_and_trait_alias_attr(flags, "constant");
}

if (UNEXPECTED((flags & ZEND_ACC_PRIVATE) && (flags & ZEND_ACC_FINAL))) {
zend_error_noreturn(
E_COMPILE_ERROR, "Private constant %s::%s cannot be final as it is not visible to other classes",
ZSTR_VAL(ce->name), ZSTR_VAL(name)
);
}

zend_const_expr_to_zval(&value_zv, value_ast_ptr);
c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment);

Expand Down

0 comments on commit a5360e8

Please sign in to comment.