tags:

views:

1052

answers:

5

I implemented dynamic loading of plugins in the following way:

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

Each plugin defines a class named PlginImpl, which works fine. But it should be possible to call further plugins specified within within the return value of process(). That would call the same method specified above, but fails with:

Fatal error: Cannot redeclare class PlginImpl in ..

Please note that each plugin is a class, i.e.:

class PlginImpl extends Plugin implements PluginInterface

Plugin offer some useful functions while PluginInterface defines i.e. process().

I assume that the fact that all plugins are named PlginImpl causes the problem, hence my question: is there a way to unload a class (PlginImpl) after loading it with require_once? Or is there an entirely different approach I should follow?


EDIT I tried without succeeding the following things:

  • unset $plgin after process()
  • calling __destruct() - it doesn't work neither within processPlugin() nor within the process method

Many, many thanks!

A: 

You might look at having the plgun->process() method call the deconstructor http://ca.php.net/manual/en/language.oop5.decon.php

Or

You could have the result of $plgin->process() stored in a temp var and then unset($plgin)

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

However I think you're probably approaching the problem in a hard way.

You should maybe have a class Plugin and then have Implement() be a method.

Thomas Schultz
I tried using unset as described above, but it only unloads the instance of PlginImpl, but the definition remains in memory (the 2nd run failes). Deconstructor doesn't work either.
MrG
A: 

i would make a class to handle the plugin loading

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;
  }
}

and to use it you would first do a

pluginLoader::instance($db);

and then to load a plugin

pluginLoader::instance()->load($plgFile);
solomongaby
Many thanks - but wouldn't I run into exactly the same probleme as before due to the fact that all plugins are called "PlginImpl" ?
MrG
combined it with the above solution and use the plugin names for the class file
solomongaby
+5  A: 

Since you can't unload a class after you've loaded it, the only option you have is to rename each plugin.

PluginX, PluginY, etc., but it shouldn't matter as you can just force them to use the plugin interface as you showed.

To load a specific plugin, you could simply have something like solomongaby suggests, but instead of a filename, you pass it the name of the plugin.. something like this:

function loadPlugin($pluginName) {
    require_once $pluginName . '.php';

    $plugin = new $pluginName;
    //do whatever with $plugin
}
Jani Hartikainen
+1  A: 

I'm not 100% sure, but I don't believe you can unload a class once it's declared.

However, there is a way to accomplish what you're trying to do.

Name each class differently and destroy one class before creating the next:

$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;
}

You'll have some extra defined classes hanging around, but unsetting the plugin once loaded should hopefully minimize memory issues.

Bryan
+1  A: 

Another option, though I don't recommend it, is to use runkit_import.

Saem
Didn't know about this one. Cool :)
AntonioCS