PHP traits - defining generic constants
PhpOopTraitsPhp Problem Overview
What is the best way to define constants that may be used by a number of classes within a namespace? I'm trying to avoid too much inheritance, so extending base classes is not an ideal solution, and I'm struggling to find a good solution using traits. Is this in any way possible in PHP 5.4 or should a different approach be taken?
I have the following situation:
trait Base
{
// Generic functions
}
class A
{
use Base;
}
class B
{
use Base;
}
The problem is that it is not possible to define constants in PHP traits. Ideally, I would want something like the following:
trait Base
{
const SOME_CONST = 'someconst';
const SOME_OTHER_CONST = 'someotherconst';
// Generic functions
}
Then these could be accessed though the class that applies the trait:
echo A::SOME_CONST;
echo B::SOME_OTHER_CONST;
But due to the limitations of traits this isn't possible. Any ideas?
Php Solutions
Solution 1 - Php
I ended up using user sectus's suggestion of interfaces as it feels like the least-problematic way of handling this. Using an interface to store constants rather than API contracts has a bad smell about it though so maybe this issue is more about OO design than trait implementation.
interface Definition
{
const SOME_CONST = 'someconst';
const SOME_OTHER_CONST = 'someotherconst';
}
trait Base
{
// Generic functions
}
class A implements Definition
{
use Base;
}
class B implements Definition
{
use Base;
}
Which allows for:
A::SOME_CONST;
B::SOME_CONST;
Solution 2 - Php
You could also use static variables. They can be used in the class or the trait itself. - Works fine for me as a replacement for const.
trait myTrait {
static $someVarA = "my specific content";
static $someVarB = "my second specific content";
}
class myCustomClass {
use myTrait;
public function hello()
{
return self::$someVarA;
}
}
Solution 3 - Php
To limit the scope of your constants, you can define them inside a namespace:
namespace Test;
const Foo = 123;
// generic functions or classes
echo Foo;
echo namespace\Foo;
A downside of this approach is that autoloading won't work for constants, at least not for 5.4; the typical way around this is to wrap those constants in a static class, i.e.:
namespace Test;
class Bar
{
const Foo = 123;
}
Solution 4 - Php
Not a good one, but maybe...
trait Base
{
public static function SOME_CONST()
{
return 'value1';
}
public static function SOME_OTHER_CONST()
{
return 'value2';
}
// generic functions
}
class A
{
use Base;
}
class B
{
use Base;
}
echo A::SOME_CONST();
echo B::SOME_OTHER_CONST();
Solution 5 - Php
Starting from PHP 8.1, it is possible to use readonly
properties in traits.
<?php
trait A
{
public readonly int $variable;
protected function initA(int $newValue){
$this->variable = $newValue;
}
public function changeVariable(int $newValue){
$this->variable = $newValue;
}
}
class B {
use A;
public function __construct() {
$this->initA(1);
}
}
$b = new B();
$b->changeVariable(5); // should faild: Fatal error: Uncaught Error: Cannot modify readonly property B::$variable
Solution 6 - Php
Something else to consider is whether or not you can use an abstract class instead, and then inherit.
abstract class Base
{
const A = 1;
const B = 2;
}
class Class1 extends Base {}
class Class2 extends Base {}
echo Class1::A;
echo Class2::B;
Of course, part of the reason for traits is replace complex inheritance trees with composition. Depends on the situation.