Added classes that implements plugin system and tests for them

This commit is contained in:
Dmitriy Simushev 2012-07-17 16:54:58 +00:00
parent 3e121c675f
commit 66a0d6ddca
8 changed files with 524 additions and 0 deletions

View File

@ -0,0 +1,157 @@
<?php
require_once dirname(__FILE__) . '/../../../../webim/libs/classes/event_dispatcher.php';
require_once dirname(__FILE__) . '/../../../../webim/libs/classes/plugin.php';
require_once dirname(__FILE__) . '/phpunit_autotest_plugin_manager/plugin.mibew.inc.php';
/**
* Test class for EventDispatcher.
* Generated by PHPUnit on 2012-07-17 at 16:09:00.
*/
class EventDispatcherTest extends PHPUnit_Framework_TestCase {
protected static $plugin = null;
public static function setUpBeforeClass() {
self::$plugin = new Phpunit_autotest_plugin_managerPlugin();
}
public static function tearDownAfterClass() {
self::$plugin = null;
}
public function testGetInstance() {
$dispatcher = EventDispatcher::getInstance();
$another_dispatcher = EventDispatcher::getInstance();
$this->assertSame($dispatcher, $another_dispatcher);
unset($another_dispatcher);
return $dispatcher;
}
/**
* @depends testGetInstance
*/
public function testRegisterEvent($dispatcher) {
// Try to register new event
$this->assertTrue($dispatcher->registerEvent('some_test_event'));
$this->assertTrue($dispatcher->registerEvent('some_another_test_event'));
// Try to register already registered event
// Following code wait for trigger user error, which converts by PHPUnit to an
// Exception
try{
$dispatcher->registerEvent('some_test_event');
$this->fail("Error expected!");
} catch(Exception $e) {}
return $dispatcher;
}
/**
* @depends testRegisterEvent
*/
public function testAttachListener($dispatcher) {
// Try to attach listener to unregistered event
// Following code wait for trigger user error, which converts by PHPUnit to an
// Exception
try{
$dispatcher->attachListener(
'unreginstered_event',
self::$plugin,
'testEventListener'
);
$this->fail("Error expected!");
} catch(Exception $e) {}
// Try to Attach wrong method as listener to event
// Following code wait for trigger user error, which converts by PHPUnit to an
// Exception
try{
$dispatcher->attachListener(
'some_test_event',
self::$plugin,
'wrongEventListener'
);
$this->fail("Error expected!");
} catch(Exception $e) {}
// Try to attach listener to registered event
$this->assertTrue(
$dispatcher->attachListener(
'some_test_event',
self::$plugin,
'testEventListener'
)
);
// Try to attach listener to registered event
$this->assertTrue(
$dispatcher->attachListener(
'some_another_test_event',
self::$plugin,
'testEventListener'
)
);
return $dispatcher;
}
/**
* @depends testAttachListener
*/
public function testDetachListener($dispatcher) {
// Try to detach listener for unregistered event.
// Following code wait for trigger user error, which converts by PHPUnit to an
// Exception
try{
$dispatcher->detachListener(
'unreginstered_event',
self::$plugin,
'testEventListener'
);
$this->fail("Error expected!");
} catch(Exception $e) {}
// Try to detach listner that was not attached to registerd event
$this->assertFalse(
$dispatcher->detachListener(
'some_test_event',
self::$plugin,
'wrongEventListener'
)
);
// Try to detach listener that was attached to registered
$this->assertTrue(
$dispatcher->detachListener(
'some_test_event',
self::$plugin,
'testEventListener'
)
);
return $dispatcher;
}
/**
* @depends testDetachListener
*/
public function testTriggerEvent($dispatcher) {
// Try to trigger unregistered event
// Following code wait for trigger user error, which converts by PHPUnit to an
// Exception
try{
$dispatcher->triggerEvent('unregistered_event', array());
$this->fail("Error expected!");
} catch(Exception $e) {}
// Try to thrigger registered event
// Wait for exception thrown by
// Phpunit_autotest_plugin_managerPlugin::testEventListener()
try{
$dispatcher->triggerEvent('some_another_test_event', array());
$this->fail("Exception excpected!");
} catch(Exception $e) {}
}
}
?>

