Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Why was addPluginsDir deprecated in smarty 5? #1149

@guycalledseven

Description

@guycalledseven

Although docs and unit tests still mention addPluginsDir method and suggest it's usage?

We have dropped support for $smarty->plugins_dir and $smarty->use_include_path.
Use $smarty->addPluginsDir() or consider writing a proper extension.

https://github.com/smarty-php/smarty/blob/21d7fbb67e8c6098084d9ba49d5cef07c8de49be/docs/upgrading.md?plain=1#L94C1-L95C102

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:

  1. eager loader for compile time
  2. 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 filters

where 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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions