0009810: Plugins architecture
authorFlávio Gomes da Silva Lisboa <flavio.lisboa@serpro.gov.br>
Wed, 26 Mar 2014 16:24:13 +0000 (13:24 -0300)
committerPhilipp Schüle <p.schuele@metaways.de>
Tue, 8 Apr 2014 09:56:58 +0000 (11:56 +0200)
... for allowing dependency injection into frontend, controller and backend layers

https://forge.tine20.org/mantisbt/view.php?id=9810

Change-Id: I8a8646b852a805c2cf3a9789f3febd311fa02437
Reviewed-on: https://gerrit.tine20.org/tine20/2817
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Philipp Schüle <p.schuele@metaways.de>
12 files changed:
tests/tine20/Tinebase/AllTests.php
tests/tine20/Tinebase/Pluggable/ConcreteTest.php [new file with mode: 0644]
tests/tine20/Tinebase/Pluggable/DummyBackend.php [new file with mode: 0644]
tests/tine20/Tinebase/Pluggable/DummyController.php [new file with mode: 0644]
tests/tine20/Tinebase/Pluggable/DummyFrontend.php [new file with mode: 0644]
tests/tine20/Tinebase/Pluggable/Plugin/DummyPlugin.php [new file with mode: 0644]
tine20/Tinebase/Backend/Abstract.php
tine20/Tinebase/Controller/Abstract.php
tine20/Tinebase/Frontend/Abstract.php
tine20/Tinebase/Pluggable/Abstract.php [new file with mode: 0644]
tine20/bootstrap.php
tine20/init_plugins.php [new file with mode: 0644]

index abcd45a..6649891 100644 (file)
@@ -63,7 +63,8 @@ class Tinebase_AllTests
         $suite->addTestSuite('Tinebase_TagsTest');
         $suite->addTestSuite('Tinebase_Log_AllTests');
         $suite->addTestSuite('Tinebase_Redis_QueueTest');
-        $suite->addTestSuite('Tinebase_TempFileTest');
+        $suite->addTestSuite('Tinebase_TempFileTest');  
+        $suite->addTestSuite('Tinebase_Pluggable_ConcreteTest');             
         
         $suite->addTest(Tinebase_User_AllTests::suite());
         $suite->addTest(Tinebase_Group_AllTests::suite());
@@ -73,7 +74,7 @@ class Tinebase_AllTests
         $suite->addTest(Tinebase_Tree_AllTests::suite());
         $suite->addTest(Tinebase_Scheduler_AllTests::suite());
         $suite->addTest(Tinebase_WebDav_AllTests::suite());
-        $suite->addTest(OpenDocument_AllTests::suite());
+        $suite->addTest(OpenDocument_AllTests::suite());        
         return $suite;
     }
 }
