Events Manager ============== The purpose of this component is to intercept the execution of most of the components of the framework by creating “hooks point”. These hook points allow the developer to obtain status information, manipulate data or change the flow of execution during the process of a component. Usage Example ------------- In the following example, we use the EventsManager to listen for events produced in a MySQL connection managed by :doc:`Phalcon\\Db <../api/Phalcon_Db>`. First, we need a listener object to do this. We created a class whose methods are the events we want to listen: .. code-block:: php attach('db', $dbListener); $connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" )); //Assign the eventsManager to the db adapter instance $connection->setEventsManager($eventsManager); //Send a SQL command to the database server $connection->query("SELECT * FROM products p WHERE p.status = 1"); In order to log all the SQL statements executed by our application, we need to use the event “afterQuery”. The first parameter passed to the event listener contains contextual information about the event that is running, the second is the connection itself. .. code-block:: php _logger = new \Phalcon\Logger\Adapter\File("../apps/logs/db.log"); } public function afterQuery($event, $connection) { $this->_logger->log($connection->getSQLStatement(), \Phalcon\Logger::INFO); } } As part of this example, we will also implement the Phalcon\\Db\\Profiler to detect the SQL statements that are taking longer to execute than expected: .. code-block:: php _profiler = new \Phalcon\Db\Profiler(); $this->_logger = new \Phalcon\Logger\Adapter\File("../apps/logs/db.log"); } public function beforeQuery($event, $connection) { $this->_profiler->startProfile($connection->getSQLStatement()); } public function afterQuery($event, $connection) { $this->_logger->log($connection->getSQLStatement(), \Phalcon\Logger::INFO); $this->_profiler->stopProfile(); } public function getProfiler() { return $this->_profiler; } } The resulting profile data can be obtained from the listener: .. code-block:: php query("SELECT * FROM products p WHERE p.status = 1"); foreach($dbListener->getProfiler()->getProfiles() as $profile){ echo "SQL Statement: ", $profile->getSQLStatement(), "\n"; echo "Start Time: ", $profile->getInitialTime(), "\n" echo "Final Time: ", $profile->getFinalTime(), "\n"; echo "Total Elapsed Time: ", $profile->getTotalElapsedSeconds(), "\n"; } In a similar manner we can register an lambda function to perform the task instead of a separate listener class (as seen above): .. code-block:: php attach('db', function($event, $connection) { if ($event->getType() == 'afterQuery') { echo $connection->getSQLStatement(); } }); Creating components that trigger Events --------------------------------------- You can create components in your application that trigger events to an EventsManager. As a consequence, there may exist listeners that react to these events when generated. In the following example we're creating a component called "MyComponent". This component is EventsManager aware; when its method "someTask" is executed it triggers two events to any listener in the EventsManager: .. code-block:: php _eventsManager = $eventsManager; } public function getEventsManager() { return $this->_eventsManager } public function someTask() { $this->_eventsManager->fire("my-component:beforeSomeTask", $this); // do some task $this->_eventsManager->fire("my-component:afterSomeTask", $this); } } Note that events produced by this component are prefixed with "my-component". This is a unique word that helps us identify events that are generated from certain component. You can even generate events outside the component with the same name. Now let's create a listener to this component: .. code-block:: php setEventsManager($myComponent); //Attach the listener to the EventsManager $eventsManager->attach('my-component', new SomeListener()); //Execute methods in the component $myComponent->someTask(); As "someTask" is executed, the two methods in the listener will be executed, producing the following output: .. code-block:: php Here, beforeSomeTask Here, afterSomeTask Additional data may also passed when triggering an event using the third parameter of "fire": .. code-block:: php fire("my-component:afterSomeTask", $this, $extraData); In a listener the third parameter also receives this data: .. code-block:: php attach('my-component', function($event, $component, $data) { print_r($data); }); //Receiving the data from the event context $eventManager->attach('my-component', function($event, $component) { print_r($event->getData()); }); If a listener it is only interested in listening a specific type of event you can attach a listener directly: .. code-block:: php attach('my-component:beforeSomeTask', function($event, $component) { //... }); Event Propagation/Cancelation ----------------------------- Many listeners may be added to the same event manager, this means that for the same type of event many listeners can be notified. The listeners are notified in the order they were registered in the EventsManager. Some events are cancelable, indicating that these may be stopped preventing other listeners are notified about the event: .. code-block:: php attach('db', function($event, $connection){ //We stop the event if it is cancelable if ($event->isCancelable()) { //Stop the event, so other listeners will not be notified about this $event->stop(); } //... }); By default events are cancelable, even most of events produced by the framework are cancelables. You can fire a not-cancelable event by passing "false" in the fourth parameter of fire: .. code-block:: php fire("my-component:afterSomeTask", $this, $extraData, false); Listener Priorities ------------------- When attaching listeners you can set a specifical priority. With this feature you can attach listeners indicating the order in which they must be called: .. code-block:: php attach('db', new DbListener(), 150); //More priority $evManager->attach('db', new DbListener(), 100); //Normal priority $evManager->attach('db', new DbListener(), 50); //Less priority Implementing your own EventsManager ----------------------------------- The :doc:`Phalcon\\Events\\ManagerInterface <../api/Phalcon_Events_ManagerInterface>` interface must be implemented to create your own EventsManager replacing the one provided by Phalcon.