ApplicationFramework.php

Go to the documentation of this file.
00001 <?PHP
00002 
00007 class ApplicationFramework {
00008 
00009     # ---- PUBLIC INTERFACE --------------------------------------------------
00010  /*@(*/
00012 
00020     function __construct($ObjectDirectories = NULL)
00021     {
00022         # save execution start time
00023         $this->ExecutionStartTime = microtime(TRUE);
00024 
00025         # save object directory search list
00026         if ($ObjectDirectories) {  $this->AddObjectDirectories($ObjectDirectories);  }
00027 
00028         # set up object file autoloader
00029         $this->SetUpObjectAutoloading();
00030 
00031         # set up function to output any buffered text in case of crash
00032         register_shutdown_function(array($this, "OnCrash"));
00033 
00034         # set up our internal environment
00035         $this->DB = new Database();
00036 
00037         # load our settings from database
00038         $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
00039         $this->Settings = $this->DB->FetchRow();
00040         if (!$this->Settings)
00041         {
00042             $this->DB->Query("INSERT INTO ApplicationFrameworkSettings"
00043                     ." (LastTaskRunAt) VALUES (NOW())");
00044             $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
00045             $this->Settings = $this->DB->FetchRow();
00046         }
00047 
00048         # set PHP maximum execution time
00049         $this->MaxExecutionTime($this->Settings["MaxExecTime"]);
00050 
00051         # register events we handle internally
00052         $this->RegisterEvent($this->PeriodicEvents);
00053         $this->RegisterEvent($this->UIEvents);
00054     }
00062     function AddObjectDirectories($Dirs)
00063     {
00064         foreach ($Dirs as $Location => $Prefix)
00065         {
00066             $Location = $Location
00067                     .((substr($Location, -1) != "/") ? "/" : "");
00068             self::$ObjectDirectories = array_merge(
00069                     array($Location => $Prefix),
00070                     self::$ObjectDirectories);
00071         }
00072     }
00073 
00078     function LoadPage($PageName)
00079     {
00080         # buffer any output from includes or PHP file
00081         ob_start();
00082 
00083         # include any files needed to set up execution environment
00084         foreach ($this->EnvIncludes as $IncludeFile)
00085         {
00086             include($IncludeFile);
00087         }
00088 
00089         # signal page load
00090         $this->SignalEvent("EVENT_PAGE_LOAD", array("PageName" => $PageName));
00091 
00092         # signal PHP file load
00093         $SignalResult = $this->SignalEvent("EVENT_PHP_FILE_LOAD", array(
00094                 "PageName" => $PageName));
00095 
00096         # if signal handler returned new page name value
00097         $NewPageName = $PageName;
00098         if (($SignalResult["PageName"] != $PageName)
00099                 && strlen($SignalResult["PageName"]))
00100         {
00101             # if new page name value is page file
00102             if (file_exists($SignalResult["PageName"]))
00103             {
00104                 # use new value for PHP file name
00105                 $PageFile = $SignalResult["PageName"];
00106             }
00107             else
00108             {
00109                 # use new value for page name
00110                 $NewPageName = $SignalResult["PageName"];
00111             }
00112         }
00113 
00114         # if we do not already have a PHP file
00115         if (!isset($PageFile))
00116         {
00117             # look for PHP file for page
00118             $OurPageFile = "pages/".$NewPageName.".php";
00119             $LocalPageFile = "local/pages/".$NewPageName.".php";
00120             $PageFile = file_exists($LocalPageFile) ? $LocalPageFile
00121                     : (file_exists($OurPageFile) ? $OurPageFile
00122                     : "pages/".$this->DefaultPage.".php");
00123         }
00124 
00125         # load PHP file
00126         include($PageFile);
00127 
00128         # save buffered output to be displayed later after HTML file loads
00129         $PageOutput = ob_get_contents();
00130         ob_end_clean();
00131 
00132         # set up for possible TSR (Terminate and Stay Resident :))
00133         $ShouldTSR = $this->PrepForTSR();
00134 
00135         # if PHP file indicated we should autorefresh to somewhere else
00136         if ($this->JumpToPage)
00137         {
00138             if (!strlen(trim($PageOutput)))
00139             {
00140                 ?><html>
00141                 <head>
00142                     <meta http-equiv="refresh" content="0; URL=<?PHP
00143                             print($this->JumpToPage);  ?>">
00144                 </head>
00145                 <body bgcolor="white">
00146                 </body>
00147                 </html><?PHP
00148             }
00149         }
00150         # else if HTML loading is not suppressed
00151         elseif (!$this->SuppressHTML)
00152         {
00153             # set content-type to get rid of diacritic errors
00154             header("Content-Type: text/html; charset="
00155                 .$this->HtmlCharset, TRUE);
00156 
00157             # load common HTML file if available
00158             $HtmlFile = $this->FindCommonTemplate("Common");
00159             if ($HtmlFile) {  include($HtmlFile);  }
00160 
00161             # begin buffering content
00162             ob_start();
00163 
00164             # signal HTML file load
00165             $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD", array(
00166                     "PageName" => $PageName));
00167 
00168             # if signal handler returned new page name value
00169             $NewPageName = $PageName;
00170             $HtmlFile = NULL;
00171             if (($SignalResult["PageName"] != $PageName)
00172                     && strlen($SignalResult["PageName"]))
00173             {
00174                 # if new page name value is HTML file
00175                 if (file_exists($SignalResult["PageName"]))
00176                 {
00177                     # use new value for HTML file name
00178                     $HtmlFile = $SignalResult["PageName"];
00179                 }
00180                 else
00181                 {
00182                     # use new value for page name
00183                     $NewPageName = $SignalResult["PageName"];
00184                 }
00185             }
00186 
00187             # load page content HTML file if available
00188             if ($HtmlFile === NULL)
00189             {
00190                 $HtmlFile = $this->FindTemplate($this->ContentTemplateList, $NewPageName);
00191             }
00192             if ($HtmlFile)
00193             {
00194                 include($HtmlFile);
00195             }
00196             else
00197             {
00198                 print("<h2>ERROR:  No HTML/TPL template found"
00199                         ." for this page.</h2>");
00200             }
00201 
00202             # signal HTML file load complete
00203             $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD_COMPLETE");
00204 
00205             # stop buffering and save content
00206             $BufferedContent = ob_get_contents();
00207             ob_end_clean();
00208 
00209             # load page start HTML file if available
00210             $HtmlFile = $this->FindCommonTemplate("Start");
00211             if ($HtmlFile) {  include($HtmlFile);  }
00212 
00213             # write out page content
00214             print($BufferedContent);
00215 
00216             # load page end HTML file if available
00217             $HtmlFile = $this->FindCommonTemplate("End");
00218             if ($HtmlFile) {  include($HtmlFile);  }
00219         }
00220 
00221         # run any post-processing routines
00222         foreach ($this->PostProcessingFuncs as $Func)
00223         {
00224             call_user_func_array($Func["FunctionName"], $Func["Arguments"]);
00225         }
00226 
00227         # write out any output buffered from page code execution
00228         if (strlen($PageOutput))
00229         {
00230             if (!$this->SuppressHTML)
00231             {
00232                 ?><table width="100%" cellpadding="5"
00233                         style="border: 2px solid #666666;  background: #CCCCCC;
00234                         font-family: Courier New, Courier, monospace;
00235                         margin-top: 10px;"><tr><td><?PHP
00236             }
00237             if ($this->JumpToPage)
00238             {
00239                 ?><div style="color: #666666;"><span style="font-size: 150%;">
00240                 <b>Page Jump Aborted</b></span>
00241                 (because of error or other unexpected output)<br />
00242                 <b>Jump Target:</b>
00243                 <i><?PHP  print($this->JumpToPage);  ?></i></div><?PHP
00244             }
00245             print($PageOutput);
00246             if (!$this->SuppressHTML)
00247             {
00248                 ?></td></tr></table><?PHP
00249             }
00250         }
00251 
00252         # terminate and stay resident (TSR!) if indicated and HTML has been output
00253         # (only TSR if HTML has been output because otherwise browsers will misbehave)
00254         if ($ShouldTSR) {  $this->LaunchTSR();  }
00255     }
00256 
00263     function SetJumpToPage($Page)
00264     {
00265         if ((strpos($Page, "?") === FALSE)
00266                 && ((strpos($Page, "=") !== FALSE)
00267                     || ((strpos($Page, ".php") === FALSE)
00268                         && (strpos($Page, ".htm") === FALSE)
00269                         && (strpos($Page, "/") === FALSE))))
00270         {
00271             $this->JumpToPage = "index.php?P=".$Page;
00272         }
00273         else
00274         {
00275             $this->JumpToPage = $Page;
00276         }
00277     }
00278 
00283     function JumpToPageIsSet()
00284     {
00285         return ($this->JumpToPage === NULL) ? FALSE : TRUE;
00286     }
00287 
00297     function HtmlCharset($NewSetting = NULL)
00298     {
00299         if ($NewSetting !== NULL) {  $this->HtmlCharset = $NewSetting;  }
00300         return $this->HtmlCharset;
00301     }
00302 
00309     function SuppressHTMLOutput($NewSetting = TRUE)
00310     {
00311         $this->SuppressHTML = $NewSetting;
00312     }
00313 
00320     function ActiveUserInterface($UIName = NULL)
00321     {
00322         if ($UIName !== NULL)
00323         {
00324             $this->ActiveUI = preg_replace("/^SPTUI--/", "", $UIName);
00325         }
00326         return $this->ActiveUI;
00327     }
00328 
00344     function AddPostProcessingCall($FunctionName,
00345             &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE,
00346             &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE,
00347             &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE)
00348     {
00349         $FuncIndex = count($this->PostProcessingFuncs);
00350         $this->PostProcessingFuncs[$FuncIndex]["FunctionName"] = $FunctionName;
00351         $this->PostProcessingFuncs[$FuncIndex]["Arguments"] = array();
00352         $Index = 1;
00353         while (isset(${"Arg".$Index}) && (${"Arg".$Index} !== self::NOVALUE))
00354         {
00355             $this->PostProcessingFuncs[$FuncIndex]["Arguments"][$Index]
00356                     =& ${"Arg".$Index};
00357             $Index++;
00358         }
00359     }
00360 
00366     function AddEnvInclude($FileName)
00367     {
00368         $this->EnvIncludes[] = $FileName;
00369     }
00370 
00376     function GUIFile($FileName)
00377     {
00378         # pull off file name suffix
00379         $NamePieces = explode(".", $FileName);
00380         $Suffix = strtolower(array_pop($NamePieces));
00381 
00382         # determine which location to search based on file suffix
00383         $ImageSuffixes = array("gif", "jpg", "png");
00384         $FileList = in_array($Suffix, $ImageSuffixes)
00385                 ? $this->ImageFileList : $this->CommonTemplateList;
00386 
00387         # search for file and return result to caller
00388         return $this->FindTemplate($FileList, $FileName);
00389     }
00390 
00400     function PUIFile($FileName)
00401     {
00402         $FullFileName = $this->GUIFile($FileName);
00403         if ($FullFileName) {  print($FullFileName);  }
00404     }
00405 
00410     function FindCommonTemplate($PageName)
00411     {
00412         return $this->FindTemplate(
00413                 array_merge($this->CommonTemplateList, $this->ContentTemplateList),
00414                 $PageName);;
00415     }
00416 
00425     function LoadFunction($Callback)
00426     {
00427         if (!is_callable($Callback) && is_string($Callback))
00428         {
00429             $Locations = $this->FunctionFileList;
00430             foreach (self::$ObjectDirectories as $Location => $Prefix)
00431             {
00432                 $Locations[] = $Location."%PAGENAME%.php";
00433                 $Locations[] = $Location."%PAGENAME%.html";
00434             }
00435             $FunctionFileName = $this->FindTemplate($Locations, "F-".$Callback);
00436             if ($FunctionFileName)
00437             {
00438                 include_once($FunctionFileName);
00439             }
00440         }
00441         return is_callable($Callback);
00442     }
00443 
00448     function GetElapsedExecutionTime()
00449     {
00450         return microtime(TRUE) - $this->ExecutionStartTime;
00451     }
00452 
00457     function GetSecondsBeforeTimeout()
00458     {
00459         return ini_get("max_execution_time") - $this->GetElapsedExecutionTime();
00460     }
00461 
00462     /*@)*/ /* Application Framework */
00463 
00464     # ---- Event Handling ----------------------------------------------------
00465  /*@(*/
00467 
00471     const EVENTTYPE_DEFAULT = 1;
00477     const EVENTTYPE_CHAIN = 2;
00483     const EVENTTYPE_FIRST = 3;
00491     const EVENTTYPE_NAMED = 4;
00492 
00494     const ORDER_FIRST = 1;
00496     const ORDER_MIDDLE = 2;
00498     const ORDER_LAST = 3;
00499 
00508     function RegisterEvent($EventsOrEventName, $EventType = NULL)
00509     {
00510         # convert parameters to array if not already in that form
00511         $Events = is_array($EventsOrEventName) ? $EventsOrEventName
00512                 : array($EventsOrEventName => $Type);
00513 
00514         # for each event
00515         foreach ($Events as $Name => $Type)
00516         {
00517             # store event information
00518             $this->RegisteredEvents[$Name]["Type"] = $Type;
00519             $this->RegisteredEvents[$Name]["Hooks"] = array();
00520         }
00521     }
00522 
00536     function HookEvent($EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
00537     {
00538         # convert parameters to array if not already in that form
00539         $Events = is_array($EventsOrEventName) ? $EventsOrEventName
00540                 : array($EventsOrEventName => $Callback);
00541 
00542         # for each event
00543         $Success = TRUE;
00544         foreach ($Events as $EventName => $EventCallback)
00545         {
00546             # if callback is valid
00547             if (is_callable($EventCallback))
00548             {
00549                 # if this is a periodic event we process internally
00550                 if (isset($this->PeriodicEvents[$EventName]))
00551                 {
00552                     # process event now
00553                     $this->ProcessPeriodicEvent($EventName, $EventCallback);
00554                 }
00555                 # if specified event has been registered
00556                 elseif (isset($this->RegisteredEvents[$EventName]))
00557                 {
00558                     # add callback for event
00559                     $this->RegisteredEvents[$EventName]["Hooks"][]
00560                             = array("Callback" => $EventCallback, "Order" => $Order);
00561 
00562                     # sort callbacks by order
00563                     if (count($this->RegisteredEvents[$EventName]["Hooks"]) > 1)
00564                     {
00565                         usort($this->RegisteredEvents[$EventName]["Hooks"],
00566                                 array("ApplicationFramework", "HookEvent_OrderCompare"));
00567                     }
00568                 }
00569                 else
00570                 {
00571                     $Success = FALSE;
00572                 }
00573             }
00574             else
00575             {
00576                 $Success = FALSE;
00577             }
00578         }
00579 
00580         # report to caller whether all callbacks were hooked
00581         return $Success;
00582     }
00583     private static function HookEvent_OrderCompare($A, $B)
00584     {
00585         if ($A["Order"] == $B["Order"]) {  return 0;  }
00586         return ($A["Order"] < $B["Order"]) ? -1 : 1;
00587     }
00588 
00597     function SignalEvent($EventName, $Parameters = NULL)
00598     {
00599         $ReturnValue = NULL;
00600 
00601         # if event has been registered
00602         if (isset($this->RegisteredEvents[$EventName]))
00603         {
00604             # set up default return value (if not NULL)
00605             switch ($this->RegisteredEvents[$EventName]["Type"])
00606             {
00607                 case self::EVENTTYPE_CHAIN:
00608                     $ReturnValue = $Parameters;
00609                     break;
00610 
00611                 case self::EVENTTYPE_NAMED:
00612                     $ReturnValue = array();
00613                     break;
00614             }
00615 
00616             # for each callback for this event
00617             foreach ($this->RegisteredEvents[$EventName]["Hooks"] as $Hook)
00618             {
00619                 # invoke callback
00620                 $Callback = $Hook["Callback"];
00621                 $Result = ($Parameters !== NULL)
00622                         ? call_user_func_array($Callback, $Parameters)
00623                         : call_user_func($Callback);
00624 
00625                 # process return value based on event type
00626                 switch ($this->RegisteredEvents[$EventName]["Type"])
00627                 {
00628                     case self::EVENTTYPE_CHAIN:
00629                         $ReturnValue = $Result;
00630                         $Parameters = $Result;
00631                         break;
00632 
00633                     case self::EVENTTYPE_FIRST:
00634                         if ($Result !== NULL)
00635                         {
00636                             $ReturnValue = $Result;
00637                             break 2;
00638                         }
00639                         break;
00640 
00641                     case self::EVENTTYPE_NAMED:
00642                         $CallbackName = is_array($Callback)
00643                                 ? (is_object($Callback[0])
00644                                         ? get_class($Callback[0])
00645                                         : $Callback[0])."::".$Callback[1]
00646                                 : $Callback;
00647                         $ReturnValue[$CallbackName] = $Result;
00648                         break;
00649 
00650                     default:
00651                         break;
00652                 }
00653             }
00654         }
00655 
00656         # return value if any to caller
00657         return $ReturnValue;
00658     }
00659 
00665     function IsStaticOnlyEvent($EventName)
00666     {
00667         return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE;
00668     }
00669 
00670     /*@)*/ /* Event Handling */
00671 
00672     # ---- Task Management ---------------------------------------------------
00673  /*@(*/
00675 
00677     const PRIORITY_HIGH = 1;
00679     const PRIORITY_MEDIUM = 2;
00681     const PRIORITY_LOW = 3;
00683     const PRIORITY_BACKGROUND = 4;
00684 
00697     function QueueTask($Callback, $Parameters = NULL,
00698             $Priority = self::PRIORITY_MEDIUM, $Description = "")
00699     {
00700         # pack task info and write to database
00701         if ($Parameters === NULL) {  $Parameters = array();  }
00702         $this->DB->Query("INSERT INTO TaskQueue"
00703                 ." (Callback, Parameters, Priority, Description)"
00704                 ." VALUES ('".addslashes(serialize($Callback))."', '"
00705                 .addslashes(serialize($Parameters))."', ".intval($Priority).", '"
00706                 .addslashes($Description)."')");
00707     }
00708 
00726     function QueueUniqueTask($Callback, $Parameters = NULL,
00727             $Priority = self::PRIORITY_MEDIUM, $Description = "")
00728     {
00729         if ($this->TaskIsInQueue($Callback, $Parameters))
00730         {
00731             $QueryResult = $this->DB->Query("SELECT TaskId,Priority FROM TaskQueue"
00732                     ." WHERE Callback = '".addslashes(serialize($Callback))."'"
00733                     .($Parameters ? " AND Parameters = '"
00734                             .addslashes(serialize($Parameters))."'" : ""));
00735             if ($QueryResult !== FALSE)
00736             {
00737                 $Record = $this->DB->FetchRow();
00738                 if ($Record["Priority"] > $Priority)
00739                 {
00740                     $this->DB->Query("UPDATE TaskQueue"
00741                             ." SET Priority = ".intval($Priority)
00742                             ." WHERE TaskId = ".intval($Record["TaskId"]));
00743                 }
00744             }
00745             return FALSE;
00746         }
00747         else
00748         {
00749             $this->QueueTask($Callback, $Parameters, $Priority, $Description);
00750             return TRUE;
00751         }
00752     }
00753 
00763     function TaskIsInQueue($Callback, $Parameters = NULL)
00764     {
00765         $FoundCount = $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM TaskQueue"
00766                 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
00767                 .($Parameters ? " AND Parameters = '"
00768                         .addslashes(serialize($Parameters))."'" : ""),
00769                 "FoundCount")
00770                 + $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM RunningTasks"
00771                 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
00772                 .($Parameters ? " AND Parameters = '"
00773                         .addslashes(serialize($Parameters))."'" : ""),
00774                 "FoundCount");
00775         return ($FoundCount ? TRUE : FALSE);
00776     }
00777 
00783     function GetTaskQueueSize($Priority = NULL)
00784     {
00785         return $this->DB->Query("SELECT COUNT(*) AS QueueSize FROM TaskQueue"
00786                 .($Priority ? " WHERE Priority = ".intval($Priority) : ""),
00787                 "QueueSize");
00788     }
00789 
00797     function GetQueuedTaskList($Count = 100, $Offset = 0)
00798     {
00799         return $this->GetTaskList("SELECT * FROM TaskQueue"
00800                 ." ORDER BY Priority, TaskId ", $Count, $Offset);
00801     }
00802 
00810     function GetRunningTaskList($Count = 100, $Offset = 0)
00811     {
00812         return $this->GetTaskList("SELECT * FROM RunningTasks"
00813                 ." WHERE StartedAt >= '".date("Y-m-d H:i:s", 
00814                         (time() - ini_get("max_execution_time")))."'"
00815                 ." ORDER BY StartedAt", $Count, $Offset);
00816     }
00817 
00825     function GetOrphanedTaskList($Count = 100, $Offset = 0)
00826     {
00827         return $this->GetTaskList("SELECT * FROM RunningTasks"
00828                 ." WHERE StartedAt < '".date("Y-m-d H:i:s", 
00829                         (time() - ini_get("max_execution_time")))."'"
00830                 ." ORDER BY StartedAt", $Count, $Offset);
00831     }
00832 
00837     function ReQueueOrphanedTask($TaskId)
00838     {
00839         $this->DB->Query("LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
00840         $this->DB->Query("INSERT INTO TaskQueue"
00841                          ." (Callback,Parameters,Priority,Description) "
00842                          ."SELECT Callback, Parameters, Priority, Description"
00843                          ." FROM RunningTasks WHERE TaskId = ".intval($TaskId));
00844         $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
00845         $this->DB->Query("UNLOCK TABLES");
00846     }
00847 
00852     function DeleteOrphanedTask($TaskId)
00853     {
00854         $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
00855     }
00856 
00862     function MaxTasks($NewValue = NULL)
00863     {
00864         if (func_num_args() && ($NewValue >= 1))
00865         {
00866             $this->DB->Query("UPDATE ApplicationFrameworkSettings"
00867                     ." SET MaxTasksRunning = '".intval($NewValue)."'");
00868             $this->Settings["MaxTasksRunning"] = intval($NewValue);
00869         }
00870         return $this->Settings["MaxTasksRunning"];
00871     }
00872 
00880     function MaxExecutionTime($NewValue = NULL)
00881     {
00882         if (func_num_args())
00883         {
00884             if ($NewValue != $this->Settings["MaxExecTime"])
00885             {
00886                 $this->Settings["MaxExecTime"] = max($NewValue, 5);
00887                 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
00888                         ." SET MaxExecTime = '"
00889                                 .intval($this->Settings["MaxExecTime"])."'");
00890             }
00891             ini_set("max_execution_time", $this->Settings["MaxExecTime"]);
00892             set_time_limit($this->Settings["MaxExecTime"]);
00893         }
00894         return ini_get("max_execution_time");
00895     }
00896 
00897     /*@)*/ /* Task Management */
00898 
00899     # ---- PRIVATE INTERFACE -------------------------------------------------
00900 
00901     private $JumpToPage = NULL;
00902     private $SuppressHTML = FALSE;
00903     private $DefaultPage = "Home";
00904     private $ActiveUI = "default";
00905     private $HtmlCharset = "UTF-8";
00906     private $PostProcessingFuncs = array();
00907     private $EnvIncludes = array();
00908     private $DB;
00909     private $Settings;
00910     private $ExecutionStartTime;
00911     private static $ObjectDirectories = array();
00912     private $MaxRunningTasksToTrack = 250;
00913     private $RunningTask;
00914 
00915     private $PeriodicEvents = array(
00916                 "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT,
00917                 "EVENT_DAILY" => self::EVENTTYPE_DEFAULT,
00918                 "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT,
00919                 "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT,
00920                 "EVENT_PERIODIC" => self::EVENTTYPE_NAMED,
00921                 );
00922     private $UIEvents = array(
00923                 "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT,
00924                 "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN,
00925                 "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN,
00926                 "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
00927                 );
00928 
00929     private function FindTemplate($FileList, $PageName)
00930     {
00931         $FileNameFound = NULL;
00932         foreach ($FileList as $FileName)
00933         {
00934             $FileName = str_replace("%ACTIVEUI%", $this->ActiveUI, $FileName);
00935             $FileName = str_replace("%PAGENAME%", $PageName, $FileName);
00936             if (file_exists($FileName))
00937             {
00938                 $FileNameFound = $FileName;
00939                 break;
00940             }
00941         }
00942         return $FileNameFound;
00943     }
00944 
00945     private function SetUpObjectAutoloading()
00946     {
00947         function __autoload($ClassName)
00948         {
00949             ApplicationFramework::AutoloadObjects($ClassName);
00950         }
00951     }
00952 
00954     static function AutoloadObjects($ClassName)
00955     {
00956         foreach (self::$ObjectDirectories as $Location => $Prefix)
00957         {
00958             $FileName = $Location.$Prefix.$ClassName.".php";
00959             if (file_exists($FileName))
00960             {
00961                 require_once($FileName);
00962                 break;
00963             }
00964         }
00965     }
00968     private function ProcessPeriodicEvent($EventName, $Callback)
00969     {
00970         # retrieve last execution time for event if available
00971         $Signature = self::GetCallbackSignature($Callback);
00972         $LastRun = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
00973                 ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
00974 
00975         # determine whether enough time has passed for event to execute
00976         $EventPeriods = array(
00977                 "EVENT_HOURLY" => 60*60,
00978                 "EVENT_DAILY" => 60*60*24,
00979                 "EVENT_WEEKLY" => 60*60*24*7,
00980                 "EVENT_MONTHLY" => 60*60*24*30,
00981                 "EVENT_PERIODIC" => 0,
00982                 );
00983         $ShouldExecute = (($LastRun === NULL)
00984                 || (time() > (strtotime($LastRun) + $EventPeriods[$EventName])))
00985                 ? TRUE : FALSE;
00986 
00987         # if event should run
00988         if ($ShouldExecute)
00989         {
00990             # add event to task queue
00991             $WrapperCallback = array("ApplicationFramework", "PeriodicEventWrapper");
00992             $WrapperParameters = array(
00993                     $EventName, $Callback, array("LastRunAt" => $LastRun));
00994             $this->QueueUniqueTask($WrapperCallback, $WrapperParameters);
00995         }
00996     }
00997 
00998     private static function PeriodicEventWrapper($EventName, $Callback, $Parameters)
00999     {
01000         static $DB;
01001         if (!isset($DB)) {  $DB = new Database();  }
01002 
01003         # run event
01004         $ReturnVal = call_user_func_array($Callback, $Parameters);
01005 
01006         # if event is already in database
01007         $Signature = self::GetCallbackSignature($Callback);
01008         if ($DB->Query("SELECT COUNT(*) AS EventCount FROM PeriodicEvents"
01009                 ." WHERE Signature = '".addslashes($Signature)."'", "EventCount"))
01010         {
01011             # update last run time for event
01012             $DB->Query("UPDATE PeriodicEvents SET LastRunAt = "
01013                     .(($EventName == "EVENT_PERIODIC")
01014                             ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
01015                             : "NOW()")
01016                     ." WHERE Signature = '".addslashes($Signature)."'");
01017         }
01018         else
01019         {
01020             # add last run time for event to database
01021             $DB->Query("INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES "
01022                     ."('".addslashes($Signature)."', "
01023                     .(($EventName == "EVENT_PERIODIC")
01024                             ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
01025                             : "NOW()").")");
01026         }
01027     }
01028 
01029     private static function GetCallbackSignature($Callback)
01030     {
01031         return !is_array($Callback) ? $Callback
01032                 : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0])
01033                         ."::".$Callback[1];
01034     }
01035 
01036     private function PrepForTSR()
01037     {
01038         # if HTML has been output and it's time to launch another task
01039         # (only TSR if HTML has been output because otherwise browsers
01040         #       may misbehave after connection is closed)
01041         if (($this->JumpToPage || !$this->SuppressHTML)
01042                 && (time() > (strtotime($this->Settings["LastTaskRunAt"])
01043                         + (ini_get("max_execution_time") 
01044                                 / $this->Settings["MaxTasksRunning"]) + 5))
01045                 && $this->GetTaskQueueSize())
01046         {
01047             # begin buffering output for TSR
01048             ob_start();
01049 
01050             # let caller know it is time to launch another task
01051             return TRUE;
01052         }
01053         else
01054         {
01055             # let caller know it is not time to launch another task
01056             return FALSE;
01057         }
01058     }
01059 
01060     private function LaunchTSR()
01061     {
01062         # set needed headers and
01063         ignore_user_abort(TRUE);
01064         header("Connection: close");
01065         header("Content-Length: ".ob_get_length());
01066 
01067         # output buffered content
01068         ob_end_flush();
01069         flush();
01070 
01071         # write out any outstanding data and end HTTP session
01072         session_write_close();
01073 
01074         # if there is still a task in the queue
01075         if ($this->GetTaskQueueSize())
01076         {
01077             # turn on output buffering to (hopefully) record any crash output
01078             ob_start();
01079 
01080             # lock tables and grab last task run time to double check
01081             $this->DB->Query("LOCK TABLES ApplicationFrameworkSettings WRITE");
01082             $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
01083             $this->Settings = $this->DB->FetchRow();
01084 
01085             # if still time to launch another task
01086             if (time() > (strtotime($this->Settings["LastTaskRunAt"])
01087                         + (ini_get("max_execution_time") 
01088                                 / $this->Settings["MaxTasksRunning"]) + 5))
01089             {
01090                 # update the "last run" time and release tables
01091                 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
01092                         ." SET LastTaskRunAt = '".date("Y-m-d H:i:s")."'");
01093                 $this->DB->Query("UNLOCK TABLES");
01094 
01095                 # run tasks while there is a task in the queue and enough time left
01096                 do
01097                 {
01098                     # run the next task
01099                     $this->RunNextTask();
01100                 }
01101                 while ($this->GetTaskQueueSize() 
01102                         && ($this->GetSecondsBeforeTimeout() > 65));
01103             }
01104             else
01105             {
01106                 # release tables
01107                 $this->DB->Query("UNLOCK TABLES");
01108             }
01109         }
01110     }
01111 
01119     private function GetTaskList($DBQuery, $Count, $Offset)
01120     {
01121         $this->DB->Query($DBQuery." LIMIT ".intval($Offset).",".intval($Count));
01122         $Tasks = array();
01123         while ($Row = $this->DB->FetchRow())
01124         {
01125             $Tasks[$Row["TaskId"]] = $Row;
01126             if ($Row["Callback"] ==
01127                     serialize(array("ApplicationFramework", "PeriodicEventWrapper")))
01128             {
01129                 $WrappedCallback = unserialize($Row["Parameters"]);
01130                 $Tasks[$Row["TaskId"]]["Callback"] = $WrappedCallback[1];
01131                 $Tasks[$Row["TaskId"]]["Parameters"] = NULL;
01132             }
01133             else
01134             {
01135                 $Tasks[$Row["TaskId"]]["Callback"] = unserialize($Row["Callback"]);
01136                 $Tasks[$Row["TaskId"]]["Parameters"] = unserialize($Row["Parameters"]);
01137             }
01138         }
01139         return $Tasks;
01140     }
01141 
01145     private function RunNextTask()
01146     {
01147         # look for task at head of queue
01148         $this->DB->Query("SELECT * FROM TaskQueue ORDER BY Priority, TaskId LIMIT 1");
01149         $Task = $this->DB->FetchRow();
01150 
01151         # if there was a task available
01152         if ($Task)
01153         {
01154             # move task from queue to running tasks list
01155             $this->DB->Query("INSERT INTO RunningTasks "
01156                              ."(TaskId,Callback,Parameters,Priority,Description) "
01157                              ."SELECT * FROM TaskQueue WHERE TaskId = "
01158                                     .intval($Task["TaskId"]));
01159             $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = "
01160                     .intval($Task["TaskId"]));
01161 
01162             # unpack stored task info
01163             $Callback = unserialize($Task["Callback"]);
01164             $Parameters = unserialize($Task["Parameters"]);
01165 
01166             # attempt to load task callback if not already available
01167             $this->LoadFunction($Callback);
01168 
01169             # run task
01170             $this->RunningTask = $Task;
01171             call_user_func_array($Callback, $Parameters);
01172             unset($this->RunningTask);
01173 
01174             # remove task from running tasks list
01175             $this->DB->Query("DELETE FROM RunningTasks"
01176                     ." WHERE TaskId = ".intval($Task["TaskId"]));
01177 
01178             # prune running tasks list if necessary
01179             $RunningTaskCount = $this->DB->Query(
01180                     "SELECT COUNT(*) AS TaskCount FROM RunningTasks", "TaskCount");
01181             if ($RunningTasksCount > $this->MaxRunningTasksToTrack)
01182             {
01183                 $this->DB->Query("DELETE FROM RunningTasks ORDER BY StartedAt"
01184                         ." LIMIT ".($RunningTasksCount - $this->MaxRunningTasksToTrack));
01185             }
01186         }
01187     }
01188 
01195     function OnCrash()
01196     {
01197         if (isset($this->RunningTask))
01198         {
01199             if (function_exists("error_get_last"))
01200             {
01201                 $CrashInfo["LastError"] = error_get_last();
01202             }
01203             if (ob_get_length() !== FALSE)
01204             {
01205                 $CrashInfo["OutputBuffer"] = ob_get_contents();
01206             }
01207             if (isset($CrashInfo))
01208             {
01209                 $DB = new Database();
01210                 $DB->Query("UPDATE RunningTasks SET CrashInfo = '"
01211                         .addslashes(serialize($CrashInfo))
01212                         ."' WHERE TaskId = ".intval($this->RunningTask["TaskId"]));
01213             }
01214         }
01215 
01216         print("\n");
01217         return;
01218 
01219         if (ob_get_length() !== FALSE)
01220         {
01221             ?>
01222             <table width="100%" cellpadding="5" style="border: 2px solid #666666;  background: #FFCCCC;  font-family: Courier New, Courier, monospace;  margin-top: 10px;  font-weight: bold;"><tr><td>
01223             <div style="font-size: 200%;">CRASH OUTPUT</div><?PHP
01224             ob_end_flush();
01225             ?></td></tr></table><?PHP
01226         }
01227     }
01230     private $CommonTemplateList = array(
01231             "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01232             "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01233             "local/interface/%ACTIVEUI%/include/%PAGENAME%.tpl",
01234             "local/interface/%ACTIVEUI%/include/%PAGENAME%.html",
01235             "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01236             "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01237             "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01238             "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01239             "local/interface/%ACTIVEUI%/include/%PAGENAME%",
01240             "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01241             "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01242             "interface/%ACTIVEUI%/include/%PAGENAME%.tpl",
01243             "interface/%ACTIVEUI%/include/%PAGENAME%.html",
01244             "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01245             "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01246             "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01247             "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01248             "interface/%ACTIVEUI%/include/%PAGENAME%",
01249             "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01250             "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01251             "SPTUI--%ACTIVEUI%/include/%PAGENAME%.tpl",
01252             "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html",
01253             "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01254             "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01255             "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01256             "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01257             "SPTUI--%ACTIVEUI%/include/%PAGENAME%",
01258             "%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
01259             "%ACTIVEUI%/include/StdPage%PAGENAME%.html",
01260             "%ACTIVEUI%/include/%PAGENAME%.tpl",
01261             "%ACTIVEUI%/include/%PAGENAME%.html",
01262             "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
01263             "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
01264             "%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
01265             "%ACTIVEUI%/include/SPT--%PAGENAME%.html",
01266             "%ACTIVEUI%/include/%PAGENAME%",
01267             "local/interface/default/include/StdPage%PAGENAME%.tpl",
01268             "local/interface/default/include/StdPage%PAGENAME%.html",
01269             "local/interface/default/include/%PAGENAME%.tpl",
01270             "local/interface/default/include/%PAGENAME%.html",
01271             "local/interface/default/include/SPT--StandardPage%PAGENAME%.tpl",
01272             "local/interface/default/include/SPT--StandardPage%PAGENAME%.html",
01273             "local/interface/default/include/SPT--%PAGENAME%.tpl",
01274             "local/interface/default/include/SPT--%PAGENAME%.html",
01275             "local/interface/default/include/%PAGENAME%",
01276             "interface/default/include/StdPage%PAGENAME%.tpl",
01277             "interface/default/include/StdPage%PAGENAME%.html",
01278             "interface/default/include/%PAGENAME%.tpl",
01279             "interface/default/include/%PAGENAME%.html",
01280             "interface/default/include/SPT--StandardPage%PAGENAME%.tpl",
01281             "interface/default/include/SPT--StandardPage%PAGENAME%.html",
01282             "interface/default/include/SPT--%PAGENAME%.tpl",
01283             "interface/default/include/SPT--%PAGENAME%.html",
01284             "interface/default/include/%PAGENAME%",
01285             );
01286     private $ContentTemplateList = array(
01287             "local/interface/%ACTIVEUI%/%PAGENAME%.tpl",
01288             "local/interface/%ACTIVEUI%/%PAGENAME%.html",
01289             "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01290             "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.html",
01291             "interface/%ACTIVEUI%/%PAGENAME%.tpl",
01292             "interface/%ACTIVEUI%/%PAGENAME%.html",
01293             "interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01294             "interface/%ACTIVEUI%/SPT--%PAGENAME%.html",
01295             "SPTUI--%ACTIVEUI%/%PAGENAME%.tpl",
01296             "SPTUI--%ACTIVEUI%/%PAGENAME%.html",
01297             "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01298             "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.html",
01299             "%ACTIVEUI%/%PAGENAME%.tpl",
01300             "%ACTIVEUI%/%PAGENAME%.html",
01301             "%ACTIVEUI%/SPT--%PAGENAME%.tpl",
01302             "%ACTIVEUI%/SPT--%PAGENAME%.html",
01303             "local/interface/default/%PAGENAME%.tpl",
01304             "local/interface/default/%PAGENAME%.html",
01305             "local/interface/default/SPT--%PAGENAME%.tpl",
01306             "local/interface/default/SPT--%PAGENAME%.html",
01307             "interface/default/%PAGENAME%.tpl",
01308             "interface/default/%PAGENAME%.html",
01309             "interface/default/SPT--%PAGENAME%.tpl",
01310             "interface/default/SPT--%PAGENAME%.html",
01311             );
01312     private $ImageFileList = array(
01313             "local/interface/%ACTIVEUI%/images/%PAGENAME%",
01314             "interface/%ACTIVEUI%/images/%PAGENAME%",
01315             "SPTUI--%ACTIVEUI%/images/%PAGENAME%",
01316             "%ACTIVEUI%/images/%PAGENAME%",
01317             "local/interface/default/images/%PAGENAME%",
01318             "interface/default/images/%PAGENAME%",
01319             );
01320     private $FunctionFileList = array(
01321             "local/interface/%ACTIVEUI%/include/%PAGENAME%.php",
01322             "local/interface/%ACTIVEUI%/include/%PAGENAME%.html",
01323             "interface/%ACTIVEUI%/include/%PAGENAME%.php",
01324             "interface/%ACTIVEUI%/include/%PAGENAME%.html",
01325             "SPTUI--%ACTIVEUI%/include/%PAGENAME%.php",
01326             "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html",
01327             "%ACTIVEUI%/include/%PAGENAME%.php",
01328             "%ACTIVEUI%/include/%PAGENAME%.html",
01329             "local/interface/default/include/%PAGENAME%.php",
01330             "local/interface/default/include/%PAGENAME%.html",
01331             "local/include/%PAGENAME%.php",
01332             "local/include/%PAGENAME%.html",
01333             "interface/default/include/%PAGENAME%.php",
01334             "interface/default/include/%PAGENAME%.html",
01335             "include/%PAGENAME%.php",
01336             "include/%PAGENAME%.html",
01337             );
01338 
01340     const NOVALUE = ".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-.";
01342 };
01343 
01344 
01345 ?>