View File

@ -0,0 +1,28 @@
<?php
require_once dirname(__FILE__) . '/../../../../webim/libs/classes/plugin_manager.php';
require_once dirname(__FILE__) . '/../../../../webim/libs/classes/plugin.php';
/**
* Test class for PluginManager.
* Generated by PHPUnit on 2012-07-17 at 16:09:18.
*/
class PluginManagerTest extends PHPUnit_Framework_TestCase {
public function testLoadPlugins() {
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__));
PluginManager::loadPlugins(
array(
array(
'name' => 'phpunit_autotest_plugin_manager'
)
)
);
if(empty($GLOBALS['phpunit_autotest_plugin_manager'])) {
$this->fail('Plugin not loaded and initialize correctly');
}
}
}
?>

View File

@ -0,0 +1,37 @@
<?php
/**
* Test plugin for PHPUnit tests
*/
Class Phpunit_autotest_plugin_managerPlugin implements Plugin{
public $eventsRegistered = false;
public $listenersRegistered = false;
public function getWeight() {
return 10;
}
public function registerEvents() {
$this->eventsRegistered = true;
$this->checkRegistration();
}
public function registerListeners() {
$this->listenersRegistered = true;
$this->checkRegistration();
}
public function checkRegistration() {
if ($this->eventsRegistered && $this->listenersRegistered) {
$GLOBALS['phpunit_autotest_plugin_manager'] = true;
}
}
public function testEventListener($vars) {
throw new Exception();
}
}
?>

View File

@ -0,0 +1 @@
Deny from all

View File

