我怎样才能有条件地让 class 使用一个特征(如果它存在)?

How can I conditionally make a class use a trait if it exists?

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

显然我无法在 class 本身内部定义它(语法错误)

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

Let's say a few of these controllers (but not all of them) require a database connection. To keep performance up, we shouldn't give every controller the database connection. What we could do is write an abstract class which extends BaseController which provides a database connection. But, in the future, what if an object that is not a controller requires a database connection? Instead of duplicating this logic, we can use horizontal reuse.

A simple trait can be created:

trait DatabaseAware 
{    
    protected $db;

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

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

This trait now provides classes with common database functionality. Any class which requires a database connection, be it a controller or a manager (or anything), can use this trait:

class IndexController extends BaseController 
{
    use DatabaseAware;

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

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

简单!

特质

一个可能可用或不可用的特性,将被使用或不使用,但最终会帮助实现一个接口:

<?php

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

界面

我们要实现的接口:

<?php

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

Class 使用特征

A class FooUsingBarTrait 使用特征 BarTrait 来实现上述接口:

<?php

class FooUsingBarTrait implements BarInterface
{
    use BarTrait;
}

Class 没有使用 trait

A class FooNotUsingBarTrait 不使用特征 BarTrait,而是实现上述接口本身:

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

有条件地创建 class 定义

最后,有条件地定义一个classFoo,取决于一个traitBarTrait是否存在:

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

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

参考:

示例见:

您的问题很有趣,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(),但是当它解决一个用其他方法无法解决的问题时,它很有趣。

这是我最后得到的:

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

完全懒惰:

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

这对我和'as is'来说很好用,除了我粘贴这一行的文件之外,没有对任何文件进行任何修改。它可能不会是适合你的案例。

考虑以下事实:

  • 不要使用结束 php 标签
  • 只有一个 class 文件
  • 您需要添加任何其他关键字(扩展、实施...)
  • 根据您的代码,可能还有更多意想不到的行为

感谢 lacalheinz 的指导post 但史蒂文用 eval() 瞄准了靶心。

无论多糟糕,你都可以通过扩展 class.


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