Implemented updated plugin system.

Plugins can now be loaded by using header files. It is no longer required that a directory is added where the plugin file is located. A iPluginHeader object is enough.
This commit is contained in:
Abel Hoogeveen 2018-09-29 19:46:24 +02:00
parent cb8e5a1113
commit a71a310ec4
No known key found for this signature in database
GPG Key ID: 96C2234920BF4292
17 changed files with 458 additions and 77 deletions

View File

@ -53,13 +53,6 @@ class PluginGetEvent extends Event
*/
public $pluginName;
/**
* Array of directories where to look at for the plugin
*
* @var array
*/
public $directories = array();
/**
* Potential plugin to return instead. If set, the plugins class will return this object
*
@ -67,10 +60,9 @@ class PluginGetEvent extends Event
*/
public $plugin = null;
public function init($pluginName, array $directories = array())
public function init($pluginName)
{
$this->pluginName = $pluginName;
$this->directories = $directories;
}
/**

View File

@ -36,6 +36,8 @@
namespace FuzeWorks;
use FuzeWorks\Exception\PluginException;
use ReflectionClass;
use ReflectionException;
/**
* Plugins Class.
@ -48,11 +50,12 @@ use FuzeWorks\Exception\PluginException;
*
* To create the plugin, create a directory (with the name of the plugin) in the Plugin folder, inside the Application environment. Next you should add a header.php file in this directory.
* This file needs to be in the FuzeWorks\Plugins namespace, and be named *PluginName*Header. For example: TestHeader.
* It is recommended that this header file implements the FuzeWorks\iPlugin. All headers must have the init() method. This method will run upon starting FuzeWorks.
* It is required that this header file implements the FuzeWorks\iPluginHeader. All headers must have the init() method. This method will run upon starting FuzeWorks.
*
* Next a plugin class should be created. This file should be named the same as the folder, and be in the Application\Plugin namespace. An alternative classname can be set in the header, by creating a public $className variable. This plugin can be called using the $plugins->get() method.
*
* @todo Implement events
* @todo Add methods to enable and disable plugins
* @author TechFuze <contact@techfuze.net>
* @copyright Copyright (c) 2013 - 2018, Techfuze. (http://techfuze.net)
*/
@ -104,7 +107,7 @@ class Plugins
/**
* Load the header files of all plugins.
*/
public function loadHeaders()
public function loadHeadersFromPluginPaths()
{
// Cycle through all pluginPaths
$this->headers = array();
@ -128,24 +131,20 @@ class Plugins
// If a header file exists, use it
$file = $pluginPath . DS . $pluginFolder . DS . 'header.php';
$pluginName = ucfirst($pluginFolder);
$className = '\FuzeWorks\Plugins\\'.$pluginName.'Header';
$pluginFolder = ucfirst($pluginFolder);
$className = '\FuzeWorks\Plugins\\'.$pluginFolder.'Header';
if (file_exists($file))
{
// And load it
if (in_array($pluginName, $this->cfg->disabled_plugins))
{
$this->headers[$pluginName] = 'disabled';
}
else
{
require_once($file);
$header = new $className();
$this->headers[$pluginName] = $header;
$this->headers[$pluginName]->init();
Factory::getInstance()->logger->log('Loaded Plugin Header: \'' . $pluginName . '\'');
}
// Load the header file
require_once($file);
$header = new $className();
if (!$header instanceof iPluginHeader)
{
continue;
}
// Load the header
$this->loadHeader($header);
}
// If it doesn't exist, skip it
@ -156,29 +155,69 @@ class Plugins
}
/**
* Get a plugin.
*
* @param string $pluginName Name of the plugin
* @param array $parameters Parameters to send to the __construct() method
* @param array $directory Directory to search for plugins in
* @return object Plugin
* Load a header object.
*
* The provided header will be loaded into the header registry and initialized.
*
* @param iPluginHeader $header
* @return bool
*/
public function get($pluginName, array $parameters = null, array $directory = null)
protected function loadHeader(iPluginHeader $header): bool
{
// Fetch the name
$pluginName = $header->getName();
// Check if the plugin is disabled
if (in_array($pluginName, $this->cfg->disabled_plugins))
{
$this->headers[$pluginName] = 'disabled';
return false;
}
// Initialize it
$h = $this->headers[$pluginName] = $header;
$h->init();
// And log it
Factory::getInstance()->logger->log('Loaded Plugin Header: \'' . $pluginName . '\'');
return true;
}
/**
* Add a Plugin to FuzeWorks
*
* The provided plugin header will be loaded into the registry and initialized
*
* @param iPluginHeader $header
* @return bool
*/
public function addPlugin(iPluginHeader $header): bool
{
return $this->loadHeader($header);
}
/**
* Get a plugin.
*
* @param string $pluginName Name of the plugin
* @param array $parameters Parameters to send to the __construct() method
* @return object Plugin
* @throws Exception\EventException
* @throws PluginException
* @throws ReflectionException
*/
public function get($pluginName, array $parameters = null)
{
if (empty($pluginName))
{
throw new PluginException("Could not load plugin. No name provided", 1);
}
// First get the directories where the plugin can be located
$directories = (is_null($directory) ? $this->pluginPaths : $directory);
// Determine the name of the plugin
$pluginFolder = $pluginName;
$pluginName = ucfirst($pluginName);
// Fire pluginGetEvent, and cancel or return custom plugin if required
$event = Events::fireEvent('pluginGetEvent', $pluginName, $directories);
$event = Events::fireEvent('pluginGetEvent', $pluginName);
if ($event->isCancelled())
{
return false;
@ -190,7 +229,6 @@ class Plugins
// Otherwise just set the variables
$pluginName = $event->pluginName;
$directories = $event->directories;
// Check if the plugin is already loaded and return directly
if (isset($this->plugins[$pluginName]))
@ -212,6 +250,8 @@ class Plugins
// Determine what file to load
$header = $this->headers[$pluginName];
// If a 'getPlugin' method is found in the header, call that instead
if (method_exists($header, 'getPlugin'))
{
$this->plugins[$pluginName] = $header->getPlugin();
@ -219,28 +259,21 @@ class Plugins
return $this->plugins[$pluginName];
}
$classFile = (isset($header->classFile) ? $header->classFile : $pluginName.".php");
// Determine class name and file
// @todo Find a more reliable method for determining header directory
$headerReflection = new ReflectionClass( get_class($header) );
$directory = dirname($headerReflection->getFileName());
$classFile = (isset($header->classFile) ? $directory.DS.$header->classFile : $directory.DS.$pluginName.".php");
$className = (isset($header->className) ? $header->className : '\Application\Plugin\\'.$pluginName);
// Find the correct file
$pluginFile = '';
foreach ($directories as $pluginPath) {
$file = $pluginPath . DS . $pluginFolder . DS . $classFile;
if (file_exists($file))
{
$pluginFile = $file;
break;
}
}
// If not found, throw exception
if (empty($pluginFile))
{
throw new PluginException("Could not load plugin. Class file does not exist", 1);
}
// Try to access the file
if (!file_exists($classFile))
{
throw new PluginException("Could not load plugin. Class file does not exist", 1);
}
// Attempt to load the plugin
require_once($pluginFile);
require_once($classFile);
if (!class_exists($className, false))
{
throw new PluginException("Could not load plugin. Class does not exist", 1);

View File

@ -37,7 +37,8 @@
namespace FuzeWorks;
interface iPlugin
interface iPluginHeader
{
public function getName(): string;
public function init();
}

View File

@ -45,13 +45,16 @@ use FuzeWorks\Plugins;
class pluginTest extends CoreTestAbstract
{
/**
* @var FuzeWorks\Plugins
*/
protected $plugins;
public function setUp()
{
$this->plugins = new Plugins();
$this->plugins->addPluginPath('tests'.DS.'plugins');
$this->plugins->loadHeaders();
$this->plugins->loadHeadersFromPluginPaths();
}
public function testGetPluginsClass()
@ -75,6 +78,22 @@ class pluginTest extends CoreTestAbstract
$this->assertSame($this->plugins->get('testReloadPlugin'), $this->plugins->get('testReloadPlugin'));
}
/**
* @depends testLoadPlugin
*/
public function testLoadHeader()
{
// Load the header object
require_once('tests' . DS . 'plugins' . DS . 'testLoadHeader'.DS.'loadHeader'.DS.'header.php');
$header = new Plugins\TestLoadHeaderHeader();
// Register the header object
$this->plugins->addPlugin($header);
// Assert the plugin
$this->assertInstanceOf('Application\Plugin\Plug', $this->plugins->get('Plug'));
}
/**
* @depends testLoadPlugin
* @expectedException FuzeWorks\Exception\PluginException
@ -92,6 +111,14 @@ class pluginTest extends CoreTestAbstract
$this->assertEquals('test_string', $this->plugins->get('testGetPluginMethod'));
}
/**
* @depends testLoadPlugin
*/
public function testGetPluginWithClassFile()
{
$this->assertInstanceOf('OtherPlug', $this->plugins->get('TestGetPluginWithClassFile'));
}
/**
* @depends testLoadPlugin
* @expectedException FuzeWorks\Exception\PluginException
@ -101,6 +128,18 @@ class pluginTest extends CoreTestAbstract
$this->plugins->get('testMissingPlugin');
}
/**
* @depends testMissingPlugin
* @expectedException FuzeWorks\Exception\PluginException
*/
public function testLoadHeaderNotIPluginHeader()
{
// Attempt to load all headers
$this->plugins->loadHeadersFromPluginPaths();
$this->plugins->get('TestLoadHeaderNotIPluginHeader');
}
/**
* @depends testLoadPlugin
* @expectedException FuzeWorks\Exception\PluginException
@ -125,7 +164,7 @@ class pluginTest extends CoreTestAbstract
public function testDisabledPlugin()
{
Factory::getInstance()->config->plugins->disabled_plugins = array('TestDisabledPlugin');
$this->plugins->loadHeaders();
$this->plugins->loadHeadersFromPluginPaths();
$this->plugins->get('testDisabledPlugin');
}
@ -136,7 +175,7 @@ class pluginTest extends CoreTestAbstract
public function testRunInvalidDirectory()
{
$this->plugins->addPluginPath('exists_not');
$this->plugins->loadHeaders();
$this->plugins->loadHeadersFromPluginPaths();
$this->plugins->get('testRunInvalidDirectory');
}
@ -146,7 +185,7 @@ class pluginTest extends CoreTestAbstract
$this->plugins->addPluginPath('tests'.DS.'plugins'.DS.'testAddPluginPath');
// And try to load it again
$this->plugins->loadHeaders();
$this->plugins->loadHeadersFromPluginPaths();
$this->assertInstanceOf('Application\Plugin\ActualPlugin', $this->plugins->get('ActualPlugin'));
}

View File

@ -34,10 +34,16 @@
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
use FuzeWorks\iPlugin;
use FuzeWorks\iPluginHeader;
class ActualPluginHeader implements iPlugin
class ActualPluginHeader implements iPluginHeader
{
public function getName(): string
{
return "ActualPlugin";
}
public function init()
{
}

View File

@ -34,10 +34,16 @@
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
use FuzeWorks\iPlugin;
use FuzeWorks\iPluginHeader;
class TestDisabledPluginHeader implements iPlugin
class TestDisabledPluginHeader implements iPluginHeader
{
public function getName(): string
{
return "TestDisabledPlugin";
}
public function init()
{
}

View File

@ -34,10 +34,16 @@
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
use FuzeWorks\iPlugin;
use FuzeWorks\iPluginHeader;
class TestGetPluginMethodHeader implements iPlugin
class TestGetPluginMethodHeader implements iPluginHeader
{
public function getName(): string
{
return "TestGetPluginMethod";
}
public function init()
{
}

View File

@ -0,0 +1,40 @@
<?php
/**
* FuzeWorks Framework Core.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, Techfuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
class OtherPlug
{
}

View File

@ -0,0 +1,53 @@
<?php
/**
* FuzeWorks Framework Core.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, Techfuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
use FuzeWorks\iPluginHeader;
class TestGetPluginWithClassFileHeader implements iPluginHeader
{
public $classFile = 'OtherPlugFile.php';
public $className = 'OtherPlug';
public function getName(): string
{
return "TestGetPluginWithClassFile";
}
public function init()
{
}
}

View File

@ -34,10 +34,16 @@
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
use FuzeWorks\iPlugin;
use FuzeWorks\iPluginHeader;
class TestInvalidClassHeader implements iPlugin
class TestInvalidClassHeader implements iPluginHeader
{
public function getName(): string
{
return "TestInvalidClass";
}
public function init()
{
}

View File

@ -0,0 +1,41 @@
<?php
/**
* FuzeWorks Framework Core.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, Techfuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace Application\Plugin;
class Plug
{
}

View File

@ -0,0 +1,50 @@
<?php
/**
* FuzeWorks Framework Core.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, Techfuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
use FuzeWorks\iPluginHeader;
class TestLoadHeaderHeader implements iPluginHeader
{
public function getName(): string
{
return "Plug";
}
public function init()
{
}
}

View File

@ -0,0 +1,41 @@
<?php
/**
* FuzeWorks Framework Core.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, Techfuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace Application\Plugin;
class TestLoadPlugin
{
}

View File

@ -0,0 +1,49 @@
<?php
/**
* FuzeWorks Framework Core.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 TechFuze
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2018, Techfuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
class TestLoadHeaderNotIPluginHeaderHeader
{
public function getName(): string
{
return "TestLoadHeaderNotIPluginHeader";
}
public function init()
{
}
}

View File

@ -34,11 +34,17 @@
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
use FuzeWorks\iPlugin;
use FuzeWorks\iPluginHeader;
class TestLoadPluginHeader implements iPlugin
class TestLoadPluginHeader implements iPluginHeader
{
public function init()
public function getName(): string
{
return "TestLoadPlugin";
}
public function init()
{
}
}

View File

@ -34,10 +34,16 @@
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
use FuzeWorks\iPlugin;
use FuzeWorks\iPluginHeader;
class TestMissingPluginHeader implements iPlugin
class TestMissingPluginHeader implements iPluginHeader
{
public function getName(): string
{
return "TestMissingPlugin";
}
public function init()
{
}

View File

@ -34,10 +34,16 @@
* @version Version 1.2.0
*/
namespace FuzeWorks\Plugins;
use FuzeWorks\iPlugin;
use FuzeWorks\iPluginHeader;
class TestReloadPluginHeader implements iPlugin
class TestReloadPluginHeader implements iPluginHeader
{
public function getName(): string
{
return "TestReloadPlugin";
}
public function init()
{
}