1

I've been trying to refactor a "bit" of code that I'd previously developed. Basically, the project was my response to not knowing how to use XSLT effectively, so I developed an XML transformation system in PHP. The program reads through the tags of an XML file and does something along these lines to convert it to HTML:

private function getTemplate(...) {
    switch ($nodeName) {
        case "a" :
            // code here to generate a link tag
            break;
        case "box" :
            // code here to generate the divs and whatnot to create a box
            break;
        case "ref" :
            // look up an external reference file and include a bibliography
            break;
        default :
            // do the default thing
    }
}

That was all working great, except that I ended up with 26 branches to my switch, and that once switch block was over 1000 lines of code. Needless to say, it made maintenance slightly more difficult.

What I've done now is to pull the code of each branch out into its own file (named "a.php", "box.php", "ref.php" ...) and include that file each time:

if (file_exists("templates/$nodeName.php")) {
    include "templates/$nodeName.php";
} else {
    // do the default thing
}

Again, this works, but benchmarking it shows that it has slowed down processing times by 50%. I'm assuming that this is because there's now up to 4000 includes being done now.

What I was considering was to put the code for each template into a function, and if the function hasn't been declared then include the file, and then run the function - the only problem with that is that the existing code has been written in the scope of the original function, using $this, etc.

Given that this code is not run in real time (eg: it merely processes the XML into static HTML files which are stored - it's not done on the fly), do you have any advice for me here?

4

3 回答 3

3

caveat: i don't know PHP, but I can google and i like function pointers better than huge switch statements, so if you can't just use XSLT...

...one option would be to adopt a naming convention for your 'worker' functions that incorporated the html node's tag name, e.g. <tagname>_converter, include all the functions, and replace your switch statement with something like:

private function getTemplate(...) 
{
    $func = $nodeName + "_converter";
    if (function_exists($func))    //see if function is defined, somehow...
    {
        $func();    //call conversion function (pass whatever is needed)
    }
}
于 2008-12-05T04:01:47.350 回答
1

尝试这样的事情:

//utils.php
函数句柄框($节点)
{
//...
}

函数句柄链接($节点)
{
//....
}
?\>

然后:

require_once '模板/utils.php';


函数获取模板()
{
  开关($节点)
  {
      案例“一”:
        handle_link($node,$otherParams);
      休息;
  }
}

}

基本上,这会将每个节点的所有功能重构为自己的功能。从这里您可以研究更通用的解决方案。它与您原来的非常相似,但 switch/case 语句将更易于管理,而其中没有大量代码。然后,如果您认为有必要,您可以考虑实现Stratgy 设计模式。

于 2008-12-05T04:43:50.273 回答
1

无需深入研究 XSLT 本身并专注于重构问题:您已经考虑了相当多的不同策略。我将针对您已经尝试过的一种方法以及另一种可能对您有用的方法提供优化技巧。

当你说依赖很多文件时性能下降了 50%。我认为您没有启用 PHP 操作码缓存。你这样做了,那 50% 的损失应该会消失。

在另一种方法上,我建议创建一个基类NodeProcessor左右。然后为每个节点创建一个新类ANodeProcessorRefNodeProcessor...等。

或者,您只能创建一个NodeProcessor包含以下形式的方法的类:processTag. 然后:

  1. 你可以打电话$this->processNode($tag);
  2. 在那个方法中:$name = 'process'. $tag;
  3. 然后:return $this->nodeProcessor->{$name};
  4. NodeProcessor应该有一种方法来为未定义方法__call()的标签执行“默认操作” 。processTag

同样,不要忘记添加操作码缓存。

于 2008-12-05T12:04:12.617 回答