Tim Igoe's Web Design, Development and Hosting Blog

Blog > PHP5 Plugin System

As part of my framework's development, I wanted to be able to make core modules fixed across many sites, but equally I wanted to be able to tweak and modify certain bits easily.

What I came up with, is a simple 1 include script plugin system that can be dropped into any existing PHP site and immediately enable plugin support anywhere the developer chooses. So called Hooks are points set within the code that can be attached to to run extra code.

pluginmanager.inc


<?

  // Plugin Manager Framework Class
  //
  // Tim Igoe (tim _AT_ timigoe _DOT_ co _DOT_ uk)

  class pluginManager
  
{
    private static $pluginManager;
    private $plugins;
    private $hooks;

    function __construct ()
    {
      $this->plugins = array ();
      $this->hooks = array ();

      // Get a list of hte plugins from the plugin folder
      // include them, then initialise them
      // Once they have been init'd
      // the hooks dotted around the system can call on the plugins
      $plugin_dir = 'plugins/';

      $pluginsList = @opendir ($plugin_dir);

      while ($PlugFolder = readdir ($pluginsList))
      
{
        if ($PlugFolder != '.' && $PlugFolder != '..')
        { // Look for folders of plugins
           if (is_dir ($plugin_dir . $PlugFolder))
                    
{
         
            
          // Now we look at the files in the plugin folder
          $PlugList = opendir ($plugin_dir . $PlugFolder);
          while ($Plug = readdir ($PlugList))
          
{
            if ($Plug != '.' && $Plug != '..' && strtolower (substr ($Plugstrlen ($Plug) - 4)) == '.php')
            {
              $Code = file_get_contents ($plugin_dir . $PlugFolder . '/' . $Plug);
              $Plug = strtolower ($Plug);
              $Plug = substr ($Plug  , 0strlen ($Plug) - 4); // Strip the .PHP

              // Save the Plugin (/plugins/PlugName/HookName.php)
              $this->addHook ($Plug$PlugFolder$Code);
            
}
          }
        }
      }
    }

    // Gets the plugin manager singleton
    public static function getPluginManager ()
    
{
      if (is_null (self::$pluginManager))
      {
        self::$pluginManager = new pluginManager ($config);
      }
      return self::$pluginManager;
    }

    private function addHook ($hook$plugin$code)
    {
      if (!isset ($this->hooks[$hook][$plugin])) // Not set
        $this->hooks[$hook][$plugin] = $code;
      else // Already set
        die ("Already Hooked into this - " . $plugin . " -> " . $hook);
    
}

    // Call any plugins that are hooked into this
    public function pluginHooks ($hook)
    
{
      
        $hook = strtolower ($hook);
        $Code = '';
        if (isset ($this->hooks[$hook]))
        {
          foreach ($this->hooks[$hook] as $plugin => $hook)
            $Code .= $hook;
        }
      return $Code;
    }
  }

?>


With this saved in your site code folder, you can include it and start it up by doing the following:

index.php


<?
  // Include the Plugin Manager
  include ('pluginmanager.inc'); 
  // Start it up
  pluginManager::getPluginManager ();
?>


Thats it then, the plugin manager is running in your site. Using the power of Eval, you can put plugins anywhere within your site setting known 'hooks' for example

index.php


<?
  include ('pluginmanager.inc'); 

  // Start it up

  pluginManager::getPluginManager ();


  eval (PluginManager::getPluginManager ()->pluginHooks ('Start'));
 
  // Do Stuff

  eval (PluginManager::getPluginManager ()->pluginHooks ('End'));
?>


This creates a hook called Start, and runs any plugins that are attached to it - which at this moment we don't have any of.

Lets Create a Plugin

Relative to the pluginmanager.inc file, create a folder plugins. Within this, we can now create plugins - a single plugin is a group of text files, called by the plugin hook that they are to hook onto and filled with the PHP code to be run.

So, for example, if we create a folder, 'test' and within this 2 text files, one called Start.php and one called End.php (to match the Hooks we created earlier). We'll now create a simple plugin to monitor the time taken to load the entire page.

Start.php


function getMicroTime ()
{
  list ($usec$sec) = explode (" ",microtime ());
  return ((float)$usec + (float)$sec);
}
$plugin_loadtime_startTime = getMicroTime ();


End.php


print "Page Generated in " . round (getMicroTime () - $plugin_loadtime_startTime2) . " seconds. ";


This simple block of code should enable you to add a Plugin System to your own sites relatively easily.

Similar Articles from the web

PHP5 Plugin System

In reply to this Allen said error in pluginmanager

I know this is an older post, but the script has potential.  I belive theres a couple of errors in the pluginmanager.inc file. Adding an extra closing tag for one of the "if" statements fixes this, but it still doesn't seem to execute the code from the plugin(s). Any ideas?

In reply to this TimIgoe said PluginManager

I believe I've corrected the problem in the above code with the missing tag, also there was a legacy line in there from the system I actually developed it for, it was reliant on a configration file being set. This has been removed (the if check at the start of the pluginHooks function)

If you need anymore help, let me know.

In reply to this brixter said it's not working!

it's not working, I followed as stated above but it returns parse error i forgot the line. Please do UPDATE this because I want this^^

In reply to this TimIgoe said Fix

In pasting the code into the blog, I'd managed to miss copying a } which caused the errors, this has been fixed now in the post above.

Let me know if you still need help.

In reply to this brixter said fast

Your fast thank for the update^^ will post for question later. Thank you very much!

In reply to this Brixter said fast

This one sure stands out from the rest of plugins tutorials out there. pretty excited to implement this one of these nights.

In reply to this Brixter said Error

Hi again!

Seems like error in the pluginmanager.inc. added } in line 51 and it runs well.

