First draft for plugins system

This commit is contained in:
Frederic Guillot
2015-09-13 14:07:56 -04:00
parent c405f99fc8
commit a6a00a0040
31 changed files with 626 additions and 237 deletions

31
app/Core/PluginBase.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
namespace Core;
/**
* Plugin Base class
*
* @package core
* @author Frederic Guillot
*/
abstract class PluginBase extends Base
{
/**
* Method called for each request
*
* @abstract
* @access public
*/
abstract public function initialize();
/**
* Returns all classes that needs to be stored in the DI container
*
* @access public
* @return array
*/
public function getClasses()
{
return array();
}
}

144
app/Core/PluginLoader.php Normal file
View File

@@ -0,0 +1,144 @@
<?php
namespace Core;
use DirectoryIterator;
use PDOException;
/**
* Plugin Loader
*
* @package core
* @author Frederic Guillot
*/
class PluginLoader extends Base
{
/**
* Schema version table for plugins
*
* @var string
*/
const TABLE_SCHEMA = 'plugin_schema_versions';
/**
* Plugin folder
*
* @var string
*/
const PATH = __DIR__.'/../../plugins';
/**
* Scan plugin folder and load plugins
*
* @access public
*/
public function scan()
{
if (file_exists(self::PATH)) {
$dir = new DirectoryIterator(self::PATH);
foreach ($dir as $fileinfo) {
if (! $fileinfo->isDot() && $fileinfo->isDir()) {
$plugin = $fileinfo->getFilename();
$this->loadSchema($plugin);
$this->load($plugin);
}
}
}
}
/**
* Load plugin
*
* @access public
*/
public function load($plugin)
{
$class = '\Plugin\\'.$plugin.'\\Plugin';
$instance = new $class($this->container);
Tool::buildDic($this->container, $instance->getClasses());
$instance->initialize();
}
/**
* Load plugin schema
*
* @access public
* @param string $plugin
*/
public function loadSchema($plugin)
{
$filename = __DIR__.'/../../plugins/'.$plugin.'/Schema/'.ucfirst(DB_DRIVER).'.php';
if (file_exists($filename)) {
require($filename);
$this->migrateSchema($plugin);
}
}
/**
* Execute plugin schema migrations
*
* @access public
* @param string $plugin
*/
public function migrateSchema($plugin)
{
$last_version = constant('\Plugin\\'.$plugin.'\Schema\VERSION');
$current_version = $this->getSchemaVersion($plugin);
try {
$this->db->startTransaction();
$this->db->getDriver()->disableForeignKeys();
for ($i = $current_version + 1; $i <= $last_version; $i++) {
$function_name = '\Plugin\\'.$plugin.'\Schema\version_'.$i;
if (function_exists($function_name)) {
call_user_func($function_name, $this->db->getConnection());
}
}
$this->db->getDriver()->enableForeignKeys();
$this->db->closeTransaction();
$this->setSchemaVersion($plugin, $i - 1);
}
catch (PDOException $e) {
$this->db->cancelTransaction();
$this->db->getDriver()->enableForeignKeys();
die('Unable to migrate schema for the plugin: '.$plugin.' => '.$e->getMessage());
}
}
/**
* Get current plugin schema version
*
* @access public
* @param string $plugin
* @return integer
*/
public function getSchemaVersion($plugin)
{
return (int) $this->db->table(self::TABLE_SCHEMA)->eq('plugin', strtolower($plugin))->findOneColumn('version');
}
/**
* Save last plugin schema version
*
* @access public
* @param string $plugin
* @param integer $version
* @return boolean
*/
public function setSchemaVersion($plugin, $version)
{
$dictionary = array(
strtolower($plugin) => $version
);
return $this->db->getDriver()->upsert(self::TABLE_SCHEMA, 'plugin', 'version', $dictionary);
}
}

View File

