Search:

CWIS Developers Documentation

  • Main Page
  • Classes
  • Files
  • File List
  • File Members

PluginManager.php

Go to the documentation of this file.
00001 <?PHP
00002 
00006 class PluginManager {
00007 
00008     # ---- PUBLIC INTERFACE --------------------------------------------------
00009 
00015     function __construct($AppFramework, $PluginDirectories)
00016     {
00017         # save framework and directory list for later use
00018         $this->AF = $AppFramework;
00019         $this->DirsToSearch = $PluginDirectories;
00020 
00021         # get our own database handle
00022         $this->DB = new Database();
00023 
00024         # hook into events to load plugin PHP and HTML files
00025         $this->AF->HookEvent("EVENT_PHP_PAGE_LOAD", array($this, "FindPluginPhpFile"));
00026         $this->AF->HookEvent("EVENT_HTML_PAGE_LOAD", array($this, "FindPluginHtmlFile"));
00027     }
00028 
00033     function LoadPlugins()
00034     {
00035         # clear any existing errors
00036         $this->ErrMsgs = array();
00037 
00038         # load list of all base plugin files
00039         $this->FindPlugins($this->DirsToSearch);
00040 
00041         # for each plugin found
00042         foreach ($this->PluginNames as $PluginName)
00043         {
00044             # bring in plugin class file
00045             include_once($this->PluginFiles[$PluginName]);
00046 
00047             # if plugin class was defined by file
00048             if (class_exists($PluginName))
00049             {
00050                 # if plugin class is a valid descendant of base plugin class
00051                 $Plugin = new $PluginName;
00052                 if (is_subclass_of($Plugin, "Plugin"))
00053                 {
00054                     # register the plugin
00055                     $this->Plugins[$PluginName] = $Plugin;
00056                     $this->PluginEnabled[$PluginName] = TRUE;
00057                     $this->Plugins[$PluginName]->Register();
00058 
00059                     # check required plugin attributes
00060                     $Attribs[$PluginName] = $this->Plugins[$PluginName]->GetAttributes();
00061                     if (!strlen($Attribs[$PluginName]["Name"]))
00062                     {
00063                         $this->ErrMsgs[$PluginName][] = "Plugin <b>".$PluginName."</b>"
00064                                 ." could not be loaded because it"
00065                                 ." did not have a <i>Name</i> attribute set.";
00066                         unset($this->PluginEnabled[$PluginName]);
00067                         unset($this->Plugins[$PluginName]);
00068                     }
00069                     if (!strlen($Attribs[$PluginName]["Version"]))
00070                     {
00071                         $this->ErrMsgs[$PluginName][] = "Plugin <b>".$PluginName."</b>"
00072                                 ." could not be loaded because it"
00073                                 ." did not have a <i>Version</i> attribute set.";
00074                         unset($this->PluginEnabled[$PluginName]);
00075                         unset($this->Plugins[$PluginName]);
00076                     }
00077                 }
00078                 else
00079                 {
00080                     $this->ErrMsgs[$PluginName][] = "Plugin <b>".$PluginName."</b>"
00081                             ." could not be loaded because <i>".$PluginName."</i> was"
00082                             ." not a subclass of base <i>Plugin</i> class";
00083                 }
00084             }
00085             else
00086             {
00087                 $this->ErrMsgs[$PluginName][] = "Expected class <i>".$PluginName
00088                         ."</i> not found in plugin file <i>"
00089                         .$this->PluginFiles[$PluginName]."</i>";
00090             }
00091         }
00092 
00093         # install or upgrade plugins if needed
00094         foreach ($this->Plugins as $PluginName => $Plugin)
00095         {
00096             if ($this->PluginEnabled[$PluginName])
00097             {
00098                 $this->InstallPlugin($Plugin);
00099             }
00100         }
00101 
00102         # check plugin dependencies
00103         $this->CheckDependencies();
00104 
00105         # initialize each plugin
00106         foreach ($this->Plugins as $Plugin)
00107         {
00108             if ($this->PluginEnabled[$PluginName])
00109             {
00110                 $ErrMsg = $Plugin->Initialize();
00111                 if ($ErrMsg !== NULL)
00112                 {
00113                     $this->ErrMsgs[$PluginName][] = "Initialization failed for"
00114                             ." plugin <b>".$PluginName."</b>: <i>".$ErrMsg."</i>";
00115                     $this->PluginEnabled[$PluginName] = FALSE;
00116                 }
00117             }
00118         }
00119 
00120         # register any events declared by each plugin
00121         foreach ($this->Plugins as $PluginName => $Plugin)
00122         {
00123             if ($this->PluginEnabled[$PluginName])
00124             {
00125                 $Events = $Plugin->DeclareEvents();
00126                 if (count($Events)) {  $this->AF->RegisterEvent($Events);  }
00127             }
00128         }
00129 
00130         # hook plugins to events
00131         foreach ($this->Plugins as $PluginName => $Plugin)
00132         {
00133             if ($this->PluginEnabled[$PluginName])
00134             {
00135                 $EventsToHook = $Plugin->HookEvents();
00136                 foreach ($EventsToHook as $EventName => $PluginMethod)
00137                 {
00138                     $Result = $this->AF->HookEvent(
00139                             $EventName, array($Plugin, $PluginMethod));
00140                     if ($Result === FALSE)
00141                     {
00142                         $this->ErrMsgs[$PluginName][] = 
00143                                 "Unable to hook requested event <i>"
00144                                 .$EventName."</i> for plugin <b>".$PluginName."</b>";
00145                     }
00146                 }
00147             }
00148         }
00149 
00150         # limit plugin directory list to only active plugins
00151         foreach ($this->PluginEnabled as $PluginName => $Enabled)
00152         {
00153             if (isset($this->PluginDirs[$PluginName]) && !$Enabled)
00154             {
00155                 unset($this->PluginDirs[$PluginName]);
00156             }
00157         }
00158 
00159         # add plugin directories to list to be searched for object files
00160         $ObjDirs = array();
00161         foreach ($this->PluginDirs as $Dir)
00162         {
00163             $ObjDirs[$Dir] = "";
00164         }
00165         $this->AF->AddObjectDirectories($ObjDirs);
00166 
00167         # report to caller whether any problems were encountered
00168         return count($this->ErrMsgs) ? FALSE : TRUE;
00169     }
00170 
00175     function GetErrorMessages()
00176     {
00177         return $this->ErrMsgs;
00178     }
00179 
00184     function GetPluginAttributes()
00185     {
00186         $Info = array();
00187         foreach ($this->Plugins as $PluginName => $Plugin)
00188         {
00189             $Info[$PluginName] = $Plugin->GetAttributes(); 
00190             $Info[$PluginName]["Enabled"] = 
00191                     $this->PluginInfo[$PluginName]["Enabled"];
00192         }
00193         return $Info;
00194     }
00195 
00202     function PluginEnabled($PluginName, $NewValue = NULL)
00203     {
00204         if ($NewValue !== NULL)
00205         {
00206             $this->DB->Query("UPDATE PluginInfo"
00207                     ." SET Enabled = ".($NewValue ? "1" : "0")
00208                     ." WHERE BaseName = '".addslashes($PluginName)."'");
00209             $this->PluginEnabled[$PluginName] = $NewValue;
00210             $this->PluginInfo[$PluginName]["Enabled"] = $NewValue;
00211         }
00212         return $this->PluginEnabled[$PluginName];
00213     }
00214 
00215 
00216     # ---- PRIVATE INTERFACE -------------------------------------------------
00217 
00218     private $Plugins = array();
00219     private $PluginFiles = array();
00220     private $PluginNames = array();
00221     private $PluginDirs = array();
00222     private $PluginInfo = array();
00223     private $PluginEnabled = array();
00224     private $AF;
00225     private $DirsToSearch;
00226     private $ErrMsgs = array();
00227     private $DB;
00228 
00229     private function FindPlugins($DirsToSearch)
00230     {
00231         # for each directory
00232         $PluginFiles = array();
00233         foreach ($DirsToSearch as $Dir)
00234         {
00235             # if directory exists
00236             if (is_dir($Dir))
00237             {
00238                 # for each file in directory
00239                 $FileNames = scandir($Dir);
00240                 foreach ($FileNames as $FileName)
00241                 {
00242                     # if file looks like base plugin file
00243                     if (preg_match("/^[a-zA-Z_][a-zA-Z0-9_]*\.php$/", $FileName))
00244                     {
00245                         # add file to list
00246                         $PluginName = preg_replace("/\.php$/", "", $FileName);
00247                         $this->PluginNames[$PluginName] = $PluginName;
00248                         $this->PluginFiles[$PluginName] = $Dir."/".$FileName;
00249                     }
00250                     # else if file looks like plugin directory
00251                     elseif (is_dir($Dir."/".$FileName) 
00252                             && preg_match("/^[a-zA-Z_][a-zA-Z0-9_]*/", $FileName))
00253                     {
00254                         # if there is a base plugin file in the directory
00255                         $PossibleFile = $Dir."/".$FileName."/".$FileName.".php";
00256                         if (file_exists($PossibleFile))
00257                         {
00258                             # add plugin and directory to lists
00259                             $this->PluginNames[$FileName] = $FileName;
00260                             $this->PluginFiles[$FileName] = $PossibleFile;
00261                             $this->PluginDirs[$FileName] = $Dir."/".$FileName;
00262                         }
00263                         else
00264                         {
00265                             $this->ErrMsgs[$FileName][] =
00266                                     "Expected plugin file <i>".$FileName.".php</i> not"
00267                                     ." found in plugin subdirectory <i>"
00268                                     .$Dir."/".$FileName."</i>";
00269                         }
00270                     }
00271                 }
00272             }
00273         }
00274 
00275         # return list of base plugin files to caller
00276         return $PluginFiles;
00277     }
00278 
00279     private function InstallPlugin($Plugin)
00280     {
00281         # retrieve info about plugin from database
00282         $PluginName = get_class($Plugin);
00283         $this->DB->Query("SELECT * FROM PluginInfo"
00284                 ." WHERE BaseName = '".addslashes($PluginName)."'");
00285 
00286         # if plugin was not found in database
00287         $Attribs = $Plugin->GetAttributes();
00288         if ($this->DB->NumRowsSelected() == 0)
00289         {
00290             # add plugin to database
00291             $this->DB->Query("INSERT INTO PluginInfo"
00292                     ." (BaseName, Version, Installed, Enabled)"
00293                     ." VALUES ('".addslashes($PluginName)."', "
00294                     ." '".addslashes($Attribs["Version"])."', "
00295                     ."0, "
00296                     ." ".($Attribs["EnabledByDefault"] ? 1 : 0).")");
00297 
00298             # read plugin settings back in
00299             $this->DB->Query("SELECT * FROM PluginInfo"
00300                  ." WHERE BaseName = '".addslashes($PluginName)."'");
00301         }
00302 
00303         # store plugin settings for later use
00304         $this->PluginInfo[$PluginName] = $this->DB->FetchRow();
00305         $this->PluginEnabled[$PluginName] = $this->PluginInfo[$PluginName]["Enabled"];
00306 
00307         # if plugin is enabled
00308         if ($this->PluginEnabled[$PluginName])
00309         {
00310             # if plugin has not been installed
00311             if (!$this->PluginInfo[$PluginName]["Installed"])
00312             {
00313                 # install plugin
00314                 $ErrMsg = $Plugin->Install();
00315     
00316                 # if install succeeded
00317                 if ($ErrMsg == NULL)
00318                 {
00319                     # mark plugin as installed
00320                     $this->DB->Query("UPDATE PluginInfo SET Installed = 1"
00321                             ." WHERE BaseName = '".addslashes($PluginName)."'");
00322                 }
00323                 else
00324                 {
00325                     # disable plugin
00326                     $this->PluginEnabled[$PluginName] = FALSE;
00327     
00328                     # record error message about installation failure
00329                     $this->ErrMsgs[$PluginName][] = "Installation of plugin <b>"
00330                             .$PluginName."</b> failed: <i>".$ErrMsg."</i>";;
00331                 }
00332             }
00333             else
00334             {
00335                 # if plugin version is newer than version in database
00336                 if (version_compare($Attribs["Version"], 
00337                         $this->PluginInfo[$PluginName]["Version"]) == 1)
00338                 {
00339                     # upgrade plugin
00340                     $ErrMsg = $Plugin->Upgrade($this->PluginInfo[$PluginName]["Version"]);
00341     
00342                     # if upgrade succeeded
00343                     if ($ErrMsg == NULL)
00344                     {
00345                         # update plugin version in database
00346                         $Attribs = $Plugin->GetAttributes();
00347                         $this->DB->Query("UPDATE PluginInfo"
00348                                 ." SET Version = '".addslashes($Attribs["Version"])."'"
00349                                 ." WHERE BaseName = '".addslashes($PluginName)."'");
00350                         $this->PluginInfo[$PluginName]["Version"] = $Attribs["Version"];
00351                     }
00352                     else
00353                     {
00354                         # disable plugin
00355                         $this->PluginEnabled[$PluginName] = FALSE;
00356     
00357                         # record error message about upgrade failure
00358                         $this->ErrMsgs[$PluginName][] = "Upgrade of plugin <b>"
00359                                 .$PluginName."</b> from version <i>"
00360                                 .addslashes($this->PluginInfo[$PluginName]["Version"])
00361                                 ."</i> to version <i>"
00362                                 .addslashes($Attribs["Version"])."</i> failed: <i>"
00363                                 .$ErrMsg."</i>";
00364                     }
00365                 }
00366                 # else if plugin version is older than version in database
00367                 elseif (version_compare($Attribs["Version"], 
00368                         $this->PluginInfo[$PluginName]["Version"]) == -1)
00369                 {
00370                     # disable plugin
00371                     $this->PluginEnabled[$PluginName] = FALSE;
00372     
00373                     # record error message about version conflict
00374                     $this->ErrMsgs[$PluginName][] = "Plugin <b>"
00375                             .$PluginName."</b> is older (<i>"
00376                             .addslashes($Attribs["Version"])
00377                             ."</i>) than previously-installed version (<i>"
00378                             .addslashes($this->PluginInfo[$PluginName]["Version"])."</i>).";
00379                 }
00380             }
00381         }
00382     }
00383 
00384     private function CheckDependencies()
00385     {
00386         # look until all enabled plugins check out okay
00387         do
00388         {
00389             # start out assuming all plugins are okay
00390             $AllOkay = TRUE;
00391 
00392             # for each plugin
00393             foreach ($this->Plugins as $PluginName => $Plugin)
00394             {
00395                 # if plugin is currently enabled
00396                 if ($this->PluginEnabled[$PluginName])
00397                 {
00398                     # load plugin attributes
00399                     if (!isset($Attribs[$PluginName]))
00400                     {
00401                         $Attribs[$PluginName] = 
00402                                 $this->Plugins[$PluginName]->GetAttributes();
00403                     }
00404 
00405                     # for each dependency for this plugin
00406                     foreach ($Attribs[$PluginName]["Requires"] 
00407                             as $ReqName => $ReqVersion)
00408                     {
00409                         # if target plugin is not present or is disabled or is too old
00410                         if (isset($this->Plugins[$ReqName])
00411                                 && !isset($Attribs[$ReqName]))
00412                         {
00413                             $Attribs[$ReqName] = 
00414                                     $this->Plugins[$ReqName]->GetAttributes();
00415                         }
00416                         if (!isset($this->PluginEnabled[$ReqName])
00417                                 || !$this->PluginEnabled[$ReqName] 
00418                                 || (version_compare($ReqVersion, 
00419                                         $Attribs[$ReqName]["Version"], ">")))
00420                         {
00421                             # add error message and disable plugin
00422                             $this->ErrMsgs[$PluginName][] = "Plugin <i>"
00423                                     .$ReqName." ".$ReqVersion."</i>"
00424                                     ." required by <b>".$PluginName."</b>"
00425                                     ." was not available.";
00426                             $this->PluginEnabled[$PluginName] = FALSE;
00427 
00428                             # set flag indicating plugin did not check out
00429                             $AllOkay = FALSE;
00430                         }
00431                     }
00432                 }
00433             }
00434         } while ($AllOkay == FALSE);
00435     }
00436 
00438     function FindPluginPhpFile($PageName)
00439     {
00440         return $this->FindPluginPageFile($PageName, "php"); 
00441     }
00445     function FindPluginHtmlFile($PageName)
00446     {
00447         return $this->FindPluginPageFile($PageName, "html"); 
00448     }
00451     private function FindPluginPageFile($PageName, $Suffix)
00452     {
00453         # set up return value assuming we will not find plugin page file
00454         $ReturnValue["PageName"] = $PageName;
00455 
00456         # look for plugin name and plugin page name in base page name
00457         preg_match("/P_([A-Za-z].[A-Za-z0-9]*)_([A-Za-z0-9_-]+)/", $PageName, $Matches);
00458 
00459         # if base page name contained name of existing plugin with its own subdirectory
00460         if ((count($Matches) == 3) && isset($this->PluginDirs[$Matches[1]]))
00461         {
00462             # if PHP file with specified name exists in plugin subdirectory
00463             $PageFile = $this->PluginDirs[$Matches[1]]."/".$Matches[2].".".$Suffix;
00464             if (file_exists($PageFile))
00465             {
00466                 # set return value to contain full plugin PHP file name
00467                 $ReturnValue["PageName"] = $PageFile;
00468             }
00469         }
00470 
00471         # return array containing page name or page file name to caller
00472         return $ReturnValue;
00473     }
00474 }
00475 
00476 ?>
CWIS logo doxygen
Copyright 2009 Internet Scout