Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

namespace-based plugins using autoload() #730

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 47 additions & 1 deletion libs/Smarty.class.php
Expand Up @@ -611,6 +611,13 @@ class Smarty extends Smarty_Internal_TemplateBase
*/
protected $plugins_dir = array();

/**
* plugins namespace
*
* @var array
*/
protected $plugins_namespaces = array();

/**
* cache directory
*
Expand Down Expand Up @@ -889,6 +896,42 @@ public function setPluginsDir($plugins_dir)
return $this;
}

/**
* Adds namespace for plugin files
*
* @param null|array|string $plugins_namespaces
*
* @return Smarty current Smarty instance for chaining
*/
public function addPluginsNamespace($plugins_namespaces)
{
$this->plugins_namespaces = array_merge($this->plugins_namespaces, (array)$plugins_namespaces);
return $this;
}

/**
* Get plugin namespace(s)
*
* @return array list of plugin namespace(s)
*/
public function getPluginsNamespaces() : Array
{
return $this->plugins_namespaces;
}

/**
* Set plugin namepaces
*
* @param string|array $plugins_namespaces namespace(s) of plugins
*
* @return Smarty current Smarty instance for chaining
*/
public function setPluginsNamespaces($plugins_namespaces)
{
$this->plugins_namespaces = (array)$plugins_namespaces;
return $this;
}

/**
* Get compiled directory
*
Expand Down Expand Up @@ -1021,7 +1064,10 @@ public function createTemplate($template, $cache_id = null, $compile_id = null,
*/
public function loadPlugin($plugin_name, $check = true)
{
return $this->ext->loadPlugin->loadPlugin($this, $plugin_name, $check);
$result = $this->ext->autoLoadPlugin->autoLoadPlugin($this, $plugin_name, $check);
if ($result === false) $result = $this->ext->loadPlugin->loadPlugin($this, $plugin_name, $check);

return $result;
}