diff --git a/tests/tine20/Tinebase/Pluggable/ConcreteTest.php b/tests/tine20/Tinebase/Pluggable/ConcreteTest.php
new file mode 100644 (file)
index 0000000..a0f4b9a
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tinebase
+ * @subpackage  Pluggable
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014 Serpro (http://www.serpro.gov.br)
+ * @author      Flávio Gomes da Silva Lisboa <flavio.lisboa@serpro.gov.br>
+ * 
+ */
+
+/**
+ * Test helper
+ */
+require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
+
+/**
+ * Test class for Tinebase_Pluggable
+ */
+class Tinebase_Pluggable_ConcreteTest extends PHPUnit_Framework_TestCase
+{
+    protected $frontend = NULL;
+    protected $controller = NULL;
+    protected $backend = NULL;        
+    
+    /**
+     * Sets up the fixture, for example
+     * This method is called before a test is executed.
+     *
+     * @access protected
+     */
+    protected function setUp()
+    {
+        // creates layer instances
+        $this->frontend = new Tinebase_Pluggable_DummyFrontend();
+        $this->controller = new Tinebase_Pluggable_DummyController();
+        $this->backend = new Tinebase_Pluggable_DummyBackend();
+        
+        // injects plugin into layers
+        Tinebase_Frontend_Abstract::attachPlugin('dummyPluginMethod', 'Tinebase_Pluggable_Plugin_DummyPlugin');
+        Tinebase_Controller_Abstract::attachPlugin('dummyPluginMethod', 'Tinebase_Pluggable_Plugin_DummyPlugin');
+        Tinebase_Backend_Abstract::attachPlugin('dummyPluginMethod', 'Tinebase_Pluggable_Plugin_DummyPlugin');
+    }
+    
+    /**
+     * Verifies if plugin is callable from layers
+     */
+    protected function testCallPluginMethod()
+    {
+        $expected = 'dummyPluginReturn';
+        
+        $frontendReturn = $this->frontend->dummyPluginMethod();
+        $controllerReturn = $this->controller->dummyPluginMethod();
+        $backendReturn = $this->backend->dummyPluginMethod();
+        
+        $this->assertEquals($expected, $frontendReturn);
+        $this->assertEquals($expected, $controllerReturn);
+        $this->assertEquals($expected, $backendReturn);        
+    }
+
+    /**
+     * Tears down the fixture
+     * This method is called after a test is executed.
+     *
+     * @access protected
+     */
+    protected function tearDown()
+    {
+    
+    }
+}
\ No newline at end of file
diff --git a/tests/tine20/Tinebase/Pluggable/DummyBackend.php b/tests/tine20/Tinebase/Pluggable/DummyBackend.php
new file mode 100644 (file)
index 0000000..b06cb31
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tinebase
+ * @subpackage  Pluggable
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014 Serpro (http://www.serpro.gov.br)
+ * @author      Flávio Gomes da Silva Lisboa <flavio.lisboa@serpro.gov.br>
+ * 
+ */
+/**
+ * Mock for Backend
+ */
+class Tinebase_Pluggable_DummyBackend extends Tinebase_Backend_Abstract
+{
+}
\ No newline at end of file
diff --git a/tests/tine20/Tinebase/Pluggable/DummyController.php b/tests/tine20/Tinebase/Pluggable/DummyController.php
new file mode 100644 (file)
index 0000000..43cf706
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tinebase
+ * @subpackage  Pluggable
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014 Serpro (http://www.serpro.gov.br)
+ * @author      Flávio Gomes da Silva Lisboa <flavio.lisboa@serpro.gov.br>
+ * 
+ */
+/**
+ * Mock for Controller
+  */
+class Tinebase_Pluggable_DummyController extends Tinebase_Controller_Abstract
+{
+}
\ No newline at end of file
diff --git a/tests/tine20/Tinebase/Pluggable/DummyFrontend.php b/tests/tine20/Tinebase/Pluggable/DummyFrontend.php
new file mode 100644 (file)
index 0000000..b9b0980
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tinebase
+ * @subpackage  Pluggable
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014 Serpro (http://www.serpro.gov.br)
+ * @author      Flávio Gomes da Silva Lisboa <flavio.lisboa@serpro.gov.br>
+ * 
+ */
+/**
+ * Mock for Frontend
+ */
+class Tinebase_Pluggable_DummyFrontend extends Tinebase_Frontend_Abstract
+{
+}
\ No newline at end of file
diff --git a/tests/tine20/Tinebase/Pluggable/Plugin/DummyPlugin.php b/tests/tine20/Tinebase/Pluggable/Plugin/DummyPlugin.php
new file mode 100644 (file)
index 0000000..cdede0d
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tinebase
+ * @subpackage  Pluggable
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014 Serpro (http://www.serpro.gov.br)
+ * @author      Flávio Gomes da Silva Lisboa <flavio.lisboa@serpro.gov.br>
+ * 
+ */
+/**
+ * Mock for Plugin
+ */
+class Tinebase_Pluggable_Plugin_DummyPlugin
+{
+    public function dummyPluginMethod()
+    {
+        return 'dummyPluginReturn';
+    }
+}
index bec1189..c7a162f 100644 (file)
@@ -16,7 +16,7 @@
  * @package     Tinebase
  * @subpackage  Backend
  */
-abstract class Tinebase_Backend_Abstract implements Tinebase_Backend_Interface
+abstract class Tinebase_Backend_Abstract extends Tinebase_Pluggable_Abstract implements Tinebase_Backend_Interface
 {
     /**
      * backend type constant
index f4095f2..5dbebca 100755 (executable)
@@ -16,7 +16,7 @@
  * @package     Tinebase
  * @subpackage  Controller
  */
-abstract class Tinebase_Controller_Abstract implements Tinebase_Controller_Interface
+abstract class Tinebase_Controller_Abstract extends Tinebase_Pluggable_Abstract implements Tinebase_Controller_Interface
 {
     /**
      * default settings
index 89626db..4358c58 100644 (file)
@@ -15,7 +15,7 @@
  * @package     Tinebase
  * @subpackage  Application
  */
-abstract class Tinebase_Frontend_Abstract implements Tinebase_Frontend_Interface
+abstract class Tinebase_Frontend_Abstract extends Tinebase_Pluggable_Abstract implements Tinebase_Frontend_Interface
 {
     /**
      * Application name
diff --git a/tine20/Tinebase/Pluggable/Abstract.php b/tine20/Tinebase/Pluggable/Abstract.php
new file mode 100644 (file)
index 0000000..74941d2
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Tinebase
+ * @subpackage  Pluggable
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014 Serpro (http://www.serpro.gov.br)
+ * @author      Flávio Gomes da Silva Lisboa <flavio.lisboa@serpro.gov.br>
+ *
+ */
+
+/**
+ * Abstract class for allowing dependency injection through a plugin
+ * architecture
+ * Use the file init_plugins.php for registering plugins for a class
+ *
+ * @package Tinebase
+ * @subpackage Pluggable
+ */
+abstract class Tinebase_Pluggable_Abstract
+{
+
+    /**
+     * Plugins for this class family
+     * Contains:
+     * '[method]' => '[Complete_Name_Of_Class]'
+     * 
+     * @var array
+     */
+    protected static $_plugins = array();
+
+    /**
+     * Attaches a plugin
+     * Path for classes must be into include_path
+     * 
+     * @param string $method            
+     * @param string $namespace            
+     */
+    public static function attachPlugin ($method, $namespace)
+    {
+        self::$_plugins[$method] = $namespace;
+        Zend_Loader_Autoloader::getInstance()->registerNamespace(
+                current(explode('_', $namespace)));
+    }
+
+    /**
+     * Attaches several plugins from a same path
+     * $methods must contain:
+     * '[method]' => '[Complete_Name_Of_Class]'
+     *
+     * @param array $methods            
+     * @param string $namespace            
+     */
+    public static function attachPlugins (array $methods, $namespace)
+    {
+        foreach ($methods as $method => $class) {
+            self::$_plugins[$method] = $class;
+        }
+        Zend_Loader_Autoloader::getInstance()->registerNamespace(
+                current(explode('_', $namespace)));
+    }
+
+    /**
+     * Dependency injection
+     * Calling of plugins
+     * 
+     * @param string $method            
+     * @param array $args            
+     */
+    public function __call ($method, array $args)
+    {
+        if (isset(self::$_plugins[$method])) {
+            $class = self::$_plugins[$method];
+            $plugin = new $plugin();
+            return call_user_func_array(array(
+                    $plugin,
+                    $method
+            ), $args);
+        } else {
+            throw new Tinebase_Exception(
+                    'Plugin ' . $method . ' was not found in haystack');
+        }
+    }
+}
\ No newline at end of file
index 4f983c1..021d6d9 100644 (file)
@@ -28,5 +28,8 @@ if (extension_loaded('iconv')) {
 // intialize composers autoloader
 require 'vendor/autoload.php';
 
+// initialize plugins
+require 'init_plugins.php';
+
 // activate our own error handler after autoloader initialization
 set_error_handler('Tinebase_Core::errorHandler', E_ALL | E_STRICT);
diff --git a/tine20/init_plugins.php b/tine20/init_plugins.php
new file mode 100644 (file)
index 0000000..50fee6a
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Tine 2.0
+ * 
+ * Use this script to initialize plugins for frontend, controller and backend layers
+ *
+ * @package     Tinebase
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Flávio Gomes da Silva Lisboa <flavio.lisboa@serpro.gov.br>
+ * @copyright   Copyright (c) 2008-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014 Serpro (http://www.serpro.gov.br)
+ *
+ */
+/*
+ * For injecting plugin into frontend layer:
+ * 
+ * Tinebase_Frontend_Abstract::attachPlugin('[method]', '[class]');
+ * 
+ * or
+ * 
+ * Tinebase_Frontend_Abstract::attachPlugins(array('[method]','[class]'), '[namespace]');
+ * 
+ * For injecting plugin into controller layer:
+ * 
+ * Tinebase_Controller_Abstract::attachPlugin('[method]', '[class]');
+ * 
+ * or
+ *  
+ * Tinebase_Controller_Abstract::attachPlugins(array('[method]','[class]'), '[namespace]');
+ * 
+ * For injecting plugin into backend layer:
+ * 
+ * Tinebase_Backend_Abstract::attachPlugin('[method]', '[class]');
+ * 
+ * or
+ * 
+ * Tinebase_Backend_Abstract::attachPlugin(array('[method]','[class]'), '[namespace]'); 
+ */
\ No newline at end of file