Search code examples
phptraits

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


I want to be able to use the trait if it's available.

Obviously i cannont define that inside the class itself (syntax Error)

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

Ideal case scenario would be something like this:

class foo
{
    var $bar;
}

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

$f=new foo();

Having hard time finding documentation and traits doesn't seems very popular but in my particular case they are very useful. I also try to keep resource as low a possible by only including files if needed.

Hints, documentation and explanation welcome as usual.


The closest my search brought me was in this article 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`");
    }
}

Where as I implement traits depending on the needs of my different objects. Database connection, debugging reporting, etc.


Solution

  • Your question is fun, and eval() likely meets your needs. This style using code generation is ugly, but I know it works because I verified it myself on my own machine. Here's how you can do it:

    $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
    

    I don't use eval() often, but it's fun when it solves a problem that otherwise cannot be conventionally solved.