动态地将函数添加到PHP类作为插件

时间:2015-04-08 15:05:32

标签: php plugins template-engine

我正在使用PHP类导出模板,并且有一个用于调用函数的标记,应该从插件脚本动态加载。脚本存储在目录中,并以标记中函数的名称命名。然后将动态加载的函数名称存储在一个数组中,这样它们只被加载一次。这样做很好,所以我的问题不是如何解决这个问题,而是我要求输入是否这是一个稳定安全的解决方案,如果有更好的方法来解决这个问题,或者我已经监督了什么?

ObserverDecorator模式都不适用于此设计,我希望它保持简约;在这种特殊情况下,我不会使用类,只使用函数。此外,如果您认为这是一个有效的解决方案,或者您提出了更好的建议;我可能也会在其他类似的类中实现它 - 因此我想从头开始做到这一点。所以,请严格和批评。

该课程看起来像这样:

<?php
// TemplateEngine.php
class TemplateEngine {
    private static $functions = array();
    private $vars = array();
    ...
    public function &getVars() { return $this->vars; }
    ...
    private function call(&$tag) {
        $name = strtr(strtolower($tag->id),'-','_');
        $value = $tag->value;
        $func = false;
        if(preg_match('/^[\w\_]+$/',$name)) {
            if(isset(self::$functions[$name])) {
                $func = &self::$functions[$name];
            } else {
                $script = __DIR__.'/functions/'.$name.'.php';
                if(file_exists($script)) $func = require_once $script;
                self::$functions[$key] = &$func;
            }
        }
        $ret = '';
        if(is_callable($func)) $ret = $func($this,$value);
        return $ret;
    }
...
}

然后函数脚本迭代一个数组并用它的值解析一个字符串,如下所示:

<?php
// functions/iterate.php
return function(&$templ,$param) {
    $vars = &$templ->getVars();
    list($key,$value) = $param;
    if(!isset($vars[$key])) return '';
    $var = $vars[$key];
    if(!is_array($var)) $var = array($key=>$var);
    $value = stripcslashes($value);
    $ret = '';
    foreach($var as $k=>$v) {
        $vars['key'] = $k;
        $vars['value'] = $v;
        $ret .= $templ->parse($value);
    }
    return $ret;
}

在模板中,标签可能如下所示:

{call iterate|js-files|<script src="{value}">\n}

这样的标签将调用函数“iterate”并在模板变量数组中查找索引“js-files”(从配置文件中读取),并解析字符串“&lt; script ...&gt ;”

1 个答案:

答案 0 :(得分:1)

一种方法(就像我在my template engine for PHP, Contemplate中所做的那样)是这样的:

在PHP模板类中有一个存储插件函数(及其名称)的数组,然后使用php __call magic method调用用户定义的插件(使用call_user_func)如果它是一个名字你的插件表中的函数,否则调用被调用的方法(因为它将是一个本机方法)

注意::这有点慢(在委托时使用__call),但这是PHP(5.1 +)中动态方法的最佳解决方案

用户可以在模板引擎中添加插件(例如):

public function addPlugin($name, $handler)
{
    $this->plugins[$name] = $handler;
}

使用示例:

MyTemplateEngine->addPlugin('foo', function(){ return 'foo'; });

调用插件:

MyTemplateEngine->foo();