@@ -213,49 +213,17 @@ class Router extends Base
if (! empty($_GET['controller']) && ! empty($_GET['action'])) {
$controller = $this->sanitize($_GET['controller'], 'app');
$action = $this->sanitize($_GET['action'], 'index');
$plugin = ! empty($_GET['plugin']) ? $this->sanitize($_GET['plugin'], '') : '';
}
else {
list($controller, $action) = $this->findRoute($this->getPath($uri, $query_string));
list($controller, $action) = $this->findRoute($this->getPath($uri, $query_string)); // TODO: add plugin for routes
$plugin = '';
}
return $this->load(
__DIR__.'/../Controller/'.ucfirst($controller).'.php',
$controller,
'\Controller\\'.ucfirst($controller),
$action
);
}
$class = empty($plugin) ? '\Controller\\'.ucfirst($controller) : '\Plugin\\'.ucfirst($plugin).'\Controller\\'.ucfirst($controller);
/**
* Load a controller and execute the action
*
* @access private
* @param string $filename
* @param string $controller
* @param string $class
* @param string $method
* @return bool
*/
private function load($filename, $controller, $class, $method)
{
if (file_exists($filename)) {
require $filename;
if (! method_exists($class, $method)) {
return false;
}
$this->action = $method;
$this->controller = $controller;
$instance = new $class($this->container);
$instance->beforeAction($controller, $method);
$instance->$method();
return true;
}
return false;
$instance = new $class($this->container);
$instance->beforeAction($controller, $action);
$instance->$action();
}
}

View File

@@ -13,11 +13,12 @@ use LogicException;
class Template extends Helper
{
/**
* Template path
* List of template overrides
*
* @var string
* @access private
* @var array
*/
const PATH = 'app/Template/';
private $overrides = array();
/**
* Render a template
@@ -33,16 +34,10 @@ class Template extends Helper
*/
public function render($__template_name, array $__template_args = array())
{
$__template_file = self::PATH.$__template_name.'.php';
if (! file_exists($__template_file)) {
throw new LogicException('Unable to load the template: "'.$__template_name.'"');
}
extract($__template_args);
ob_start();
include $__template_file;
include $this->getTemplateFile($__template_name);
return ob_get_clean();
}
@@ -62,4 +57,41 @@ class Template extends Helper
$template_args + array('content_for_layout' => $this->render($template_name, $template_args))
);
}
/**
* Define a new template override
*
* @access public
* @param string $original_template
* @param string $new_template
*/
public function setTemplateOverride($original_template, $new_template)
{
$this->overrides[$original_template] = $new_template;
}
/**
* Find template filename
*
* Core template name: 'task/show'
* Plugin template name: 'myplugin:task/show'
*
* @access public
* @param string $template_name
* @return string
*/
public function getTemplateFile($template_name)
{
$template_name = isset($this->overrides[$template_name]) ? $this->overrides[$template_name] : $template_name;
if (strpos($template_name, ':') !== false) {
list($plugin, $template) = explode(':', $template_name);
$path = __DIR__.'/../../plugins/'.ucfirst($plugin).'/Template/'.$template.'.php';
}
else {
$path = __DIR__.'/../Template/'.$template_name.'.php';
}
return $path;
}
}

View File

@@ -2,6 +2,8 @@
namespace Core;
use Pimple\Container;
/**
* Tool class
*
@@ -23,7 +25,6 @@ class Tool
$fp = fopen($filename, 'w');
if (is_resource($fp)) {
foreach ($rows as $fields) {
fputcsv($fp, $fields);
}
@@ -51,4 +52,24 @@ class Tool
return $identifier;
}
/**
* Build dependency injection container from an array
*
* @static
* @access public
* @param Container $container
* @param array $namespaces
*/
public static function buildDIC(Container $container, array $namespaces)
{
foreach ($namespaces as $namespace => $classes) {
foreach ($classes as $name) {
$class = '\\'.$namespace.'\\'.$name;
$container[lcfirst($name)] = function ($c) use ($class) {
return new $class($c);
};
}
}
}
}

View File

@@ -15,7 +15,7 @@ class Translator
*
* @var string
*/
const PATH = 'app/Locale/';
const PATH = 'app/Locale';
/**
* Locale
@@ -196,18 +196,27 @@ class Translator
* @static
* @access public
* @param string $language Locale code: fr_FR
* @param string $path Locale folder
*/
public static function load($language)
public static function load($language, $path = self::PATH)
{
setlocale(LC_TIME, $language.'.UTF-8', $language);
$filename = self::PATH.$language.DIRECTORY_SEPARATOR.'translations.php';
$filename = $path.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'translations.php';
if (file_exists($filename)) {
self::$locales = require $filename;
}
else {
self::$locales = array();
self::$locales = array_merge(self::$locales, require($filename));
}
}
/**
* Clear locales stored in memory
*
* @static
* @access public
*/
public static function unload()
{
self::$locales = array();
}
}