2009-07-23 82 views
5

我实现插件的动态加载的方式如下:卸载动态类在PHP

function processPlugin($plgFile, $db) { 
    require_once($plgFile); 
    $plgin = new PlginImpl(); 
    $plgin->setDb($db); 
    $ret = $plgin->process(); 
    return $ret; 
} 

每个插件都定义了一个名为PlginImpl类,它工作正常。但应该可以在返回值process()内调用更多的插件。这将要求上述规定的同样的方法,但失败:

Fatal error: Cannot redeclare class PlginImpl in .. 

请注意,每个插件是一个类,即:

class PlginImpl extends Plugin implements PluginInterface 

Plugin提供一些有用的功能,同时PluginInterface定义即process()

我认为所有插件都被命名为PlginImpl的事实会导致问题,因此我的问题是:在加载require_once之后,有没有办法卸载一个类(PlginImpl)?或者我应该遵循完全不同的方法?


编辑 我尝试没有成功以下的事情: process()
  • 调用__destruct()

    • 没有设置$plgin - 它并不既不内processPlugin()也不process方法中工作

    非常感谢!

  • 回答

    10

    由于在加载完类后无法卸载该类,因此唯一的选择是重命名每个插件。

    PluginX,PluginY等,但它应该不重要,因为你可以强制他们使用插件接口,如你所示。

    要加载特定插件,你可以简单地有类似solomongaby建议,但不是一个文件名,你通过它的名字插件的..这样的事情:

    function loadPlugin($pluginName) { 
        require_once $pluginName . '.php'; 
    
        $plugin = new $pluginName; 
        //do whatever with $plugin 
    } 
    
    0

    你可能看具有plgun-> process()方法调用析构http://ca.php.net/manual/en/language.oop5.decon.php

    或者

    你可以有$ plgin-结果>处理()存储在一个临时的变种,然后未设置($ plgin)

    function processPlugin($plgFile, $db) { 
        require_once($plgFile); 
        $plgin = new PlginImpl(); 
        $plgin->setDb($db); 
        $result = $plgin->process(); 
        unset($plgin); 
        return $result; 
    } 
    

    但是我认为你可能很难接近这个问题。

    你应该也许有一个类插件,然后实现()是一种方法。

    +0

    我尝试使用unset,如上所述,但它只卸载PlginImpl的实例,但定义仍保留在内存中(第二次运行失败)。解构器也不起作用。 – MrG 2009-07-23 15:17:14

    0

    我会做一个类来处理插件加载

    class pluginLoader { 
        protected $_plugins = array(); 
        protected $_db; 
    
        public function setDB($db) { 
        $this->_db = $db; 
        } 
    
    
        public function load($plgFile) { 
        if (!isset($this->_plugins[$plgFile])) { 
         require_once($plgFile); 
         $plgin = new $plgFile(); 
         $plgin->setDb($this->_db); 
         $this->_plugins[$plgFile] = $plgin; 
        } 
        $this->_plugins[$plgFile]->process(); 
        } 
    
        public static function instance($db) { 
        static $instance; 
    
        if (!$instance instanceof self) { 
         $instance = new self($db); 
        } 
        return $instance; 
        } 
    } 
    

    ,并使用它,你会首先做一个

    pluginLoader::instance($db); 
    

    ,然后加载插件

    pluginLoader::instance()->load($plgFile); 
    
    +0

    非常感谢 - 但是由于所有插件都被称为“PlginImpl”,我不会像以前一样遇到同样的问题吗? – MrG 2009-07-23 15:23:18

    +0

    将它与上面的解决方案结合起来并使用类文件的插件名称 – 2009-07-24 07:15:05

    1

    我不是100%肯定,但我不相信一旦声明它就可以卸载一个班级。

    但是,那里一种方法来完成你想要做的事情。创建下一个之前

    名称每一类不同,摧毁了一个类:

    $class1 = "foo"; 
    $class2 = "bar"; 
    
    $pluginResult = processPlugin($class1); 
    // do stuff 
    $pluginResult = processPlugin($class2); 
    
    function processPlugin($pluginName, $db) { 
        require_once($pluginName . ".inc"); //or whatever scheme you want to use. 
        $plgin = new $plugin; 
        $plgin->setDb($db); 
        $ret = $plgin->process(); 
        unset($plgin); 
        return $ret; 
    } 
    

    你有一些额外定义的类游逛,但取消设置插件加载后应该有希望尽量减少内存问题。

    4

    另一种选择,虽然我不推荐它,但是使用runkit_import

    +1

    不知道这个。酷:) – AntonioCS 2009-08-10 21:47:58

    1

    我知道这个问题已经发布很多年了,但我今天碰到了这个问题,我希望这个日志可以帮助未来有同样的问题。由于在许多回复中已经提到,PHP(至少到5.3)不允许卸载类;可以做的是避免名称之间的冲突。牢记这一点,我写了一个代码,将每个插件加载到唯一的名称空间中。

    $uniqueContext = 'Context_'.rand(); 
    $content = file_get_contents($actionScript); 
    if (strstr($content, '<?php')) { 
        $content = str_replace('<?php', "<?php namespace $uniqueContext;", $content); 
    } else { 
        if (strstr($content, '<?')) { 
         $content = str_replace('<?', "<?php namespace $uniqueContext;", $content); 
        } else { 
         $content = "namespace $uniqueContext;".$content; 
        } 
    } 
    $tmp=array_search('uri', @array_flip(stream_get_meta_data($GLOBALS[mt_rand()]=tmpfile()))); 
    file_put_contents($tmp, $content); 
    require_once($tmp); 
    

    插件作家必须知道引用的类是指相关的背景下,主机创建的。