PHP traits - defining generic constants

PhpOopTraits

Php 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTom JowittView Question on Stackoverflow
Solution 1 - PhpTom JowittView Answer on Stackoverflow
Solution 2 - PhpMichaelView Answer on Stackoverflow
Solution 3 - PhpJa͢ckView Answer on Stackoverflow
Solution 4 - PhpJekisView Answer on Stackoverflow
Solution 5 - PhpWeld0sView Answer on Stackoverflow
Solution 6 - PhpOsanView Answer on Stackoverflow