【问题标题】:How can I conditionally make a class use a trait if it exists?如果存在,我如何有条件地使类使用特征?
【发布时间】:2017-08-16 00:06:40
【问题描述】:

我希望能够use trait如果它可用

显然我不能在类本身内部定义它(语法错误)

//fails
include_once('myTrait.php');

class foo
{ 
    var $bar; 

    if (trait_exists('myTrait')) {
        use myTrait;
    }
}

//also fails
foo use myTrait;

//also fails
$f = new foo();
$f use myTrait;

//also fails
$f = new foo() use myTrait;

理想的情况是这样的:

class foo
{
    var $bar;
}

if (file_exists('myTrait.php')) {
   include_once('myTrait.php');
   //make class foo use myTrait;
}

$f=new foo();

很难找到文档和特征似乎不是很受欢迎,但在我的特殊情况下它们非常有用。我还尝试通过仅在需要时包含文件来尽可能减少资源。

像往常一样欢迎提示、文档和解释。


我的搜索带给我的最接近的是这篇文章http://brendan-bates.com/traits-the-right-way/

假设其中一些控制器(但不是全部)需要 数据库连接。为了保持性能,我们不应该付出所有 控制数据库连接。我们能做的就是写一个 扩展提供数据库的 BaseController 的抽象类 联系。但是,在未来,如果一个对象不是 控制器需要数据库连接?而不是复制这个 逻辑上,我们可以使用横向复用。

可以创建一个简单的特征:

trait DatabaseAware 
{    
    protected $db;

    public function setDatabase($db) 
    {
        $this->db = $db;
    }

    protected function query($query) 
    {
        $this->db->query($query);
    }
}

这个特性现在为类提供了通用的数据库功能。 任何需要数据库连接的类,无论是控制器还是 经理(或任何人)可以使用此特征:

class IndexController extends BaseController 
{
    use DatabaseAware;

    public function indexAction() 
    {
        $this->query("SELECT * FROM `someTable`");
    }
}

我根据不同对象的需要来实现特征。数据库连接、调试报告等

【问题讨论】:

  • 你可以根据特征的存在定义一个完整的类(在if(trait_exists('myTrait'))内)
  • A trait 用新方法“增强”了一个类。如果您不知道何时编写代码,如果类提供了方法,您打算如何使用这些方法?在我看来,您试图以错误的方式解决一个完全不同的问题。
  • “我还尝试通过仅在需要时包含文件来保持资源尽可能低。” -- 这是自动加载器的责任。
  • @axiac 这可以通过实现一个接口来解决 - 或者 trait 提供实现,如果没有,则以其他方式实现。
  • 特征不是动态特征。它要么存在,要么不存在。如果它不存在,那么use 的类都不存在;它们无法加载和使用。如果特征存在,那么整个问题就没有任何意义。在你需要的地方使用它,不要费心检查它的存在。如果您需要的功能可能可用也可能不可用(因为外部原因,而不是因为代码不存在),那么该解决方案使用其他技术:DIStrategy 设计模式。

标签: php traits


【解决方案1】:

简单!

性状

一个可能可用或不可用的特征,将被使用或不被使用,但最终将有助于实现接口:

<?php

trait BarTrait
{
    public function bar()
    {
        return 'Hey, I am your friend!';
    }
}

界面

我们希望实现的接口:

<?php

interface BarInterface
{
    /**
     * @return string
     */
    public function bar();
}

使用特征类

一个类FooUsingBarTrait,它使用一个特征BarTrait来实现上述接口:

<?php

class FooUsingBarTrait implements BarInterface
{
    use BarTrait;
}

类不使用特征

一个类FooNotUsingBarTrait,它不使用特征BarTrait,而是实现了上述接口本身:

class FooNotUsingBarTrait implements BarInterface
{
    public function bar()
    {
        return 'Hey, I am one of your friends!';
    }
}

有条件地创建类定义

最后,有条件地定义一个类Foo,这取决于一个特征BarTrait是否存在:

<?php

if (trait_exists(BarTrait::class) {
    class Foo extends FooUsingBarTrait
    {
    }
} else {
    class Foo extends FooNotUsingBarTrait
    {
    }
}

创建您的实例

$foo = new Foo();

$foo->bar();

var_dump(
    get_class($foo),
    class_parents(Foo::class)
);

注意如果FooUsingBarTraitFooNotUsingBarTrait 两个类都实现了一个公共接口,这可能最有意义 - 毕竟,您可能希望提供一些将在两个实现之间共享的功能:一个使用特征,另一个通过其他方式(该类提供的方法)。

参考见:

例如,请参阅:

【讨论】:

  • 很多文档都在讨论与特征一起使用的接口。我将在界面的文档中进行更多挖掘。你的大部分答案都是我已经知道但从另一个角度来看的东西。
  • 呵呵,很高兴为您服务-希望解决方案对您有用,@LouisLoudogTrottier!
【解决方案2】:

您的问题很有趣,并且 eval() 可能满足您的需求。这种使用代码生成的风格很难看,但我知道它有效,因为我自己在自己的机器上验证了它。你可以这样做:

$src = '
class foo {
  var $bar; // and all of your other code goes here
';
if (file_exists('myTrait.php')) {
  include_once('myTrait.php');
  $src .= "use myTrait;\n";
}
$src .= "}";
eval ($src); // your class finally gets declared

我不经常使用 eval(),但是当它解决一个传统上无法解决的问题时,它会很有趣。

【讨论】:

  • 我认为 $src 可以是任何解释为字符串的东西? ob_start(); include 'src/class.php'; $src=ob_get_contents(); ...我的问题可能很有趣,但我会更有趣地尝试这个并调整它以适应我的懒惰。没什么大不了的,但肯定会尝试一下,因为它似乎是我正在寻找的最接近的解决方案。
  • 谢谢你,有点摆弄,我能够把它混合成一个班轮。
  • 另外,现在真正的乐趣开始了,知道如何利用它以及何时不使用它。
  • 我很高兴它对你有用!看起来你改进了我建议的答案。根据你的 cmets,我很确定我知道你做了什么,我认为这很漂亮!
【解决方案3】:

不管有多糟糕,你都可以通过扩展类来做到这一点。


trait AutoPilot {
   function navigate() {
       echo 'navigating...';
   }
}

if (trait_exists('AutoPilot')) {
   class Machine {
       use AutoPilot;
   }
} else {
   class Machine {
   }
}

class Car extends Machine {
}


$car = new Car;
$car->navigate();

【讨论】:

    【解决方案4】:

    这是我最终得到的结果:

    eval("class myClass {" 
        . (trait_exists('myTrait') ? "use myTrait;" : "") 
        . str_replace(['class ', '<?php'], '//', file_get_contents(myClass.php"))
    );
    

    总懒惰:

    • 复制trait_exists 行以添加更多特征
    • 注释掉class 关键字和&lt;?php 标记,这样您就不必编辑类文件
    • 评估一长行臭代码。

    这很好对我来说并且“按原样”工作,除了我将这一行粘贴到的文件之外,无需对任何文件进行任何修改。你可能不会这样。

    考虑以下事实:

    • 不要使用关闭 php 标记
    • 文件只有一个类
    • 您需要添加任何其他关键字(扩展、实现、...)
    • 可能还有更多意想不到的行为,具体取决于您的代码

    感谢 lacalheinz 的指导性帖子,但 Steven 用 eval() 瞄准了目标。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-16
      • 2015-11-23
      • 2012-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多