Apps powered by Webasyst framework can support events. An event is a special place in an app’s source code which allows to “plug in” custom code to it without modifying an original app. Custom code can be executed either in a plugin developed for that app or in another app. Whether your app must support events is up to you as its developer.
How it works
Simple example:
- A user opens a website page.
- To generate the page HTML code, an app’s PHP code is executed.
- An app’s PHP code contains an event. It was added by the app developer.
- An event is processed by a plugin. This is because the plugin developer has decided so. As soon as the code execution reaches the event declaration in an app’s code, a plugin attached to it will be executed, too.
The event processing involves 2 types of software products—those containing events and those capable of processing events provided by other products.
Declaring events in an app
To declare an event in an app’s PHP code, add the call of system method wa('app_id')->event('event_id')
to the desired place.
Example
$result = wa('myapp')->event('backend', $params);
In this example:
'myapp'
is the ID of the app to which an event is being added.'backend'
is an event name. You can think of any suitable event name for your app. Try to make it intuitively comprehensible for other developers so that they can easily understand when and where it occurs and how they can use it in their products.$params
is an optionl variable to pass parameters to event handlers. Parameters may be useful for plugin and app developers to handle events more efficiently.$result
is optional result, which may be returned by plugins and other apps for your app to use, if necessary. If your app does not expect any results, do not use them.
Subscribing to events in plugins
To enable the execution of a plugin’s code upon occurrence of an event, the plugin developer must subscribe their plugin to that event. To subscribe means to declare in a special way that certain piece of plugin’s code will be executed when a certain event occurs. To do so, add an item with 'handlers'
key to configuration file plugin.php
.
There are several ways to subscribe a plugin to app events:
- one method to handle one event of its app
- several methods to handle one event of its app
- one method to handle several events of its app
- handle events of other apps
Method 1. One plugin method per event
Example
'handlers' => array( 'backend_menu' => 'backendMenu', ),
This record means that the main plugin class contains a public non-static mathod named backendMenu
, which will be executed upon occurrence of backend_menu
events in the same app for which the plugin was developed.
Method 2. Several plugin methods per event
Example
'handlers' => array( 'backend_menu' => array('backendMenu', 'commonHandler', '...'), ),
This record means that the main plugin class contains public non-static methods named according to method names listed as an array, which will be executed upon occurrence of backend_menu
event in the same app for which the plugin was developed.
Once an event occurs, the specified methods will be executed in the same order as they are listed in the configuration file, until any of them returns any result different from null
. Should any of the specified methods return anything rather than null
, all remaining methods will not be executed.
Method 3. One plugin method per several events: multiple records
Example
'handlers' => array( 'backend_layout' => 'layoutEvent', 'frontend_layout' => 'layoutEvent', ),
This record means that both backend_layout
and frontend_layout
events are handled by the same method in main plugin class, the method in this example is named layoutEvent
.
Method 4. One plugin method per several events: mask
Example
'handlers' => array( 'order_action.*' => 'orderAction', ),
If an app has several events wilth the similar names containing the period character (.), then a developer may subscribe their plugin to all such events at once by replacing the different part in event names with an asterisk (*). In this example an app has events named order_action.create
, order_action.pay
, order_action.edit
, and a plugin can be subscribed to handle all of them in one method of its main class.
You can use the asterisk only for events with the period character in their names. To use a mask for other types of event names, write a regular expression.
Example 1
'handlers' => array( '~(backend_frontend)_layout~' => 'layoutEvent', ),
You can use ~
or /
characters as regular expression delimiters.
Example 2
'handlers' => array( '~[a-z]+_layout~' => 'layoutEvent', ),
This example is similar to the previous one with the exception that a regular expression is more “greedy” this time, because it matches more event names. Not only backend_layout
and frontend_layout
will be handled by a plugin in this case, but also dialog_layout
and email_layout
, for instance, if there are such events in an app.
You need to be careful when subscribing your plugins to multiple events using regular expressions to avoid subscription to unnecessary events. This may otherwise result in increased server load or make an app or plugin behave unexpectedly.
Method 5. Handling other apps’ events
Previous examples show how a plugin can be subscribed to handle events in its own app. There is also an option for a plugin to handle events occurring in other apps. To make it do so, use the extended subscription format — add of a sub-array with '*'
key to 'handlers'
array. Then add another sub-array into it, with the following values:
event_app_id
: ID of app whose event needs to be handled;event
: name of event to be handled;class
: name of PHP class containing the handling method;method
: name of handling method in specified class.
Example 1
'handlers' => array( '*' => array( array( 'event_app_id' => 'otherapp', 'event' => 'event_name', 'class' => 'someappMyPlugin', 'method' => 'eventHandler', ) ), ),
In this example a different app is specified in 'event_app_id'
field. If necessary, you may specify several handling methods as an array or as a regular expression.
Example 2
'handlers' => array( '*' => array( array( 'event_app_id' => '*', 'event' => 'signup', 'class' => 'someappMyPlugin', 'method' => 'signup', ) ), ),
This is a more common example because no specific app is mentioned here whose events a plugin is being subscribed to. Instead of an app ID there is an asterisk *
, it means that a plugin will handle events with the specified name whenever they may occur in any of the installed apps. An example of such an event is signup
. This event is declared in framework’s system classes but those classes are used by different apps, therefore, a plugin must register the event occurrence during the execution of all apps’ PHP code.
Example 3
'handlers' => array( '*' => array( array( 'event_app_id' => '*', 'event' => '~.+~', 'class' => 'someappMyPlugin', 'method' => 'eventHandler', ) ), ),
This is the most common general-purpose example, which is used to subscribe a plugin to all events of all apps. Most probably, you will never need to use it because it will require a lot of server resources. Use this format only in very special cases.
Subscribing apps to other apps’ events
There are two ways of subscribing apps to events:
- creation of a PHP class file a at standard path
- specifying PHP classes’ and their methods’ names in a configuration file
Method 1. Creation of a PHP class file at a stanard path
In this case you do not need to create a configuration file. It is enough to create a handling PHP class. The class file path format is wa-apps/app_id/lib/handlers/<em>event_app_id</em>.<em>event_name</em>.handler.php
. The format of the class name to be declared in that file is app_id<em>Event_app_idEvent_name</em>Handler
. The class must extend system class waEventHandler
. The class’s main method must be named execute()
.
Example
ID of app containing an event: shop.
Event declared in shop
app: backend_order.
ID of app which will handle backend_order
event in shop
app: crm.
Path to event handling class file: wa-apps/crm/lib/handlers/shop.backend_order.handler.php
.
Name of event handling class: crmShopBackend_orderHandler.
Event handling class code example:
class crmShopBackend_orderHandler extends waEventHandler { public function execute(&$params, $event_name = null) { //... } }
Method 2. Specifying PHP classes and methods in a configuration file
Create file wa-apps/app_id/lib/handlers/wildcard.php
. It must contain an array with the information about methods and their classes which will be handling specified events in another app.
Example of wildcard.php
file
return array( array( 'event' => 'event_name_1', 'class' => 'app_idEvent_app_idEvent_name_1Handler', 'method' => array('execute', 'extraMethod'), 'event_app_id' => 'event_app_id_1', 'file' => 'path/to/some/class1.php', ), array( 'event' => 'event_name_2', 'class' => 'app_idEvent_app_idEvent_name_2Handler', 'method' => array('execute', 'extraMethod'), 'event_app_id' => 'event_app_id_2', 'file' => 'path/to/some/class2.php', ), array( ... ), );
In this file:
event_app_id
: ID of an app whose event must be handledevent
: name of event to be handledclass
: name of PHP class containing an event handling methodmethod
: name of an event handling method contained in specified classfile
: path to event handling class, it must be specified only in the cases when a class is not available via framework’s autoloading mechanism.
Try to specify only methods of classes in wildcard.php
which have been created only for the purpose of event handling rather than methods of common app classes. This approach will ensure availability of all those classes and their methods as your app evolves.
Event handling method signature
An event method, both in an app and in a plugin, always receives 2 parameters:
- Event data. This value is passed to
wa()->event('event_name', <strong>$params</strong>)
method as the$params
value. Different values are passed for different events. Some events are passed no data, therefore, their handlers receive anull
value from the first parameter.
Event data, when passed, are always available by reference. Therefore, you can modify them in your handling method so that an app can use further use them, if necessary. - Event name. You may use it, for example, when a handling method is used for several events, to change the common handling logic according to a specific event. This parameter is optional, you may omit it in the method signature if you are not going to use its value.
Example 1
//second parameter is specified to obtain an vent name public function eventHandler(&$params, $event_name = null) { //... }
Example 2
//only one parameter is specified so the event name is not used in the handling method public function eventHandler(&$params) { //... }
In apps it is recommended for event handling PHP classed to extend system class waEventHandler
and override its public method execute()
. This will allow your app to automatically support any further exhancements to the event handling mechanism, which may be added to future framework versions.
Dynamic event handler subscription
When developing or debugging a product, you may find it useful to be able to dynamically link a custom event handler, which will otherwise not be available in a published product. You can do so by calling waEvent::addCustomHandler($handler)
method as shown in the example below. The $handler
parameter must contain a custom handler’s data.
$handler = array( 'object' => new someCustomClass(), 'method' => 'eventHandler', //или array('eventHandler1', 'eventHandler2', ...) 'event' => 'some_event', 'event_app_id' => 'some_app', ); waEvent::addCustomHandler($handler);
If several methods are listed in 'method'
field, then all of them will be executed, regardless of the results which may be returned by any of them, even if they should not be equal to null
. In this way dynamically linked custom event handlers are different from ordinary event handlers declared in apps and plugins.
Event handlers debugging
When exceptions are thrown during the execution of event handler methods, they are suppressed by the framework and their messages are written to log file wa-log/error.log
. Those log records may be useful for your debugging process.
You can log the execution time of each event handler to a file. To do so, get authorized as a backend user and addcookie value event_log_execution
= 1. This will make event handling-related information written to log file wa-log/webasyst/waEventExecutionTime.log
in the following format:
===Start log block=== Recorded: User name array ( event_name => array ( 0 => array ( 'app_id' => 'crm', //app containing an event handler 'plugin_id' => 'yandex', //plugin subscribed to even handling 'regex' => '/backend_header/', //event name as a regular expression 'file' => 'webasyst.backend_header.handler.php', //file name, if available 'class' => 'crmWebasystBackend_headerHandler', //class name 'method' => array ( 0 => 'execute', //handling class methods ), 'execution_time' => 0.0188, //event handler’s execution time in seconds ), ), ) ===End log block==="
See also waEvent
class description.