【问题标题】:PHP traits - defining generic constantsPHP 特征 - 定义通用常量
【发布时间】:2014-08-13 00:35:31
【问题描述】:

定义可以被命名空间中的多个类使用的常量的最佳方法是什么?我试图避免过多的继承,所以扩展基类不是一个理想的解决方案,我正在努力寻找一个使用特征的好的解决方案。这在 PHP 5.4 中是否有可能,还是应该采取不同的方法?

我有以下情况:

trait Base
{
    // Generic functions
}

class A 
{
    use Base;
}

class B 
{
    use Base;
}

问题是无法在 PHP 特征中定义常量。理想情况下,我想要以下内容:

trait Base
{
    const SOME_CONST = 'someconst';
    const SOME_OTHER_CONST = 'someotherconst';

    // Generic functions
}

然后可以通过应用 trait 的类访问这些:

echo A::SOME_CONST;
echo B::SOME_OTHER_CONST;

但是由于特征的限制,这是不可能的。有什么想法吗?

【问题讨论】:

  • 可以使用静态属性,它不是一个常量,但在调用它时也只有一个字符不同...public static $SOME_VAR = 'someconst';echo B::$SOME_VAR
  • 感谢您的提示。这是一种选择,尽管这是一项重构工作,而不是新代码,所以我更希望行为保持不变而不是更新所有常量调用(有很多)。
  • 你可以使用接口来声明常量。

标签: php oop traits


【解决方案1】:

我最终使用了user sectus's suggestion 的接口,因为这感觉是处理这个问题的最少问题的方式。使用接口来存储常量而不是 API 契约有一种不好的味道,所以这个问题可能更多是关于 OO 设计而不是 trait 实现。

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;
}

这允许:

A::SOME_CONST;
B::SOME_CONST;

【讨论】:

  • 所以接口可以有常量...但特征不能? PHP 变得越来越奇怪了。
  • @Adambean 覆盖一个常量违背了constant 的目的,它并不意味着改变。
  • 然而,如果我想在 trait 的代码实现中使用一个值,这是不可能的。
  • @Aknosis 我个人认为它不仅仅是一个常数在其生命周期内不会改变价值。这并不意味着在扩展类时不能用另一个值重新定义它(并使用 [例如] static::VALIDATION_REGEX 来获取 late-static-bound 值)
  • PHP '错误'#75060
【解决方案2】:

您也可以使用静态变量。它们可以在类或特征本身中使用。 - 作为 const 的替代品,对我来说效果很好。

trait myTrait {
    static $someVarA = "my specific content";
    static $someVarB = "my second specific content";
}

class myCustomClass {
    use myTrait;

    public function hello()
    {
        return self::$someVarA;
    }
}

【讨论】:

  • 我知道这是旧的,但你不想用 const 来改变值吗?
  • 你不能在 Trait 中使用常量。
【解决方案3】:

要限制常量的范围,您可以在命名空间中定义它们:

namespace Test;

const Foo = 123;

// generic functions or classes

echo Foo;
echo namespace\Foo;

这种方法的一个缺点是自动加载对常量不起作用,至少对 5.4 不起作用;解决这个问题的典型方法是将这些常量包装在一个静态类中,即:

namespace Test;

class Bar
{
    const Foo = 123;
}

【讨论】:

  • 有趣的想法。我没有意识到您可以直接访问命名空间常量。问题是这个库是 PSR-0 自动加载的,所以我担心这种方法行不通。不过感谢您的回答。
  • @orciny 好吧,如果将这些常量包装在静态类中,自动加载将起作用;不是最漂亮的代码的最闪亮的例子,但它会工作:)
【解决方案4】:

不是很好,但也许...

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();

【讨论】:

  • 请注意那些没有遵循的人,这些是静态方法,而不是常量。虽然使用时它们看起来有点像常量。如果您要这样做并且希望这些方法的行为更像常量,则需要在方法前加上关键字 final 以确保它们不会被覆盖(与常量相同的行为)。
  • @John Hunt, final 不适用于特质,请参阅 stackoverflow.com/a/33481511/251735
【解决方案5】:

从 PHP 8.1 开始,可以在特征中使用 readonly 属性。

<?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

【讨论】:

    【解决方案6】:

    另外需要考虑的是你是否可以使用抽象类来代替,然后继承。

    abstract class Base
    {
        const A = 1;
        const B = 2;
    }
    
    class Class1 extends Base {}
    class Class2 extends Base {}
    
    echo Class1::A;
    echo Class2::B;
    

    当然,trait 的部分原因是用组合替换复杂的继承树。视情况而定。

    【讨论】:

      猜你喜欢
      • 2017-03-20
      • 1970-01-01
      • 1970-01-01
      • 2013-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-29
      • 2016-08-19
      相关资源
      最近更新 更多