@ -0,0 +1,163 @@
<?php
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Provide event-related functionality.
* Implements singleton pattern.
*/
Class EventDispatcher {
/**
* An instance of EventDispatcher class.
* @var EventDispatcher
*/
protected static $instance = null;
/**
* Events and listeners array.
* @var array
*/
protected $events = array();
/**
* Increments any time when plugin adds. Use for determine plugins order for plugins with
* equal priority.
* @var int
*/
protected $offset = 0;
/**
* Returns an instance of EventDispatcher class.
*
* @return EventDispatcher
*/
public static function getInstance(){
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Make constructor unavailable for client code
*/
protected function __constructor() {}
/**
* Attaches listener function to event.
*
* @param string $event_name Event's name
* @param Plugin $plugin Plugin object, that handles the event
* @param string $listener Plugins method, that handles the event
* @param int $priority Priority of listener. If $priority = null, the plugin weight will
* use instead.
* @return boolean true on success or false on failure.
*
* @see Plugin::getWeight()
*/
public function attachListener($event_name, Plugin $plugin, $listener, $priority = null){
// Check event exists
if (! array_key_exists($event_name, $this->events)) {
trigger_error("Event '{$event_name}' does not exists!", E_USER_WARNING);
return false;
}
// Check method is callable
if (! is_callable(array($plugin, $listener))) {
trigger_error("Method '{$listener}' is not callable!", E_USER_WARNING);
return false;
}
// Check priority
if (is_null($priority)) {
$priority = $plugin->getWeight();
}
// Attach listener
$this->events[$event_name][$priority . "_" . $this->offset] = array(
'plugin' => $plugin,
'listener' => $listener
);
$this->offset++;
return true;
}
/**
* Detach listener function from event
*
* @param string $event_name Event's name
* @param Plugin $plugin Plugin object, that handles the event
* @param string $listener Plugins method, that handles the event
* @return boolean true on success or false on failure.
*/
public function detachListener($event_name, Plugin $plugin, $listener){
// Check event exists
if (! array_key_exists($event_name, $this->events)) {
trigger_error("Event '{$event_name}' does not exists!", E_USER_WARNING);
return false;
}
// Search event and $plugin->$listener
foreach ($this->events[$event_name] as $index => $event) {
if ($event['plugin'] === $plugin && $event['listener'] == $listener) {
// Detach listener
unset($this->events[$event_name][$index]);
return true;
}
}
return false;
}
/**
* Registers new event
*
* @param string $event_name Event's name
* @return boolean true on success or false on failure
*/
public function registerEvent($event_name){
// Check event exists
if (array_key_exists($event_name, $this->events)) {
trigger_error("Event '{$event_name}' already exists!", E_USER_WARNING);
return false;
}
// register event
$this->events[$event_name] = array();
return true;
}
/**
* Triggers the event
*
* @param string $event_name Event's name
* @param array $arguments Arguments passed to listener
* @return boolean true on success or false on failure
*/
public function triggerEvent($event_name, $arguments = array()){
// Check event exists
if (! array_key_exists($event_name, $this->events)) {
trigger_error("Event '{$event_name}' does not exists!", E_USER_WARNING);
return false;
}
// Sorting listeners by priority
uksort($this->events[$event_name], 'strnatcmp');
// Invoke listeners
foreach ($this->events[$event_name] as $event) {
$plugin = $event['plugin'];
$listener = $event['listener'];
$plugin->$listener($arguments);
}
}
}
?>

View File

@ -0,0 +1,43 @@
<?php
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Base plugin interface
*/
interface Plugin {
/**
* Returns plugin weight. Weight is used for determine loading order and as default
* listner priority.
*
* @return int Plugin weight
*/
public function getWeight();
/**
* Register events
*/
public function registerEvents();
/**
* Register listeners
*/
public function registerListeners();
}
?>

View File

@ -0,0 +1,94 @@
<?php
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Manage plugins
*/
Class PluginManager {
/**
* Loads plugins and invokes Plugin::registerEvents() and Plugin::registerListeners()
*
* @param array $plugins_list List of plugins' names and configurations. For example:
* <code>
* $plugins_list = array();
* $plugins_list[] = array(
* 'name' => 'plugin_name', // Obligatory value
* 'config' => array( // Pass to plugin constructor
* 'weight' => 100,
* 'some_configurable_value' => 'value'
* )
* )
* </code>
*
* @see Plugin::registerEvents()
* @see Plugin::registerListeners()
*/
public static function loadPlugins($plugins_list){
// Add include path
$include_path = get_include_path();
$include_path .= empty($include_path) ? '' : PATH_SEPARATOR ;
set_include_path($include_path . realpath(dirname(__FILE__) . "/../../plugins/"));
// Load plugins
$loading_queue = array();
$offset = 0;
foreach ($plugins_list as $plugin) {
if (empty($plugin['name'])) {
trigger_error("Plugin name undefined!", E_USER_WARNING);
continue;
}
$plugin_name = $plugin['name'];
$plugin_config = isset($plugin['config']) ? $plugin['config'] : array();
$plugin_classname = ucfirst($plugin_name) . "Plugin";
// Try to load plugin file
if (! (include_once $plugin_name."/plugin.mibew.inc.php")) {
trigger_error("Cannot load plugin file!", E_USER_ERROR);
}
// Check plugin class name
if (! class_exists($plugin_classname)) {
trigger_error(
"Plugin class '{$plugin_classname}' does not defined!",
E_USER_WARNING
);
continue;
}
// Check if plugin implements 'Plugin' interface
if (! in_array('Plugin', class_implements($plugin_classname))) {
trigger_error(
"Plugin class '{$plugin_classname}' does not implement " .
"'Plugin' interface!",
E_USER_WARNING
);
continue;
}
// Add plugin to loading queue
$plugin_instance = new $plugin_classname($plugin_config);
$loading_queue[$plugin_instance->getWeight() . "_" . $offset] = $plugin_instance;
$offset++;
}
// Sort queue in order to plugins' weights
uksort($loading_queue, 'strnatcmp');
// Add events and listeners
foreach ($loading_queue as $plugin) {
$plugin->registerEvents();
$plugin->registerListeners();
}
}
}
?>

View File

@ -0,0 +1 @@
Deny from all