我如何有条件地让一个类使用特征?如果它存在?

时间:2017-08-16 00:06:40

标签: php traits

我希望能够use特性如果可用

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

//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`");
    }
}

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

3 个答案:

答案 0 :(得分:4)

容易!

性状

可能使用与否的特性将被使用或不使用,但最终将有助于实现接口:

<?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都实现了一个公共接口,这可能是最有意义的 - 毕竟,您可能希望提供一些将在这两个实现:一个使用特征,另一个使用其他方法(该类提供的方法)。

供参考,见:

例如,请参阅:

答案 1 :(得分:0)

您的问题很有趣,而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(),但它解决了传统上无法解决的问题时很有趣。

答案 2 :(得分:-1)

这里我最终得到了:

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

总懒惰:

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

这对我来说很好 和'原样'没有对任何文件进行任何修改,除了我粘贴这一行的文件。你可能不会这样。

考虑以下事实:

  • 不要使用关闭php标记
  • 只有一个按档案分类
  • 您需要添加任何其他关键字(扩展,工具,...)
  • 可能会出现更多意外行为,具体取决于您的代码

感谢lacalheinz的指导性帖子,但史蒂文瞄准了带有eval()的窃贼。