Search:

# CWIS Developers Documentation

- Main Page
- Classes
- Files

- 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             # load UI functions
    00162             $this->LoadUIFunctions();
    00163
    00164             # begin buffering content
    00165             ob_start();
    00166
    00167             # signal HTML file load
    00168             $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD", array(
    00169                     "PageName" => $PageName));
    00170
    00171             # if signal handler returned new page name value
    00172             $NewPageName = $PageName;
    00173             $HtmlFile = NULL;
    00174             if (($SignalResult["PageName"] != $PageName)
    00175                     && strlen($SignalResult["PageName"]))
    00176             {
    00177                 # if new page name value is HTML file
    00178                 if (file_exists($SignalResult["PageName"]))
    00179                 {
    00180                     # use new value for HTML file name
    00181                     $HtmlFile = $SignalResult["PageName"];
    00182                 }
    00183                 else
    00184                 {
    00185                     # use new value for page name
    00186                     $NewPageName = $SignalResult["PageName"];
    00187                 }
    00188             }
    00189
    00190             # load page content HTML file if available
    00191             if ($HtmlFile === NULL)
    00192             {
    00193                 $HtmlFile = $this->FindTemplate($this->ContentTemplateList, $NewPageName);
    00194             }
    00195             if ($HtmlFile)
    00196             {
    00197                 include($HtmlFile);
    00198             }
    00199             else
    00200             {
    00201                 print("<h2>ERROR:  No HTML/TPL template found"
    00202                         ." for this page.</h2>");
    00203             }
    00204
    00205             # signal HTML file load complete
    00206             $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD_COMPLETE");
    00207
    00208             # stop buffering and save content
    00209             $BufferedContent = ob_get_contents();
    00210             ob_end_clean();
    00211
    00212             # load page start HTML file if available
    00213             $HtmlFile = $this->FindCommonTemplate("Start");
    00214             if ($HtmlFile) {  include($HtmlFile);  }
    00215
    00216             # write out page content
    00217             print($BufferedContent);
    00218
    00219             # load page end HTML file if available
    00220             $HtmlFile = $this->FindCommonTemplate("End");
    00221             if ($HtmlFile) {  include($HtmlFile);  }
    00222         }
    00223
    00224         # run any post-processing routines
    00225         foreach ($this->PostProcessingFuncs as $Func)
    00226         {
    00227             call_user_func_array($Func["FunctionName"], $Func["Arguments"]);
    00228         }
    00229
    00230         # write out any output buffered from page code execution
    00231         if (strlen($PageOutput))
    00232         {
    00233             if (!$this->SuppressHTML)
    00234             {
    00235                 ?><table width="100%" cellpadding="5"
    00236                         style="border: 2px solid #666666;  background: #CCCCCC;
    00237                         font-family: Courier New, Courier, monospace;
    00238                         margin-top: 10px;"><tr><td><?PHP
    00239             }
    00240             if ($this->JumpToPage)
    00241             {
    00242                 ?><div style="color: #666666;"><span style="font-size: 150%;">
    00243                 <b>Page Jump Aborted</b></span>
    00244                 (because of error or other unexpected output)<br />
    00245                 <b>Jump Target:</b>
    00246                 <i><?PHP  print($this->JumpToPage);  ?></i></div><?PHP
    00247             }
    00248             print($PageOutput);
    00249             if (!$this->SuppressHTML)
    00250             {
    00251                 ?></td></tr></table><?PHP
    00252             }
    00253         }
    00254
    00255         # terminate and stay resident (TSR!) if indicated and HTML has been output
    00256         # (only TSR if HTML has been output because otherwise browsers will misbehave)
    00257         if ($ShouldTSR) {  $this->LaunchTSR();  }
    00258     }
    00259
    00266     function SetJumpToPage($Page)
    00267     {
    00268         if ((strpos($Page, "?") === FALSE)
    00269                 && ((strpos($Page, "=") !== FALSE)
    00270                     || ((strpos($Page, ".php") === FALSE)
    00271                         && (strpos($Page, ".htm") === FALSE)
    00272                         && (strpos($Page, "/") === FALSE))))
    00273         {
    00274             $this->JumpToPage = "index.php?P=".$Page;
    00275         }
    00276         else
    00277         {
    00278             $this->JumpToPage = $Page;
    00279         }
    00280     }
    00281
    00286     function JumpToPageIsSet()
    00287     {
    00288         return ($this->JumpToPage === NULL) ? FALSE : TRUE;
    00289     }
    00290
    00300     function HtmlCharset($NewSetting = NULL)
    00301     {
    00302         if ($NewSetting !== NULL) {  $this->HtmlCharset = $NewSetting;  }
    00303         return $this->HtmlCharset;
    00304     }
    00305
    00312     function SuppressHTMLOutput($NewSetting = TRUE)
    00313     {
    00314         $this->SuppressHTML = $NewSetting;
    00315     }
    00316
    00323     function ActiveUserInterface($UIName = NULL)
    00324     {
    00325         if ($UIName !== NULL)
    00326         {
    00327             $this->ActiveUI = preg_replace("/^SPTUI--/", "", $UIName);
    00328         }
    00329         return $this->ActiveUI;
    00330     }
    00331
    00347     function AddPostProcessingCall($FunctionName,
    00348             &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE,
    00349             &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE,
    00350             &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE)
    00351     {
    00352         $FuncIndex = count($this->PostProcessingFuncs);
    00353         $this->PostProcessingFuncs[$FuncIndex]["FunctionName"] = $FunctionName;
    00354         $this->PostProcessingFuncs[$FuncIndex]["Arguments"] = array();
    00355         $Index = 1;
    00356         while (isset(${"Arg".$Index}) && (${"Arg".$Index} !== self::NOVALUE))
    00357         {
    00358             $this->PostProcessingFuncs[$FuncIndex]["Arguments"][$Index]
    00359                     =& ${"Arg".$Index};
    00360             $Index++;
    00361         }
    00362     }
    00363
    00369     function AddEnvInclude($FileName)
    00370     {
    00371         $this->EnvIncludes[] = $FileName;
    00372     }
    00373
    00379     function GUIFile($FileName)
    00380     {
    00381         # pull off file name suffix
    00382         $NamePieces = explode(".", $FileName);
    00383         $Suffix = strtolower(array_pop($NamePieces));
    00384
    00385         # determine which location to search based on file suffix
    00386         $ImageSuffixes = array("gif", "jpg", "png");
    00387         $FileList = in_array($Suffix, $ImageSuffixes)
    00388                 ? $this->ImageFileList : $this->CommonTemplateList;
    00389
    00390         # search for file and return result to caller
    00391         return $this->FindTemplate($FileList, $FileName);
    00392     }
    00393
    00403     function PUIFile($FileName)
    00404     {
    00405         $FullFileName = $this->GUIFile($FileName);
    00406         if ($FullFileName) {  print($FullFileName);  }
    00407     }
    00408
    00413     function FindCommonTemplate($PageName)
    00414     {
    00415         return $this->FindTemplate(
    00416                 array_merge($this->CommonTemplateList, $this->ContentTemplateList),
    00417                 $PageName);
    00418     }
    00419
    00428     function LoadFunction($Callback)
    00429     {
    00430         if (!is_callable($Callback) && is_string($Callback))
    00431         {
    00432             $Locations = $this->FunctionFileList;
    00433             foreach (self::$ObjectDirectories as $Location => $Prefix)
    00434             {
    00435                 $Locations[] = $Location."%PAGENAME%.php";
    00436                 $Locations[] = $Location."%PAGENAME%.html";
    00437             }
    00438             $FunctionFileName = $this->FindTemplate($Locations, "F-".$Callback);
    00439             if ($FunctionFileName)
    00440             {
    00441                 include_once($FunctionFileName);
    00442             }
    00443         }
    00444         return is_callable($Callback);
    00445     }
    00446
    00451     function GetElapsedExecutionTime()
    00452     {
    00453         return microtime(TRUE) - $this->ExecutionStartTime;
    00454     }
    00455
    00460     function GetSecondsBeforeTimeout()
    00461     {
    00462         return ini_get("max_execution_time") - $this->GetElapsedExecutionTime();
    00463     }
    00464
    00465     /*@)*/ /* Application Framework */
    00466
    00467     # ---- Event Handling ----------------------------------------------------
    00468  /*@(*/
    00470
    00474     const EVENTTYPE_DEFAULT = 1;
    00480     const EVENTTYPE_CHAIN = 2;
    00486     const EVENTTYPE_FIRST = 3;
    00494     const EVENTTYPE_NAMED = 4;
    00495
    00497     const ORDER_FIRST = 1;
    00499     const ORDER_MIDDLE = 2;
    00501     const ORDER_LAST = 3;
    00502
    00511     function RegisterEvent($EventsOrEventName, $EventType = NULL)
    00512     {
    00513         # convert parameters to array if not already in that form
    00514         $Events = is_array($EventsOrEventName) ? $EventsOrEventName
    00515                 : array($EventsOrEventName => $Type);
    00516
    00517         # for each event
    00518         foreach ($Events as $Name => $Type)
    00519         {
    00520             # store event information
    00521             $this->RegisteredEvents[$Name]["Type"] = $Type;
    00522             $this->RegisteredEvents[$Name]["Hooks"] = array();
    00523         }
    00524     }
    00525
    00539     function HookEvent($EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
    00540     {
    00541         # convert parameters to array if not already in that form
    00542         $Events = is_array($EventsOrEventName) ? $EventsOrEventName
    00543                 : array($EventsOrEventName => $Callback);
    00544
    00545         # for each event
    00546         $Success = TRUE;
    00547         foreach ($Events as $EventName => $EventCallback)
    00548         {
    00549             # if callback is valid
    00550             if (is_callable($EventCallback))
    00551             {
    00552                 # if this is a periodic event we process internally
    00553                 if (isset($this->PeriodicEvents[$EventName]))
    00554                 {
    00555                     # process event now
    00556                     $this->ProcessPeriodicEvent($EventName, $EventCallback);
    00557                 }
    00558                 # if specified event has been registered
    00559                 elseif (isset($this->RegisteredEvents[$EventName]))
    00560                 {
    00561                     # add callback for event
    00562                     $this->RegisteredEvents[$EventName]["Hooks"][]
    00563                             = array("Callback" => $EventCallback, "Order" => $Order);
    00564
    00565                     # sort callbacks by order
    00566                     if (count($this->RegisteredEvents[$EventName]["Hooks"]) > 1)
    00567                     {
    00568                         usort($this->RegisteredEvents[$EventName]["Hooks"],
    00569                                 array("ApplicationFramework", "HookEvent_OrderCompare"));
    00570                     }
    00571                 }
    00572                 else
    00573                 {
    00574                     $Success = FALSE;
    00575                 }
    00576             }
    00577             else
    00578             {
    00579                 $Success = FALSE;
    00580             }
    00581         }
    00582
    00583         # report to caller whether all callbacks were hooked
    00584         return $Success;
    00585     }
    00586     private static function HookEvent_OrderCompare($A, $B)
    00587     {
    00588         if ($A["Order"] == $B["Order"]) {  return 0;  }
    00589         return ($A["Order"] < $B["Order"]) ? -1 : 1;
    00590     }
    00591
    00600     function SignalEvent($EventName, $Parameters = NULL)
    00601     {
    00602         $ReturnValue = NULL;
    00603
    00604         # if event has been registered
    00605         if (isset($this->RegisteredEvents[$EventName]))
    00606         {
    00607             # set up default return value (if not NULL)
    00608             switch ($this->RegisteredEvents[$EventName]["Type"])
    00609             {
    00610                 case self::EVENTTYPE_CHAIN:
    00611                     $ReturnValue = $Parameters;
    00612                     break;
    00613
    00614                 case self::EVENTTYPE_NAMED:
    00615                     $ReturnValue = array();
    00616                     break;
    00617             }
    00618
    00619             # for each callback for this event
    00620             foreach ($this->RegisteredEvents[$EventName]["Hooks"] as $Hook)
    00621             {
    00622                 # invoke callback
    00623                 $Callback = $Hook["Callback"];
    00624                 $Result = ($Parameters !== NULL)
    00625                         ? call_user_func_array($Callback, $Parameters)
    00626                         : call_user_func($Callback);
    00627
    00628                 # process return value based on event type
    00629                 switch ($this->RegisteredEvents[$EventName]["Type"])
    00630                 {
    00631                     case self::EVENTTYPE_CHAIN:
    00632                         $ReturnValue = $Result;
    00633                         $Parameters = $Result;
    00634                         break;
    00635
    00636                     case self::EVENTTYPE_FIRST:
    00637                         if ($Result !== NULL)
    00638                         {
    00639                             $ReturnValue = $Result;
    00640                             break 2;
    00641                         }
    00642                         break;
    00643
    00644                     case self::EVENTTYPE_NAMED:
    00645                         $CallbackName = is_array($Callback)
    00646                                 ? (is_object($Callback[0])
    00647                                         ? get_class($Callback[0])
    00648                                         : $Callback[0])."::".$Callback[1]
    00649                                 : $Callback;
    00650                         $ReturnValue[$CallbackName] = $Result;
    00651                         break;
    00652
    00653                     default:
    00654                         break;
    00655                 }
    00656             }
    00657         }
    00658
    00659         # return value if any to caller
    00660         return $ReturnValue;
    00661     }
    00662
    00668     function IsStaticOnlyEvent($EventName)
    00669     {
    00670         return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE;
    00671     }
    00672
    00673     /*@)*/ /* Event Handling */
    00674
    00675     # ---- Task Management ---------------------------------------------------
    00676  /*@(*/
    00678
    00680     const PRIORITY_HIGH = 1;
    00682     const PRIORITY_MEDIUM = 2;
    00684     const PRIORITY_LOW = 3;
    00686     const PRIORITY_BACKGROUND = 4;
    00687
    00700     function QueueTask($Callback, $Parameters = NULL,
    00701             $Priority = self::PRIORITY_MEDIUM, $Description = "")
    00702     {
    00703         # pack task info and write to database
    00704         if ($Parameters === NULL) {  $Parameters = array();  }
    00705         $this->DB->Query("INSERT INTO TaskQueue"
    00706                 ." (Callback, Parameters, Priority, Description)"
    00707                 ." VALUES ('".addslashes(serialize($Callback))."', '"
    00708                 .addslashes(serialize($Parameters))."', ".intval($Priority).", '"
    00709                 .addslashes($Description)."')");
    00710     }
    00711
    00729     function QueueUniqueTask($Callback, $Parameters = NULL,
    00730             $Priority = self::PRIORITY_MEDIUM, $Description = "")
    00731     {
    00732         if ($this->TaskIsInQueue($Callback, $Parameters))
    00733         {
    00734             $QueryResult = $this->DB->Query("SELECT TaskId,Priority FROM TaskQueue"
    00735                     ." WHERE Callback = '".addslashes(serialize($Callback))."'"
    00736                     .($Parameters ? " AND Parameters = '"
    00737                             .addslashes(serialize($Parameters))."'" : ""));
    00738             if ($QueryResult !== FALSE)
    00739             {
    00740                 $Record = $this->DB->FetchRow();
    00741                 if ($Record["Priority"] > $Priority)
    00742                 {
    00743                     $this->DB->Query("UPDATE TaskQueue"
    00744                             ." SET Priority = ".intval($Priority)
    00745                             ." WHERE TaskId = ".intval($Record["TaskId"]));
    00746                 }
    00747             }
    00748             return FALSE;
    00749         }
    00750         else
    00751         {
    00752             $this->QueueTask($Callback, $Parameters, $Priority, $Description);
    00753             return TRUE;
    00754         }
    00755     }
    00756
    00766     function TaskIsInQueue($Callback, $Parameters = NULL)
    00767     {
    00768         $FoundCount = $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM TaskQueue"
    00769                 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
    00770                 .($Parameters ? " AND Parameters = '"
    00771                         .addslashes(serialize($Parameters))."'" : ""),
    00772                 "FoundCount")
    00773                 + $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM RunningTasks"
    00774                 ." WHERE Callback = '".addslashes(serialize($Callback))."'"
    00775                 .($Parameters ? " AND Parameters = '"
    00776                         .addslashes(serialize($Parameters))."'" : ""),
    00777                 "FoundCount");
    00778         return ($FoundCount ? TRUE : FALSE);
    00779     }
    00780
    00786     function GetTaskQueueSize($Priority = NULL)
    00787     {
    00788         return $this->DB->Query("SELECT COUNT(*) AS QueueSize FROM TaskQueue"
    00789                 .($Priority ? " WHERE Priority = ".intval($Priority) : ""),
    00790                 "QueueSize");
    00791     }
    00792
    00800     function GetQueuedTaskList($Count = 100, $Offset = 0)
    00801     {
    00802         return $this->GetTaskList("SELECT * FROM TaskQueue"
    00803                 ." ORDER BY Priority, TaskId ", $Count, $Offset);
    00804     }
    00805
    00813     function GetRunningTaskList($Count = 100, $Offset = 0)
    00814     {
    00815         return $this->GetTaskList("SELECT * FROM RunningTasks"
    00816                 ." WHERE StartedAt >= '".date("Y-m-d H:i:s",
    00817                         (time() - ini_get("max_execution_time")))."'"
    00818                 ." ORDER BY StartedAt", $Count, $Offset);
    00819     }
    00820
    00828     function GetOrphanedTaskList($Count = 100, $Offset = 0)
    00829     {
    00830         return $this->GetTaskList("SELECT * FROM RunningTasks"
    00831                 ." WHERE StartedAt < '".date("Y-m-d H:i:s",
    00832                         (time() - ini_get("max_execution_time")))."'"
    00833                 ." ORDER BY StartedAt", $Count, $Offset);
    00834     }
    00835
    00840     function ReQueueOrphanedTask($TaskId)
    00841     {
    00842         $this->DB->Query("LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
    00843         $this->DB->Query("INSERT INTO TaskQueue"
    00844                          ." (Callback,Parameters,Priority,Description) "
    00845                          ."SELECT Callback, Parameters, Priority, Description"
    00846                          ." FROM RunningTasks WHERE TaskId = ".intval($TaskId));
    00847         $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
    00848         $this->DB->Query("UNLOCK TABLES");
    00849     }
    00850
    00855     function DeleteOrphanedTask($TaskId)
    00856     {
    00857         $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
    00858     }
    00859
    00865     function MaxTasks($NewValue = NULL)
    00866     {
    00867         if (func_num_args() && ($NewValue >= 1))
    00868         {
    00869             $this->DB->Query("UPDATE ApplicationFrameworkSettings"
    00870                     ." SET MaxTasksRunning = '".intval($NewValue)."'");
    00871             $this->Settings["MaxTasksRunning"] = intval($NewValue);
    00872         }
    00873         return $this->Settings["MaxTasksRunning"];
    00874     }
    00875
    00883     function MaxExecutionTime($NewValue = NULL)
    00884     {
    00885         if (func_num_args())
    00886         {
    00887             if ($NewValue != $this->Settings["MaxExecTime"])
    00888             {
    00889                 $this->Settings["MaxExecTime"] = max($NewValue, 5);
    00890                 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
    00891                         ." SET MaxExecTime = '"
    00892                                 .intval($this->Settings["MaxExecTime"])."'");
    00893             }
    00894             ini_set("max_execution_time", $this->Settings["MaxExecTime"]);
    00895             set_time_limit($this->Settings["MaxExecTime"]);
    00896         }
    00897         return ini_get("max_execution_time");
    00898     }
    00899
    00900     /*@)*/ /* Task Management */
    00901
    00902     # ---- PRIVATE INTERFACE -------------------------------------------------
    00903
    00904     private $JumpToPage = NULL;
    00905     private $SuppressHTML = FALSE;
    00906     private $DefaultPage = "Home";
    00907     private $ActiveUI = "default";
    00908     private $HtmlCharset = "UTF-8";
    00909     private $PostProcessingFuncs = array();
    00910     private $EnvIncludes = array();
    00911     private $DB;
    00912     private $Settings;
    00913     private $ExecutionStartTime;
    00914     private static $ObjectDirectories = array();
    00915     private $MaxRunningTasksToTrack = 250;
    00916     private $RunningTask;
    00917
    00918     private $PeriodicEvents = array(
    00919                 "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT,
    00920                 "EVENT_DAILY" => self::EVENTTYPE_DEFAULT,
    00921                 "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT,
    00922                 "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT,
    00923                 "EVENT_PERIODIC" => self::EVENTTYPE_NAMED,
    00924                 );
    00925     private $UIEvents = array(
    00926                 "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT,
    00927                 "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN,
    00928                 "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN,
    00929                 "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
    00930                 );
    00931
    00932     private function FindTemplate($FileList, $PageName)
    00933     {
    00934         $FileNameFound = NULL;
    00935         foreach ($FileList as $FileName)
    00936         {
    00937             $FileName = str_replace("%ACTIVEUI%", $this->ActiveUI, $FileName);
    00938             $FileName = str_replace("%PAGENAME%", $PageName, $FileName);
    00939             if (file_exists($FileName))
    00940             {
    00941                 $FileNameFound = $FileName;
    00942                 break;
    00943             }
    00944         }
    00945         return $FileNameFound;
    00946     }
    00947
    00948     private function SetUpObjectAutoloading()
    00949     {
    00950         function __autoload($ClassName)
    00951         {
    00952             ApplicationFramework::AutoloadObjects($ClassName);
    00953         }
    00954     }
    00955
    00957     static function AutoloadObjects($ClassName)
    00958     {
    00959         foreach (self::$ObjectDirectories as $Location => $Prefix)
    00960         {
    00961             $FileName = $Location.$Prefix.$ClassName.".php";
    00962             if (file_exists($FileName))
    00963             {
    00964                 require_once($FileName);
    00965                 break;
    00966             }
    00967         }
    00968     }
    00971     private function LoadUIFunctions()
    00972     {
    00973         $Dirs = array(
    00974                 "local/interface/%ACTIVEUI%/include",
    00975                 "interface/%ACTIVEUI%/include",
    00976                 "local/interface/default/include",
    00977                 "interface/default/include",
    00978                 );
    00979         foreach ($Dirs as $Dir)
    00980         {
    00981             $Dir = str_replace("%ACTIVEUI%", $this->ActiveUI, $Dir);
    00982             if (is_dir($Dir))
    00983             {
    00984                 $FileNames = scandir($Dir);
    00985                 foreach ($FileNames as $FileName)
    00986                 {
    00987                     if (preg_match("/^F-([A-Za-z_]+)\.php/", $FileName, $Matches)
    00988                             || preg_match("/^F-([A-Za-z_]+)\.html/", $FileName, $Matches))
    00989                     {
    00990                         if (!function_exists($Matches[1]))
    00991                         {
    00992                             include_once($Dir."/".$FileName);
    00993                         }
    00994                     }
    00995                 }
    00996             }
    00997         }
    00998     }
    00999
    01000     private function ProcessPeriodicEvent($EventName, $Callback)
    01001     {
    01002         # retrieve last execution time for event if available
    01003         $Signature = self::GetCallbackSignature($Callback);
    01004         $LastRun = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
    01005                 ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
    01006
    01007         # determine whether enough time has passed for event to execute
    01008         $EventPeriods = array(
    01009                 "EVENT_HOURLY" => 60*60,
    01010                 "EVENT_DAILY" => 60*60*24,
    01011                 "EVENT_WEEKLY" => 60*60*24*7,
    01012                 "EVENT_MONTHLY" => 60*60*24*30,
    01013                 "EVENT_PERIODIC" => 0,
    01014                 );
    01015         $ShouldExecute = (($LastRun === NULL)
    01016                 || (time() > (strtotime($LastRun) + $EventPeriods[$EventName])))
    01017                 ? TRUE : FALSE;
    01018
    01019         # if event should run
    01020         if ($ShouldExecute)
    01021         {
    01022             # add event to task queue
    01023             $WrapperCallback = array("ApplicationFramework", "PeriodicEventWrapper");
    01024             $WrapperParameters = array(
    01025                     $EventName, $Callback, array("LastRunAt" => $LastRun));
    01026             $this->QueueUniqueTask($WrapperCallback, $WrapperParameters);
    01027         }
    01028     }
    01029
    01030     private static function PeriodicEventWrapper($EventName, $Callback, $Parameters)
    01031     {
    01032         static $DB;
    01033         if (!isset($DB)) {  $DB = new Database();  }
    01034
    01035         # run event
    01036         $ReturnVal = call_user_func_array($Callback, $Parameters);
    01037
    01038         # if event is already in database
    01039         $Signature = self::GetCallbackSignature($Callback);
    01040         if ($DB->Query("SELECT COUNT(*) AS EventCount FROM PeriodicEvents"
    01041                 ." WHERE Signature = '".addslashes($Signature)."'", "EventCount"))
    01042         {
    01043             # update last run time for event
    01044             $DB->Query("UPDATE PeriodicEvents SET LastRunAt = "
    01045                     .(($EventName == "EVENT_PERIODIC")
    01046                             ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
    01047                             : "NOW()")
    01048                     ." WHERE Signature = '".addslashes($Signature)."'");
    01049         }
    01050         else
    01051         {
    01052             # add last run time for event to database
    01053             $DB->Query("INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES "
    01054                     ."('".addslashes($Signature)."', "
    01055                     .(($EventName == "EVENT_PERIODIC")
    01056                             ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
    01057                             : "NOW()").")");
    01058         }
    01059     }
    01060
    01061     private static function GetCallbackSignature($Callback)
    01062     {
    01063         return !is_array($Callback) ? $Callback
    01064                 : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0])
    01065                         ."::".$Callback[1];
    01066     }
    01067
    01068     private function PrepForTSR()
    01069     {
    01070         # if HTML has been output and it's time to launch another task
    01071         # (only TSR if HTML has been output because otherwise browsers
    01072         #       may misbehave after connection is closed)
    01073         if (($this->JumpToPage || !$this->SuppressHTML)
    01074                 && (time() > (strtotime($this->Settings["LastTaskRunAt"])
    01075                         + (ini_get("max_execution_time")
    01076                                 / $this->Settings["MaxTasksRunning"]) + 5))
    01077                 && $this->GetTaskQueueSize())
    01078         {
    01079             # begin buffering output for TSR
    01080             ob_start();
    01081
    01082             # let caller know it is time to launch another task
    01083             return TRUE;
    01084         }
    01085         else
    01086         {
    01087             # let caller know it is not time to launch another task
    01088             return FALSE;
    01089         }
    01090     }
    01091
    01092     private function LaunchTSR()
    01093     {
    01094         # set needed headers and
    01095         ignore_user_abort(TRUE);
    01096         header("Connection: close");
    01097         header("Content-Length: ".ob_get_length());
    01098
    01099         # output buffered content
    01100         ob_end_flush();
    01101         flush();
    01102
    01103         # write out any outstanding data and end HTTP session
    01104         session_write_close();
    01105
    01106         # if there is still a task in the queue
    01107         if ($this->GetTaskQueueSize())
    01108         {
    01109             # turn on output buffering to (hopefully) record any crash output
    01110             ob_start();
    01111
    01112             # lock tables and grab last task run time to double check
    01113             $this->DB->Query("LOCK TABLES ApplicationFrameworkSettings WRITE");
    01114             $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
    01115             $this->Settings = $this->DB->FetchRow();
    01116
    01117             # if still time to launch another task
    01118             if (time() > (strtotime($this->Settings["LastTaskRunAt"])
    01119                         + (ini_get("max_execution_time")
    01120                                 / $this->Settings["MaxTasksRunning"]) + 5))
    01121             {
    01122                 # update the "last run" time and release tables
    01123                 $this->DB->Query("UPDATE ApplicationFrameworkSettings"
    01124                         ." SET LastTaskRunAt = '".date("Y-m-d H:i:s")."'");
    01125                 $this->DB->Query("UNLOCK TABLES");
    01126
    01127                 # run tasks while there is a task in the queue and enough time left
    01128                 do
    01129                 {
    01130                     # run the next task
    01131                     $this->RunNextTask();
    01132                 }
    01133                 while ($this->GetTaskQueueSize()
    01134                         && ($this->GetSecondsBeforeTimeout() > 65));
    01135             }
    01136             else
    01137             {
    01138                 # release tables
    01139                 $this->DB->Query("UNLOCK TABLES");
    01140             }
    01141         }
    01142     }
    01143
    01151     private function GetTaskList($DBQuery, $Count, $Offset)
    01152     {
    01153         $this->DB->Query($DBQuery." LIMIT ".intval($Offset).",".intval($Count));
    01154         $Tasks = array();
    01155         while ($Row = $this->DB->FetchRow())
    01156         {
    01157             $Tasks[$Row["TaskId"]] = $Row;
    01158             if ($Row["Callback"] ==
    01159                     serialize(array("ApplicationFramework", "PeriodicEventWrapper")))
    01160             {
    01161                 $WrappedCallback = unserialize($Row["Parameters"]);
    01162                 $Tasks[$Row["TaskId"]]["Callback"] = $WrappedCallback[1];
    01163                 $Tasks[$Row["TaskId"]]["Parameters"] = NULL;
    01164             }
    01165             else
    01166             {
    01167                 $Tasks[$Row["TaskId"]]["Callback"] = unserialize($Row["Callback"]);
    01168                 $Tasks[$Row["TaskId"]]["Parameters"] = unserialize($Row["Parameters"]);
    01169             }
    01170         }
    01171         return $Tasks;
    01172     }
    01173
    01177     private function RunNextTask()
    01178     {
    01179         # look for task at head of queue
    01180         $this->DB->Query("SELECT * FROM TaskQueue ORDER BY Priority, TaskId LIMIT 1");
    01181         $Task = $this->DB->FetchRow();
    01182
    01183         # if there was a task available
    01184         if ($Task)
    01185         {
    01186             # move task from queue to running tasks list
    01187             $this->DB->Query("INSERT INTO RunningTasks "
    01188                              ."(TaskId,Callback,Parameters,Priority,Description) "
    01189                              ."SELECT * FROM TaskQueue WHERE TaskId = "
    01190                                     .intval($Task["TaskId"]));
    01191             $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = "
    01192                     .intval($Task["TaskId"]));
    01193
    01194             # unpack stored task info
    01195             $Callback = unserialize($Task["Callback"]);
    01196             $Parameters = unserialize($Task["Parameters"]);
    01197
    01198             # attempt to load task callback if not already available
    01199             $this->LoadFunction($Callback);
    01200
    01201             # run task
    01202             $this->RunningTask = $Task;
    01203             call_user_func_array($Callback, $Parameters);
    01204             unset($this->RunningTask);
    01205
    01206             # remove task from running tasks list
    01207             $this->DB->Query("DELETE FROM RunningTasks"
    01208                     ." WHERE TaskId = ".intval($Task["TaskId"]));
    01209
    01210             # prune running tasks list if necessary
    01211             $RunningTaskCount = $this->DB->Query(
    01212                     "SELECT COUNT(*) AS TaskCount FROM RunningTasks", "TaskCount");
    01213             if ($RunningTasksCount > $this->MaxRunningTasksToTrack)
    01214             {
    01215                 $this->DB->Query("DELETE FROM RunningTasks ORDER BY StartedAt"
    01216                         ." LIMIT ".($RunningTasksCount - $this->MaxRunningTasksToTrack));
    01217             }
    01218         }
    01219     }
    01220
    01226     function OnCrash()
    01227     {
    01228         if (isset($this->RunningTask))
    01229         {
    01230             if (function_exists("error_get_last"))
    01231             {
    01232                 $CrashInfo["LastError"] = error_get_last();
    01233             }
    01234             if (ob_get_length() !== FALSE)
    01235             {
    01236                 $CrashInfo["OutputBuffer"] = ob_get_contents();
    01237             }
    01238             if (isset($CrashInfo))
    01239             {
    01240                 $DB = new Database();
    01241                 $DB->Query("UPDATE RunningTasks SET CrashInfo = '"
    01242                         .addslashes(serialize($CrashInfo))
    01243                         ."' WHERE TaskId = ".intval($this->RunningTask["TaskId"]));
    01244             }
    01245         }
    01246
    01247         print("\n");
    01248         return;
    01249
    01250         if (ob_get_length() !== FALSE)
    01251         {
    01252             ?>
    01253             <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>
    01254             <div style="font-size: 200%;">CRASH OUTPUT</div><?PHP
    01255             ob_end_flush();
    01256             ?></td></tr></table><?PHP
    01257         }
    01258     }
    01259
    01260     private $CommonTemplateList = array(
    01261             "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
    01262             "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html",
    01263             "local/interface/%ACTIVEUI%/include/%PAGENAME%.tpl",
    01264             "local/interface/%ACTIVEUI%/include/%PAGENAME%.html",
    01265             "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
    01266             "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
    01267             "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
    01268             "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html",
    01269             "local/interface/%ACTIVEUI%/include/%PAGENAME%",
    01270             "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
    01271             "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html",
    01272             "interface/%ACTIVEUI%/include/%PAGENAME%.tpl",
    01273             "interface/%ACTIVEUI%/include/%PAGENAME%.html",
    01274             "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
    01275             "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
    01276             "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
    01277             "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html",
    01278             "interface/%ACTIVEUI%/include/%PAGENAME%",
    01279             "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
    01280             "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.html",
    01281             "SPTUI--%ACTIVEUI%/include/%PAGENAME%.tpl",
    01282             "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html",
    01283             "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
    01284             "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
    01285             "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
    01286             "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.html",
    01287             "SPTUI--%ACTIVEUI%/include/%PAGENAME%",
    01288             "%ACTIVEUI%/include/StdPage%PAGENAME%.tpl",
    01289             "%ACTIVEUI%/include/StdPage%PAGENAME%.html",
    01290             "%ACTIVEUI%/include/%PAGENAME%.tpl",
    01291             "%ACTIVEUI%/include/%PAGENAME%.html",
    01292             "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl",
    01293             "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html",
    01294             "%ACTIVEUI%/include/SPT--%PAGENAME%.tpl",
    01295             "%ACTIVEUI%/include/SPT--%PAGENAME%.html",
    01296             "%ACTIVEUI%/include/%PAGENAME%",
    01297             "local/interface/default/include/StdPage%PAGENAME%.tpl",
    01298             "local/interface/default/include/StdPage%PAGENAME%.html",
    01299             "local/interface/default/include/%PAGENAME%.tpl",
    01300             "local/interface/default/include/%PAGENAME%.html",
    01301             "local/interface/default/include/SPT--StandardPage%PAGENAME%.tpl",
    01302             "local/interface/default/include/SPT--StandardPage%PAGENAME%.html",
    01303             "local/interface/default/include/SPT--%PAGENAME%.tpl",
    01304             "local/interface/default/include/SPT--%PAGENAME%.html",
    01305             "local/interface/default/include/%PAGENAME%",
    01306             "interface/default/include/StdPage%PAGENAME%.tpl",
    01307             "interface/default/include/StdPage%PAGENAME%.html",
    01308             "interface/default/include/%PAGENAME%.tpl",
    01309             "interface/default/include/%PAGENAME%.html",
    01310             "interface/default/include/SPT--StandardPage%PAGENAME%.tpl",
    01311             "interface/default/include/SPT--StandardPage%PAGENAME%.html",
    01312             "interface/default/include/SPT--%PAGENAME%.tpl",
    01313             "interface/default/include/SPT--%PAGENAME%.html",
    01314             "interface/default/include/%PAGENAME%",
    01315             );
    01316     private $ContentTemplateList = array(
    01317             "local/interface/%ACTIVEUI%/%PAGENAME%.tpl",
    01318             "local/interface/%ACTIVEUI%/%PAGENAME%.html",
    01319             "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl",
    01320             "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.html",
    01321             "interface/%ACTIVEUI%/%PAGENAME%.tpl",
    01322             "interface/%ACTIVEUI%/%PAGENAME%.html",
    01323             "interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl",
    01324             "interface/%ACTIVEUI%/SPT--%PAGENAME%.html",
    01325             "SPTUI--%ACTIVEUI%/%PAGENAME%.tpl",
    01326             "SPTUI--%ACTIVEUI%/%PAGENAME%.html",
    01327             "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.tpl",
    01328             "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.html",
    01329             "%ACTIVEUI%/%PAGENAME%.tpl",
    01330             "%ACTIVEUI%/%PAGENAME%.html",
    01331             "%ACTIVEUI%/SPT--%PAGENAME%.tpl",
    01332             "%ACTIVEUI%/SPT--%PAGENAME%.html",
    01333             "local/interface/default/%PAGENAME%.tpl",
    01334             "local/interface/default/%PAGENAME%.html",
    01335             "local/interface/default/SPT--%PAGENAME%.tpl",
    01336             "local/interface/default/SPT--%PAGENAME%.html",
    01337             "interface/default/%PAGENAME%.tpl",
    01338             "interface/default/%PAGENAME%.html",
    01339             "interface/default/SPT--%PAGENAME%.tpl",
    01340             "interface/default/SPT--%PAGENAME%.html",
    01341             );
    01342     private $ImageFileList = array(
    01343             "local/interface/%ACTIVEUI%/images/%PAGENAME%",
    01344             "interface/%ACTIVEUI%/images/%PAGENAME%",
    01345             "SPTUI--%ACTIVEUI%/images/%PAGENAME%",
    01346             "%ACTIVEUI%/images/%PAGENAME%",
    01347             "local/interface/default/images/%PAGENAME%",
    01348             "interface/default/images/%PAGENAME%",
    01349             );
    01350     private $FunctionFileList = array(
    01351             "local/interface/%ACTIVEUI%/include/%PAGENAME%.php",
    01352             "local/interface/%ACTIVEUI%/include/%PAGENAME%.html",
    01353             "interface/%ACTIVEUI%/include/%PAGENAME%.php",
    01354             "interface/%ACTIVEUI%/include/%PAGENAME%.html",
    01355             "SPTUI--%ACTIVEUI%/include/%PAGENAME%.php",
    01356             "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html",
    01357             "%ACTIVEUI%/include/%PAGENAME%.php",
    01358             "%ACTIVEUI%/include/%PAGENAME%.html",
    01359             "local/interface/default/include/%PAGENAME%.php",
    01360             "local/interface/default/include/%PAGENAME%.html",
    01361             "local/include/%PAGENAME%.php",
    01362             "local/include/%PAGENAME%.html",
    01363             "interface/default/include/%PAGENAME%.php",
    01364             "interface/default/include/%PAGENAME%.html",
    01365             "include/%PAGENAME%.php",
    01366             "include/%PAGENAME%.html",
    01367             );
    01368
    01369     const NOVALUE = ".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-.";
    01370 };
    01371
    01372
    01373 ?>

Copyright 2010 Internet Scout