-
-
Notifications
You must be signed in to change notification settings - Fork 710
Description
Although docs and unit tests still mention addPluginsDir method and suggest it's usage?
We have dropped support for
$smarty->plugins_dirand$smarty->use_include_path.
Use$smarty->addPluginsDir()or consider writing a proper extension.
I have 265 plugins working on smarty 4 working on dozens of different projects, and trying to bring them back to smarty 5, without need to write extensions or modify html templates (replacing all with modifiers and rewriting logic on this scale is just not viable).
AFAIK the only way to bring my plugins dir back to work now on smarty 5 is to preload ALL plugins at initialisation time like this:
function registerLegacyPlugins($smarty, array $pluginDirs)
{
foreach ($pluginDirs as $dir) {
if (!is_dir($dir) || !is_readable($dir)) {
continue;
}
$files = scandir($dir);
foreach ($files as $file) {
// We are looking for files like: type.name.php (e.g., compiler.version.php)
if (preg_match('/^(function|modifier|block|compiler)\.(.+)\.php$/', $file, $matches)) {
$type = $matches[1];
$name = $matches[2];
$filePath = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR . $file;
// The callback function name follows Smarty's legacy convention
$callback = "smarty_{$type}_{$name}";
// We need to include the file to make the function available
require_once $filePath;
if (is_callable($callback)) {
$smarty->registerPlugin($type, $name, $callback);
}
}
}
}
}
// enable replacement for old $smarty->addPluginsDir
registerLegacyPlugins($smarty, $myPluginDirs);I do not want to load everything all at once at every request, I need ability to lazy load it. So, to make this work I took hybrid approach of using 2 loaders:
- eager loader for compile time
- lazy loader for runtime
/**
* EAGERLY loads and registers only the plugins that define tags.
* This runs once to satisfy the compiler.
*/
function registerTagPlugins(Smarty\Smarty $smarty, array $pluginDirs)
{
$tag_types = 'compiler|function|block';
foreach ($pluginDirs as $dir) {
if (!is_dir($dir) || !is_readable($dir)) continue;
foreach (scandir($dir) as $file) {
if (preg_match("/^({$tag_types})\.(.+)\.php$/", $file, $matches)) {
$type = $matches[1];
$name = $matches[2];
$filePath = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR . $file;
$callback = "smarty_{$type}_{$name}";
require_once $filePath;
if (is_callable($callback)) {
$smarty->registerPlugin($type, $name, $callback);
}
}
}
}
}
$myPluginDirs = [
'library/smarty5/myplugins/'
;
registerTagPlugins($smarty, $myPluginDirs);
require('smarty5/LazyModifierAndFilterExtension.php'); // For lazy-loading modifiers/filters
$smarty->addExtension(new LazyModifierAndFilterExtension($myPluginDirs)); // For modifiers and filterswhere extension is
<?php // file: LazyModifierAndFilterExtension.php
use Smarty\Extension\Base;
class LazyModifierAndFilterExtension extends Base
{
private array $plugins = [];
public function __construct(array $pluginDirs)
{
$lazy_types = 'modifier|outputfilter|prefilter|postfilter';
foreach ($pluginDirs as $dir) {
if (!is_dir($dir) || !is_readable($dir)) continue;
foreach (scandir($dir) as $file) {
if (preg_match("/^({$lazy_types})\.(.+)\.php$/", $file, $matches)) {
$type = $matches[1];
$name = $matches[2];
$this->plugins[$type][$name] = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR . $file;
}
}
}
}
public function getModifierCallback(string $name): ?callable
{
return $this->lazyLoad('modifier', $name);
}
public function getFilterCallback(string $type, string $name): ?callable
{
$pluginType = $type . 'filter';
return $this->lazyLoad($pluginType, $name);
}
private function lazyLoad(string $type, string $name): ?callable
{
if (isset($this->plugins[$type][$name])) {
require_once $this->plugins[$type][$name];
$callback = "smarty_{$type}_{$name}";
if (is_callable($callback)) {
return $callback;
}
}
return null;
}
}Is this by design of smarty5? Why such design and such a major breaking change?