/**
Expand Down
1 change: 1 addition & 0 deletions libs/sysplugins/smarty_internal_extension_handler.php
Expand Up @@ -31,6 +31,7 @@
* @property Smarty_Internal_Method_AssignByRef $assignByRef
* @property Smarty_Internal_Method_LoadFilter $loadFilter
* @property Smarty_Internal_Method_LoadPlugin $loadPlugin
* @property Smarty_Internal_Method_AutoLoadPlugin $autoLoadPlugin
* @property Smarty_Internal_Method_RegisterFilter $registerFilter
* @property Smarty_Internal_Method_RegisterObject $registerObject
* @property Smarty_Internal_Method_RegisterPlugin $registerPlugin
Expand Down
56 changes: 56 additions & 0 deletions libs/sysplugins/smarty_internal_method_autoloadplugin.php
@@ -0,0 +1,56 @@
<?php

/**
* Smarty Extension AutoLoadPlugin
*
* $smarty->autoLoadPlugin() method
*
* @package Smarty
* @subpackage PluginsInternal
* @author Stefan Froehlich
*/
class Smarty_Internal_Method_AutoLoadPlugin
{
/**
* Takes unknown classes and tries to load the
* appropriate plugin file via autoloader.
* The namespace must be registered with Smarty,
* the class name must match the plugin name and
* there needs to be a method named like the plugin
* type.
*
* @param \Smarty $smarty
* @param string $plugin_name class plugin name to load
* @param bool $check check if already loaded
*
* @return bool|string|array
* @throws \SmartyException
*/
public function autoLoadPlugin(Smarty $smarty, String $plugin_name, Bool $check)
{
// Naming convention of plugins is "Smarty_type_name" (corresponds
// to the file name of procedural plugins). We need to extract type
// and name
if (!preg_match('#^smarty_((internal)|([^_]+))_(.+)$#i', $plugin_name, $matches)) {
throw new SmartyException("plugin {$plugin_name} is not a valid name format");
}

// if function or class exists, exit silently (procedural plugin already loaded)
if ($check && (is_callable($plugin_name) || class_exists($plugin_name, false))) {
return true;
}

// check for auto-loader plugin
foreach ($smarty->getPluginsNamespaces() as $namespace) {
$class = $namespace . '\\' . $matches[4];
if (class_exists($class, true) && is_callable(array($class, $matches[1]))) {
// the requested class exists and the required method exists
if ($check) return true;
else return array($class, $matches[1]);
}
}

// no plugin loaded
return false;
}
}
5 changes: 4 additions & 1 deletion libs/sysplugins/smarty_internal_runtime_filterhandler.php
Expand Up @@ -41,7 +41,10 @@ public function runFilter($type, $content, Smarty_Internal_Template $template)
} elseif (class_exists($plugin_name, false) && is_callable(array($plugin_name, 'execute'))) {
$callback = array($plugin_name, 'execute');
} elseif ($template->smarty->loadPlugin($plugin_name, false)) {
if (function_exists($plugin_name)) {
if (class_exists($name, true) && is_callable(array($name, "{$type}filter"))) {
// use autoloader style plugin
$callback = array($name, "{$type}filter");
} elseif (function_exists($plugin_name)) {
// use loaded Smarty2 style plugin
$callback = $plugin_name;
} elseif (class_exists($plugin_name, false) && is_callable(array($plugin_name, 'execute'))) {
Expand Down
37 changes: 28 additions & 9 deletions libs/sysplugins/smarty_internal_template.php
Expand Up @@ -448,19 +448,38 @@ public function _checkPlugins($plugins)
{
static $checked = array();
foreach ($plugins as $plugin) {
$name = join('::', (array)$plugin[ 'function' ]);
if (!isset($checked[ $name ])) {
if (!is_callable($plugin[ 'function' ])) {
if (is_file($plugin[ 'file' ])) {
include_once $plugin[ 'file' ];
if (is_callable($plugin[ 'function' ])) {
$checked[ $name ] = true;
if (array_key_exists('method', $plugin)) {
// autoloader plugins
$name = join('::', $plugin[ 'method' ]);
if (is_callable($name)) {
$checked[ $name ] = true;
}
else {
throw new SmartyException("Plugin '{$name}' not callable");
}
}
elseif (array_key_exists('function', $plugin)) {
// legacy plugins
$name = join('::', (array)$plugin[ 'function' ]);
if (!isset($checked[ $name ])) {
if (!is_callable($plugin[ 'function' ])) {
if (is_file($plugin[ 'file' ])) {
include_once $plugin[ 'file' ];
if (is_callable($plugin[ 'function' ])) {
$checked[ $name ] = true;
}
}
} else {
$checked[ $name ] = true;
}
} else {
$checked[ $name ] = true;
}
}
else {
// plugin must either set "method" (auto-load plugins)
// or "function" (procedural plugins)
throw new SmartyException("Plugin '{$name}' not defined");
}

if (!isset($checked[ $name ])) {
if (false !== $this->smarty->loadPlugin($name)) {
$checked[ $name ] = true;
Expand Down
15 changes: 14 additions & 1 deletion libs/sysplugins/smarty_internal_templatecompilerbase.php
Expand Up @@ -820,7 +820,20 @@ public function getPlugin($plugin_name, $plugin_type)
// loop through plugin dirs and find the plugin
$function = 'smarty_' . $plugin_type . '_' . $plugin_name;
$file = $this->smarty->loadPlugin($function, false);
if (is_string($file)) {
if (is_array($file)) {
// plugin is implemented by a static method of an auto-loaded class
if ($this->caching && ($this->nocache || $this->tag_nocache)) {
$this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'method' ] =
$file;
} else {
$this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'method' ] =
$file;
}
if ($plugin_type === 'modifier') {
$this->modifier_plugins[ $plugin_name ] = true;
}
return join('::', $file);
} elseif (is_string($file)) {
if ($this->caching && ($this->nocache || $this->tag_nocache)) {
$this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
$file;
Expand Down