← Home

Let your classes live their own life

First, we'll set up a simple event generating class. EventGenerator will be able to register listeners and call a method on them when an event gets raised.

(For the sake of simplicity, we won't rely here on a separate Event class that's usually used to pack information about the event's type, where the event got fired, and what exactly happened.)

class EventGenerator {
   private $listeners = array();
   public function attachListener($listener) {
      echo "attaching: " . get_class($listener) . "<br>";
      $this->listeners[] = $listener;
   }
   protected function raiseEvent($method, $args) {
      foreach ($this->listeners as $listener) {
         if (method_exists($listener, $method)) {
            call_user_func_array(
               array($listener, $method), $args);
         } else {
            $msg = "method does not exist: $method";
            throw new Exception($msg);
         }
      }
   }
}

Now, we'll extend this class to the Lifecycle class that's responsible to cycle through some predefined stages. To keep it simple, we'll choose the stages 'init', 'execute', 'exit'. In dot.net, for example, there are much more stages like '...'.

class Lifecycle extends EventGenerator {
   private $stages = array('init', 'execute', 'exit');
   public function run () {
      $method = 'onStageEnter';
      foreach ($this->stages as $stage) {
         echo "We're ready to enter stage: $stage<br>";
         $this->raiseEvent($method, array($stage));
      }
   }
}

So, when the Lifecycle's run() method gets called, the Lifecycle will raise StageEnter events, each with the current stage as a parameter.

Next, we need a class that we can register as a listener to the EventGenerator class. To be able to respond to the StageEnter events of the Lifecycle, we'll implement a single method onStageEnter() that relays to (not yet implemented) methods onInit(), onExecute() and onExit().

class LifecycleListener {
   public function onStageEnter($stage) {
      echo "entered $stage in " . get_class($this) . "<br>";
      $method = 'on' . ucFirst ($stage);
      if (method_exists($this, $method)) {
         $this->$method();
      }
   }
}

We can now extend the LifecycleListener to some custom class that we want to respond to the Lifecycle. Therefore, we'll refer to the Component example from our last article (A php way to simple class aggregation).

class Component extends LifecycleListener {
   protected function onInit() {
      echo
         "We've arrived on stage: <b>init</b>.<br>" .
         "So, let's intialize the party.<br>";
   }
   protected function onExecute() {
      echo
         "We've arrived on stage: <b>execute</b>.<br>" .
         "Now, let's perform our play.<br>";
   }
   protected function onExit() {
      echo
         "We've arrived on stage: <b>exit</b>.<br>" .
         "Finally, let's clean up the theater.<br>";
   }
}

That's self-explaining, isn't it? In onInit(), onExecute() and onExit() we would implement our real-life business stuff just as needed for a concrete Component.

Now, we can go for a test:

$lifecycle = new Lifecycle();
$lifecycle->attachListener(new Component());
$lifecycle->run();

This will result in the following output:

attaching: Component
We're ready to enter the stage: init
We've arrived on stage: init.
So, let's intialize the party.
We're ready to enter the stage: execute
We've arrived on stage: execute.
Now, let's perform our play.
We're ready to enter the stage: exit
We've arrived on stage: exit.
Finally, let's clean up the theater.