Hi,
It’s always struck me as slightly odd that traits don’t support constants the way classes and interfaces do.
I tried to find an explanation of the lack of support in the original RFC, and came up empty.
A consequent discussion in R11 has led me here.
Can anyone working on internals explain why traits don’t allow constants (either technically or philosophically)?
Moreover, what’s the opinion(s) of the list, on adding support for this? Would an RFC be needed?
Cheers
Stephen
Hi,
It’s always struck me as slightly odd that traits don’t support constants the way classes and interfaces do.
I tried to find an explanation of the lack of support in the original RFC, and came up empty.A consequent discussion in R11 has led me here.
Can anyone working on internals explain why traits don’t allow constants (either technically or philosophically)?
Moreover, what’s the opinion(s) of the list, on adding support for this?
Yes. Please.
-Mike
It’s always struck me as slightly odd that traits don’t support constants the way classes and interfaces do.
I tried to find an explanation of the lack of support in the original RFC, and came up empty.A consequent discussion in R11 has led me here.
Can anyone working on internals explain why traits don’t allow constants (either technically or philosophically)?
Moreover, what’s the opinion(s) of the list, on adding support for this? Would an RFC be needed?
I'm in favor of adding it. I doubt there's any insurmountable technical obstacles.
(I work on a static analyzer for php (https://github.com/phan/phan) and don't expect technical issues with that proposal)
I personally find it inconvenient to put constants used by traits in different classes/interfaces or in properties.
Other languages allow adding constants to traits:
- Java allows defining constants on interfaces, which allow defining default method implementations like PHP traits
- https://doc.rust-lang.org/edition-guide/rust-2018/trait-system/associated-constants.html allows defining constants with values on traits
(it also does various other things that impractical for php)
One thing to document/add tests for would be the resolution of self::MY_CONST
.
In a trait, self::method()
may be overridden by the method of the class that's using the trait.
I'd expect self::MY_CONST
to be the same.
<?php
trait T {
public static function main() { self::foo(); }
public static function foo() { echo "In trait\n"; }
}
class C {
use T;
public static function foo() { echo "In override\n"; }
}
C::main(); // outputs "In override"
Other thoughts:
- Should renaming and insteadof also work for constants? I'd assume yes https://www.php.net/manual/en/language.oop5.traits.php#language.oop5.traits.conflict
(e.g.use A, B { const A::MY_CONST insteadof B; }
) - We already check if there are conflicting values when inheriting constants from an interface and another interface/trait.
Cheers,
- Tyson
Can anyone working on internals explain why traits don’t allow constants (either technically or philosophically)?
Moreover, what’s the opinion(s) of the list, on adding support for this? Would an RFC be needed?
I don't think there's any insurmountable obstacles, but it would be useful to add a lot of tests because of potential edge cases (resolution, opcache, reference counting)
involved in traits and adding new places where constants can go.
(I'd also worked on a declined RFC and implementation for changing what could be used in a constant expression)
<?php
trait T {
// Similarly, if constants could be declared in traits,
// it would be useful to have the same behavior of `echo ClassOrTrait::MY_CONST`.
// const MY_CONST = self::OTHER_CONST
// const MY_CONST_2 = self::CONST_DEFINED_BOTH_IN_TRAIT_AND_CLASS
//
// MY_CONST_2 could probably be correctly implemented if
// subclasses copied the AST of the class constant's expression
// instead of the evaluated value if self:: or static:: (or __CLASS__?) were referenced.
public static function main($x = self::OTHER_CONST) {
echo $x, "\n";
}
}
// Getting reference counting correct is important to test
// `strtolower()` is locale-dependent, so the value isn't immutable.
define('REFERENCE_COUNTED_VALUE', strtolower('From C'));
class C {
use T;
const OTHER_CONST = REFERENCE_COUNTED_VALUE;
}
class D {
use T;
const OTHER_CONST = 'From D';
}
C::main();
try {
T::main();
} catch (Throwable $x) {
echo $x->getMessage() . "\n";
}
D::main();
/*
Output:
from c
Undefined class constant 'self::OTHER_CONST'
From D
*/
- Tyson
On Sat, Jun 27, 2020 at 3:53 PM Stephen Reay php-lists@koalephant.com
wrote:
Hi,
It’s always struck me as slightly odd that traits don’t support constants
the way classes and interfaces do.
I tried to find an explanation of the lack of support in the original RFC,
and came up empty.A consequent discussion in R11 has led me here.
Can anyone working on internals explain why traits don’t allow constants
(either technically or philosophically)?
Moreover, what’s the opinion(s) of the list, on adding support for this?
Would an RFC be needed?
Sounds like a reasonable addition. An RFC will be needed to specify the
details, which tend to be tricky whenever traits are involved. Some
suggestions:
- Constants mustn't be accessible directly on the trait, i.e.
TraitName::FOOBAR throws. self::FOOBAR within the trait is legal in that
"self" is remapped to the using class, as usual. - The same constants important from multiple traits should follow the
rules of properties, i.e. require that values match. Conflict resolution
for constants should very much not be supported.
Regards,
Nikita
On Sat, Jun 27, 2020 at 3:53 PM Stephen Reay <php-lists@koalephant.com mailto:php-lists@koalephant.com>
wrote:Hi,
It’s always struck me as slightly odd that traits don’t support constants
the way classes and interfaces do.
I tried to find an explanation of the lack of support in the original RFC,
and came up empty.A consequent discussion in R11 has led me here.
Can anyone working on internals explain why traits don’t allow constants
(either technically or philosophically)?
Moreover, what’s the opinion(s) of the list, on adding support for this?
Would an RFC be needed?Sounds like a reasonable addition. An RFC will be needed to specify the
details, which tend to be tricky whenever traits are involved. Some
suggestions:
- Constants mustn't be accessible directly on the trait, i.e.
TraitName::FOOBAR throws. self::FOOBAR within the trait is legal in that
"self" is remapped to the using class, as usual.- The same constants important from multiple traits should follow the
rules of properties, i.e. require that values match. Conflict resolution
for constants should very much not be supported.Regards,
Nikita
Hi All,
I finally found some time to write something to get this started. I’m following the advice given, and putting this on GH initially, any and all comments/feedback/suggestions are welcome!
https://github.com/stephenreay/php-rfcs/blob/master/trait-constants.md https://github.com/stephenreay/php-rfcs/blob/master/trait-constants.md
Note: I realise it doesn’t lay out voting choices, target version etc: It seems more pertinent to focus on nailing down some kind of fixed target of what should be achieved, before detailing the when/etc.
Cheers
Stephen