Search:

# CWIS Developers Documentation

- Main Page
- Classes
- Files

- Search for |

- File List
- File Members

# 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 ?>