1
\$\begingroup\$

I've been rewriting parts of my own framework and eventually came back to where it all began, making traits more useful and implementing auto initializing traits and I am wondering if someone has a bright idea on how to implement it a bit cleaner.

The idea of this is simple, let traits have constructors that are called before the class __construct().

namespace myframework\traits{
  trait Construct{
    function __wakeup(){
      foreach(array_remove(__TRAIT__, ref(class_uses(self::class, false))) as $trait){
        $list[($v = \myframework::loader()->trait($trait))[0]][] = $v[1];
        # generates: $list[$priority][] = $callback
      }

      if(ksort($list)){
        foreach($list as $cb){
          foreach($cb as $fn){
            $fn->call($this);
          }
        }
      } else {
        # Something horrible happend, oh well can/does it ever fail?
      }
    }
  }

  trait DB{
    private $db;
  }

  # Register anon function to fire when trait gets loaded. Constructor.
  \myframework::loader()->trait('myframework\traits\DB', function(){
    $this->db = myframework\Database::getInstance();
  }, 0);

  # This function essentially makes:
  # $data['myframework\traits\DB'] = [0 => 0, 1 => callback]
  # where key index 0 is the load priority, which gets sorted.
}

I tried to trim the code as much as possible for simplicity, the array_remove() is a function returning the an updated array by removing a key from a generated array and ref() is just a wrapper function to allow array_remove() to be inlined.

Now lets assume the above code is autoloaded, making a new file and doing the following is now possible:

class someClass{
  use myframework\traits\Construct;
  use myframework\traits\DB;

  public function __construct(){
    // DRY concept, $this->db is already set.

    $stmt = $this->db->prepare("SELECT * FROM table");
  }
}

Now this is just a minor example, imagine if you add a trait Log adding a method log($msg) that will automatically log the message under the class name and with the auto initializer the object itself can determine where to log, for example the filesystem if the database failed to connect.

So my question mainly is, if there is a cleaner way to implement the anonymous function because it is currently registered by name instead of reference. Also since this is the core of the framework it needs to be efficient as possible because __wakeup() can be called for each object created using this system.

Also I'd like to have some input on what I could improve upon it.

\$\endgroup\$
0
\$\begingroup\$

I've been working on it and I feel I finally have it like I wanted to.

Instead of using the __wakeup() function, I'm binding a closure to the new object that dispatches other closures whom are registered to a traits name. So as a result I don't even need the Constructor trait anymore.

Not only that, hooking this all up to an event system I was able to create destructors as well with an added bonus that I can use __wakeup() and __sleep() as they where designed for.

I'm reusing code by using the event system, so in summary its now more dynamic, shorter code and somewhat faster in execution

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.