It would be nice to be able to add <?PHP in the plugin directory. So I rplaced the code see below:

    // Call any plugins that are hooked into this
    public function pluginHooks ($hook)
     {
     
        $hook = strtolower ($hook);
        $Code = '';
        if (isset ($this->hooks[$hook]))
        {
          foreach ($this->hooks[$hook] as $plugin => $hook)
            $Code .= $hook;
        }

// Let's use eval instead of return
      eval "PHP?>".$Code."<?";
    }
  }

Use echo to print the plugin.
echo (PluginManager::getPluginManager ()->pluginHooks ('Start'));


In reply to this Brixter said reply

Hi again!



Hi, I noticed at your tutorial that if you are to create a plugin, you
won't be able to do so if you include php tags (<?PHP ?> ). So I
modified your code to work the way I like. Please see below if i am
correct:



<?PHP

  class PSOUP_plugin

   {

    private static $PSOUP_plugin;

    private $plugins;

    private $hooks;



    function __construct ()

    {

      $this->plugins = array ();

      $this->hooks = array ();



      // Get a list of hte plugins from the plugin folder

      // include them, then initialise them

      // Once they have been init'd

      // the hooks dotted around the system can call on the plugins

      $plugin_dir = 'PSOUP_plugins/';



      $pluginsList = @opendir ($plugin_dir);



      while ($PlugFolder = readdir ($pluginsList))

       {

        if ($PlugFolder != '.' && $PlugFolder != '..')

        { // Look for folders of plugins

           if (is_dir ($plugin_dir . $PlugFolder))

                     {

        

           

          // Now we look at the files in the plugin folder

          $PlugList = opendir ($plugin_dir . $PlugFolder);

          while ($Plug = readdir ($PlugList))

           {

            if ($Plug != '.' && $Plug != '..' && strtolower (substr ($Plug, strlen ($Plug) - 4)) == '.php')

            {

              $Code = file_get_contents ($plugin_dir . $PlugFolder . '/' . $Plug);

              $Plug = strtolower ($Plug);

              $Plug = substr ($Plug  , 0, strlen ($Plug) - 4); // Strip the .PHP



              // Save the Plugin (/plugins/PlugName/HookName.php)

              $this->addHook ($Plug, $PlugFolder, $Code);

             }

          }

        }

      }

    }

    }



   

    // Gets the plugin manager singleton

    public static function fetch ()

     {

      if (is_null (self::$PSOUP_plugin))

      {

        self::$PSOUP_plugin = new PSOUP_plugin ($config);

      }

      return self::$PSOUP_plugin;

    }



    private function addHook ($hook, $plugin, $code)

    {

      if (!isset ($this->hooks[$hook][$plugin])) // Not set

        $this->hooks[$hook][$plugin] = $code;

      else // Already set

        die ("Already Hooked into this - " . $plugin . " -> " . $hook);

     }



    // Call any plugins that are hooked into this

    public function PSOUP_hooks ($hook)

     {

 



        $hook = strtolower ($hook);

        $Code = '';

        if (isset ($this->hooks[$hook]))

        {

          foreach ($this->hooks[$hook] as $plugin => $hook)

            $Code .= $hook;

        }

        eval ("PHP?>".$Code."<?");

    }

  }



?>





I also noticed that with my current directory structure please see
below, when I try to invoke the plugin inside the directory admin, the
plugin it wont work. So with this regard, i created another plugin file
(admin.class.php) and changed the directory path from PSOUP_plugins/
into ../PSOUP_plugins/.



Please how can I fix this?



This is my directory structure as of now.



MYSCRIPT:

 - Plugins

 - Admin

 - PSOUP_plugins

 - Includes - plugin.class.php - admin.class.php

 - index.php

In reply to this TimIgoe said Fix

I don't think that'd work - You've modified the eval to have php?> and then <? rahter than ?> and <?php

For each plugin set, you need a folder, then you name the files with teh code the same as the 'hook' you are looking for.

in the case of the one you pasted,

PluginManager::getPluginManager ()->pluginHooks ('Start')

the file would be called Start.php within a folder, in plugins. So...

plugins\myTestPlugin\Start.php

also, you NEED to eval the code inline to get the function to run in the context of the data it has to access, if you eval in the object, your plugin code will never see the data from 'outside' that it may need to work on.

In reply to this brixter said question

Thanks fixed about the ?> and <?PHP. Yes the plugin need to have the same name, but my question is that when I try to run the plugin inside a directory let say admin directory, it won't run not unless I create another plugin class and change the plugin path adding "../". So my problem right now is that I have the 2 same plugin class.

That is my only probem now. Thanks for this great plugin class youve shared. I am able to maximize by adding the ability to upload, install, uninstall and configure the activated plugin inside my admin panel. I also able to create my own "Hello Dolly" plugin with this ^^

Post a reply