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 ('2000-01-02 03:04:05')"); 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 # sanitize incoming page name 00090 $PageName = preg_replace("/[^a-zA-Z0-9_.-]/", "", $PageName); 00091 00092 # signal page load 00093 $this->SignalEvent("EVENT_PAGE_LOAD", array("PageName" => $PageName)); 00094 00095 # signal PHP file load 00096 $SignalResult = $this->SignalEvent("EVENT_PHP_FILE_LOAD", array( 00097 "PageName" => $PageName)); 00098 00099 # if signal handler returned new page name value 00100 $NewPageName = $PageName; 00101 if (($SignalResult["PageName"] != $PageName) 00102 && strlen($SignalResult["PageName"])) 00103 { 00104 # if new page name value is page file 00105 if (file_exists($SignalResult["PageName"])) 00106 { 00107 # use new value for PHP file name 00108 $PageFile = $SignalResult["PageName"]; 00109 } 00110 else 00111 { 00112 # use new value for page name 00113 $NewPageName = $SignalResult["PageName"]; 00114 } 00115 } 00116 00117 # if we do not already have a PHP file 00118 if (!isset($PageFile)) 00119 { 00120 # look for PHP file for page 00121 $OurPageFile = "pages/".$NewPageName.".php"; 00122 $LocalPageFile = "local/pages/".$NewPageName.".php"; 00123 $PageFile = file_exists($LocalPageFile) ? $LocalPageFile 00124 : (file_exists($OurPageFile) ? $OurPageFile 00125 : "pages/".$this->DefaultPage.".php"); 00126 } 00127 00128 # load PHP file 00129 include($PageFile); 00130 00131 # save buffered output to be displayed later after HTML file loads 00132 $PageOutput = ob_get_contents(); 00133 ob_end_clean(); 00134 00135 # set up for possible TSR (Terminate and Stay Resident :)) 00136 $ShouldTSR = $this->PrepForTSR(); 00137 00138 # if PHP file indicated we should autorefresh to somewhere else 00139 if ($this->JumpToPage) 00140 { 00141 if (!strlen(trim($PageOutput))) 00142 { 00143 ?><html> 00144 <head> 00145 <meta http-equiv="refresh" content="0; URL=<?PHP 00146 print($this->JumpToPage); ?>"> 00147 </head> 00148 <body bgcolor="white"> 00149 </body> 00150 </html><?PHP 00151 } 00152 } 00153 # else if HTML loading is not suppressed 00154 elseif (!$this->SuppressHTML) 00155 { 00156 # set content-type to get rid of diacritic errors 00157 header("Content-Type: text/html; charset=" 00158 .$this->HtmlCharset, TRUE); 00159 00160 # load common HTML file if available 00161 $HtmlFile = $this->FindCommonTemplate("Common"); 00162 if ($HtmlFile) { include($HtmlFile); } 00163 00164 # load UI functions 00165 $this->LoadUIFunctions(); 00166 00167 # begin buffering content 00168 ob_start(); 00169 00170 # signal HTML file load 00171 $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD", array( 00172 "PageName" => $PageName)); 00173 00174 # if signal handler returned new page name value 00175 $NewPageName = $PageName; 00176 $HtmlFile = NULL; 00177 if (($SignalResult["PageName"] != $PageName) 00178 && strlen($SignalResult["PageName"])) 00179 { 00180 # if new page name value is HTML file 00181 if (file_exists($SignalResult["PageName"])) 00182 { 00183 # use new value for HTML file name 00184 $HtmlFile = $SignalResult["PageName"]; 00185 } 00186 else 00187 { 00188 # use new value for page name 00189 $NewPageName = $SignalResult["PageName"]; 00190 } 00191 } 00192 00193 # load page content HTML file if available 00194 if ($HtmlFile === NULL) 00195 { 00196 $HtmlFile = $this->FindTemplate($this->ContentTemplateList, $NewPageName); 00197 } 00198 if ($HtmlFile) 00199 { 00200 include($HtmlFile); 00201 } 00202 else 00203 { 00204 print("<h2>ERROR: No HTML/TPL template found" 00205 ." for this page.</h2>"); 00206 } 00207 00208 # signal HTML file load complete 00209 $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD_COMPLETE"); 00210 00211 # stop buffering and save content 00212 $BufferedContent = ob_get_contents(); 00213 ob_end_clean(); 00214 00215 # load page start HTML file if available 00216 $HtmlFile = $this->FindCommonTemplate("Start"); 00217 if ($HtmlFile) { include($HtmlFile); } 00218 00219 # write out page content 00220 print($BufferedContent); 00221 00222 # load page end HTML file if available 00223 $HtmlFile = $this->FindCommonTemplate("End"); 00224 if ($HtmlFile) { include($HtmlFile); } 00225 } 00226 00227 # run any post-processing routines 00228 foreach ($this->PostProcessingFuncs as $Func) 00229 { 00230 call_user_func_array($Func["FunctionName"], $Func["Arguments"]); 00231 } 00232 00233 # write out any output buffered from page code execution 00234 if (strlen($PageOutput)) 00235 { 00236 if (!$this->SuppressHTML) 00237 { 00238 ?><table width="100%" cellpadding="5" 00239 style="border: 2px solid #666666; background: #CCCCCC; 00240 font-family: Courier New, Courier, monospace; 00241 margin-top: 10px;"><tr><td><?PHP 00242 } 00243 if ($this->JumpToPage) 00244 { 00245 ?><div style="color: #666666;"><span style="font-size: 150%;"> 00246 <b>Page Jump Aborted</b></span> 00247 (because of error or other unexpected output)<br /> 00248 <b>Jump Target:</b> 00249 <i><?PHP print($this->JumpToPage); ?></i></div><?PHP 00250 } 00251 print($PageOutput); 00252 if (!$this->SuppressHTML) 00253 { 00254 ?></td></tr></table><?PHP 00255 } 00256 } 00257 00258 # terminate and stay resident (TSR!) if indicated and HTML has been output 00259 # (only TSR if HTML has been output because otherwise browsers will misbehave) 00260 if ($ShouldTSR) { $this->LaunchTSR(); } 00261 } 00262 00269 function SetJumpToPage($Page) 00270 { 00271 if ((strpos($Page, "?") === FALSE) 00272 && ((strpos($Page, "=") !== FALSE) 00273 || ((strpos($Page, ".php") === FALSE) 00274 && (strpos($Page, ".htm") === FALSE) 00275 && (strpos($Page, "/") === FALSE)))) 00276 { 00277 $this->JumpToPage = "index.php?P=".$Page; 00278 } 00279 else 00280 { 00281 $this->JumpToPage = $Page; 00282 } 00283 } 00284 00289 function JumpToPageIsSet() 00290 { 00291 return ($this->JumpToPage === NULL) ? FALSE : TRUE; 00292 } 00293 00303 function HtmlCharset($NewSetting = NULL) 00304 { 00305 if ($NewSetting !== NULL) { $this->HtmlCharset = $NewSetting; } 00306 return $this->HtmlCharset; 00307 } 00308 00315 function SuppressHTMLOutput($NewSetting = TRUE) 00316 { 00317 $this->SuppressHTML = $NewSetting; 00318 } 00319 00326 function ActiveUserInterface($UIName = NULL) 00327 { 00328 if ($UIName !== NULL) 00329 { 00330 $this->ActiveUI = preg_replace("/^SPTUI--/", "", $UIName); 00331 } 00332 return $this->ActiveUI; 00333 } 00334 00350 function AddPostProcessingCall($FunctionName, 00351 &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE, 00352 &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE, 00353 &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE) 00354 { 00355 $FuncIndex = count($this->PostProcessingFuncs); 00356 $this->PostProcessingFuncs[$FuncIndex]["FunctionName"] = $FunctionName; 00357 $this->PostProcessingFuncs[$FuncIndex]["Arguments"] = array(); 00358 $Index = 1; 00359 while (isset(${"Arg".$Index}) && (${"Arg".$Index} !== self::NOVALUE)) 00360 { 00361 $this->PostProcessingFuncs[$FuncIndex]["Arguments"][$Index] 00362 =& ${"Arg".$Index}; 00363 $Index++; 00364 } 00365 } 00366 00372 function AddEnvInclude($FileName) 00373 { 00374 $this->EnvIncludes[] = $FileName; 00375 } 00376 00382 function GUIFile($FileName) 00383 { 00384 # pull off file name suffix 00385 $NamePieces = explode(".", $FileName); 00386 $Suffix = strtolower(array_pop($NamePieces)); 00387 00388 # determine which location to search based on file suffix 00389 $ImageSuffixes = array("gif", "jpg", "png"); 00390 $FileList = in_array($Suffix, $ImageSuffixes) 00391 ? $this->ImageFileList : $this->CommonTemplateList; 00392 00393 # search for file and return result to caller 00394 return $this->FindTemplate($FileList, $FileName); 00395 } 00396 00406 function PUIFile($FileName) 00407 { 00408 $FullFileName = $this->GUIFile($FileName); 00409 if ($FullFileName) { print($FullFileName); } 00410 } 00411 00416 function FindCommonTemplate($PageName) 00417 { 00418 return $this->FindTemplate( 00419 array_merge($this->CommonTemplateList, $this->ContentTemplateList), 00420 $PageName); 00421 } 00422 00431 function LoadFunction($Callback) 00432 { 00433 if (!is_callable($Callback) && is_string($Callback)) 00434 { 00435 $Locations = $this->FunctionFileList; 00436 foreach (self::$ObjectDirectories as $Location => $Prefix) 00437 { 00438 $Locations[] = $Location."%PAGENAME%.php"; 00439 $Locations[] = $Location."%PAGENAME%.html"; 00440 } 00441 $FunctionFileName = $this->FindTemplate($Locations, "F-".$Callback); 00442 if ($FunctionFileName) 00443 { 00444 include_once($FunctionFileName); 00445 } 00446 } 00447 return is_callable($Callback); 00448 } 00449 00454 function GetElapsedExecutionTime() 00455 { 00456 return microtime(TRUE) - $this->ExecutionStartTime; 00457 } 00458 00463 function GetSecondsBeforeTimeout() 00464 { 00465 return ini_get("max_execution_time") - $this->GetElapsedExecutionTime(); 00466 } 00467 00468 /*@)*/ /* Application Framework */ 00469 00470 # ---- Event Handling ---------------------------------------------------- 00471 /*@(*/ 00473 00477 const EVENTTYPE_DEFAULT = 1; 00483 const EVENTTYPE_CHAIN = 2; 00489 const EVENTTYPE_FIRST = 3; 00497 const EVENTTYPE_NAMED = 4; 00498 00500 const ORDER_FIRST = 1; 00502 const ORDER_MIDDLE = 2; 00504 const ORDER_LAST = 3; 00505 00514 function RegisterEvent($EventsOrEventName, $EventType = NULL) 00515 { 00516 # convert parameters to array if not already in that form 00517 $Events = is_array($EventsOrEventName) ? $EventsOrEventName 00518 : array($EventsOrEventName => $Type); 00519 00520 # for each event 00521 foreach ($Events as $Name => $Type) 00522 { 00523 # store event information 00524 $this->RegisteredEvents[$Name]["Type"] = $Type; 00525 $this->RegisteredEvents[$Name]["Hooks"] = array(); 00526 } 00527 } 00528 00542 function HookEvent($EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE) 00543 { 00544 # convert parameters to array if not already in that form 00545 $Events = is_array($EventsOrEventName) ? $EventsOrEventName 00546 : array($EventsOrEventName => $Callback); 00547 00548 # for each event 00549 $Success = TRUE; 00550 foreach ($Events as $EventName => $EventCallback) 00551 { 00552 # if callback is valid 00553 if (is_callable($EventCallback)) 00554 { 00555 # if this is a periodic event we process internally 00556 if (isset($this->PeriodicEvents[$EventName])) 00557 { 00558 # process event now 00559 $this->ProcessPeriodicEvent($EventName, $EventCallback); 00560 } 00561 # if specified event has been registered 00562 elseif (isset($this->RegisteredEvents[$EventName])) 00563 { 00564 # add callback for event 00565 $this->RegisteredEvents[$EventName]["Hooks"][] 00566 = array("Callback" => $EventCallback, "Order" => $Order); 00567 00568 # sort callbacks by order 00569 if (count($this->RegisteredEvents[$EventName]["Hooks"]) > 1) 00570 { 00571 usort($this->RegisteredEvents[$EventName]["Hooks"], 00572 array("ApplicationFramework", "HookEvent_OrderCompare")); 00573 } 00574 } 00575 else 00576 { 00577 $Success = FALSE; 00578 } 00579 } 00580 else 00581 { 00582 $Success = FALSE; 00583 } 00584 } 00585 00586 # report to caller whether all callbacks were hooked 00587 return $Success; 00588 } 00589 private static function HookEvent_OrderCompare($A, $B) 00590 { 00591 if ($A["Order"] == $B["Order"]) { return 0; } 00592 return ($A["Order"] < $B["Order"]) ? -1 : 1; 00593 } 00594 00603 function SignalEvent($EventName, $Parameters = NULL) 00604 { 00605 $ReturnValue = NULL; 00606 00607 # if event has been registered 00608 if (isset($this->RegisteredEvents[$EventName])) 00609 { 00610 # set up default return value (if not NULL) 00611 switch ($this->RegisteredEvents[$EventName]["Type"]) 00612 { 00613 case self::EVENTTYPE_CHAIN: 00614 $ReturnValue = $Parameters; 00615 break; 00616 00617 case self::EVENTTYPE_NAMED: 00618 $ReturnValue = array(); 00619 break; 00620 } 00621 00622 # for each callback for this event 00623 foreach ($this->RegisteredEvents[$EventName]["Hooks"] as $Hook) 00624 { 00625 # invoke callback 00626 $Callback = $Hook["Callback"]; 00627 $Result = ($Parameters !== NULL) 00628 ? call_user_func_array($Callback, $Parameters) 00629 : call_user_func($Callback); 00630 00631 # process return value based on event type 00632 switch ($this->RegisteredEvents[$EventName]["Type"]) 00633 { 00634 case self::EVENTTYPE_CHAIN: 00635 $ReturnValue = $Result; 00636 $Parameters = $Result; 00637 break; 00638 00639 case self::EVENTTYPE_FIRST: 00640 if ($Result !== NULL) 00641 { 00642 $ReturnValue = $Result; 00643 break 2; 00644 } 00645 break; 00646 00647 case self::EVENTTYPE_NAMED: 00648 $CallbackName = is_array($Callback) 00649 ? (is_object($Callback[0]) 00650 ? get_class($Callback[0]) 00651 : $Callback[0])."::".$Callback[1] 00652 : $Callback; 00653 $ReturnValue[$CallbackName] = $Result; 00654 break; 00655 00656 default: 00657 break; 00658 } 00659 } 00660 } 00661 00662 # return value if any to caller 00663 return $ReturnValue; 00664 } 00665 00671 function IsStaticOnlyEvent($EventName) 00672 { 00673 return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE; 00674 } 00675 00676 /*@)*/ /* Event Handling */ 00677 00678 # ---- Task Management --------------------------------------------------- 00679 /*@(*/ 00681 00683 const PRIORITY_HIGH = 1; 00685 const PRIORITY_MEDIUM = 2; 00687 const PRIORITY_LOW = 3; 00689 const PRIORITY_BACKGROUND = 4; 00690 00703 function QueueTask($Callback, $Parameters = NULL, 00704 $Priority = self::PRIORITY_MEDIUM, $Description = "") 00705 { 00706 # pack task info and write to database 00707 if ($Parameters === NULL) { $Parameters = array(); } 00708 $this->DB->Query("INSERT INTO TaskQueue" 00709 ." (Callback, Parameters, Priority, Description)" 00710 ." VALUES ('".addslashes(serialize($Callback))."', '" 00711 .addslashes(serialize($Parameters))."', ".intval($Priority).", '" 00712 .addslashes($Description)."')"); 00713 } 00714 00732 function QueueUniqueTask($Callback, $Parameters = NULL, 00733 $Priority = self::PRIORITY_MEDIUM, $Description = "") 00734 { 00735 if ($this->TaskIsInQueue($Callback, $Parameters)) 00736 { 00737 $QueryResult = $this->DB->Query("SELECT TaskId,Priority FROM TaskQueue" 00738 ." WHERE Callback = '".addslashes(serialize($Callback))."'" 00739 .($Parameters ? " AND Parameters = '" 00740 .addslashes(serialize($Parameters))."'" : "")); 00741 if ($QueryResult !== FALSE) 00742 { 00743 $Record = $this->DB->FetchRow(); 00744 if ($Record["Priority"] > $Priority) 00745 { 00746 $this->DB->Query("UPDATE TaskQueue" 00747 ." SET Priority = ".intval($Priority) 00748 ." WHERE TaskId = ".intval($Record["TaskId"])); 00749 } 00750 } 00751 return FALSE; 00752 } 00753 else 00754 { 00755 $this->QueueTask($Callback, $Parameters, $Priority, $Description); 00756 return TRUE; 00757 } 00758 } 00759 00769 function TaskIsInQueue($Callback, $Parameters = NULL) 00770 { 00771 $FoundCount = $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM TaskQueue" 00772 ." WHERE Callback = '".addslashes(serialize($Callback))."'" 00773 .($Parameters ? " AND Parameters = '" 00774 .addslashes(serialize($Parameters))."'" : ""), 00775 "FoundCount") 00776 + $this->DB->Query("SELECT COUNT(*) AS FoundCount FROM RunningTasks" 00777 ." WHERE Callback = '".addslashes(serialize($Callback))."'" 00778 .($Parameters ? " AND Parameters = '" 00779 .addslashes(serialize($Parameters))."'" : ""), 00780 "FoundCount"); 00781 return ($FoundCount ? TRUE : FALSE); 00782 } 00783 00789 function GetTaskQueueSize($Priority = NULL) 00790 { 00791 return $this->DB->Query("SELECT COUNT(*) AS QueueSize FROM TaskQueue" 00792 .($Priority ? " WHERE Priority = ".intval($Priority) : ""), 00793 "QueueSize"); 00794 } 00795 00803 function GetQueuedTaskList($Count = 100, $Offset = 0) 00804 { 00805 return $this->GetTaskList("SELECT * FROM TaskQueue" 00806 ." ORDER BY Priority, TaskId ", $Count, $Offset); 00807 } 00808 00816 function GetRunningTaskList($Count = 100, $Offset = 0) 00817 { 00818 return $this->GetTaskList("SELECT * FROM RunningTasks" 00819 ." WHERE StartedAt >= '".date("Y-m-d H:i:s", 00820 (time() - ini_get("max_execution_time")))."'" 00821 ." ORDER BY StartedAt", $Count, $Offset); 00822 } 00823 00831 function GetOrphanedTaskList($Count = 100, $Offset = 0) 00832 { 00833 return $this->GetTaskList("SELECT * FROM RunningTasks" 00834 ." WHERE StartedAt < '".date("Y-m-d H:i:s", 00835 (time() - ini_get("max_execution_time")))."'" 00836 ." ORDER BY StartedAt", $Count, $Offset); 00837 } 00838 00843 function ReQueueOrphanedTask($TaskId) 00844 { 00845 $this->DB->Query("LOCK TABLES TaskQueue WRITE, RunningTasks WRITE"); 00846 $this->DB->Query("INSERT INTO TaskQueue" 00847 ." (Callback,Parameters,Priority,Description) " 00848 ."SELECT Callback, Parameters, Priority, Description" 00849 ." FROM RunningTasks WHERE TaskId = ".intval($TaskId)); 00850 $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId)); 00851 $this->DB->Query("UNLOCK TABLES"); 00852 } 00853 00858 function DeleteTask($TaskId) 00859 { 00860 $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = ".intval($TaskId)); 00861 $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId)); 00862 } 00863 00871 function GetTask($TaskId) 00872 { 00873 # assume task will not be found 00874 $Task = NULL; 00875 00876 # look for task in task queue 00877 $this->DB->Query("SELECT * FROM TaskQueue WHERE TaskId = ".intval($TaskId)); 00878 00879 # if task was not found in queue 00880 if (!$this->DB->NumRowsSelected()) 00881 { 00882 # look for task in running task list 00883 $this->DB->Query("SELECT * FROM RunningTasks WHERE TaskId = " 00884 .intval($TaskId)); 00885 } 00886 00887 # if task was found 00888 if ($this->DB->NumRowsSelected()) 00889 { 00890 # if task was periodic 00891 $Row = $this->DB->FetchRow(); 00892 if ($Row["Callback"] == 00893 serialize(array("ApplicationFramework", "PeriodicEventWrapper"))) 00894 { 00895 # unpack periodic task callback 00896 $WrappedCallback = unserialize($Row["Parameters"]); 00897 $Task["Callback"] = $WrappedCallback[1]; 00898 $Task["Parameters"] = NULL; 00899 } 00900 else 00901 { 00902 # unpack task callback and parameters 00903 $Task["Callback"] = unserialize($Row["Callback"]); 00904 $Task["Parameters"] = unserialize($Row["Parameters"]); 00905 } 00906 } 00907 00908 # return task to caller 00909 return $Task; 00910 } 00911 00917 function MaxTasks($NewValue = NULL) 00918 { 00919 if (func_num_args() && ($NewValue >= 1)) 00920 { 00921 $this->DB->Query("UPDATE ApplicationFrameworkSettings" 00922 ." SET MaxTasksRunning = '".intval($NewValue)."'"); 00923 $this->Settings["MaxTasksRunning"] = intval($NewValue); 00924 } 00925 return $this->Settings["MaxTasksRunning"]; 00926 } 00927 00935 function MaxExecutionTime($NewValue = NULL) 00936 { 00937 if (func_num_args() && !ini_get("safe_mode")) 00938 { 00939 if ($NewValue != $this->Settings["MaxExecTime"]) 00940 { 00941 $this->Settings["MaxExecTime"] = max($NewValue, 5); 00942 $this->DB->Query("UPDATE ApplicationFrameworkSettings" 00943 ." SET MaxExecTime = '" 00944 .intval($this->Settings["MaxExecTime"])."'"); 00945 } 00946 ini_set("max_execution_time", $this->Settings["MaxExecTime"]); 00947 set_time_limit($this->Settings["MaxExecTime"]); 00948 } 00949 return ini_get("max_execution_time"); 00950 } 00951 00952 /*@)*/ /* Task Management */ 00953 00954 # ---- PRIVATE INTERFACE ------------------------------------------------- 00955 00956 private $JumpToPage = NULL; 00957 private $SuppressHTML = FALSE; 00958 private $DefaultPage = "Home"; 00959 private $ActiveUI = "default"; 00960 private $HtmlCharset = "UTF-8"; 00961 private $PostProcessingFuncs = array(); 00962 private $EnvIncludes = array(); 00963 private $DB; 00964 private $Settings; 00965 private $ExecutionStartTime; 00966 private static $ObjectDirectories = array(); 00967 private $MaxRunningTasksToTrack = 250; 00968 private $RunningTask; 00969 00970 private $PeriodicEvents = array( 00971 "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT, 00972 "EVENT_DAILY" => self::EVENTTYPE_DEFAULT, 00973 "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT, 00974 "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT, 00975 "EVENT_PERIODIC" => self::EVENTTYPE_NAMED, 00976 ); 00977 private $UIEvents = array( 00978 "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT, 00979 "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN, 00980 "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN, 00981 "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT, 00982 ); 00983 00984 private function FindTemplate($FileList, $PageName) 00985 { 00986 $FileNameFound = NULL; 00987 foreach ($FileList as $FileName) 00988 { 00989 $FileName = str_replace("%ACTIVEUI%", $this->ActiveUI, $FileName); 00990 $FileName = str_replace("%PAGENAME%", $PageName, $FileName); 00991 if (file_exists($FileName)) 00992 { 00993 $FileNameFound = $FileName; 00994 break; 00995 } 00996 } 00997 return $FileNameFound; 00998 } 00999 01000 private function SetUpObjectAutoloading() 01001 { 01002 function __autoload($ClassName) 01003 { 01004 ApplicationFramework::AutoloadObjects($ClassName); 01005 } 01006 } 01007 01009 static function AutoloadObjects($ClassName) 01010 { 01011 foreach (self::$ObjectDirectories as $Location => $Prefix) 01012 { 01013 $FileName = $Location.$Prefix.$ClassName.".php"; 01014 if (file_exists($FileName)) 01015 { 01016 require_once($FileName); 01017 break; 01018 } 01019 } 01020 } 01023 private function LoadUIFunctions() 01024 { 01025 $Dirs = array( 01026 "local/interface/%ACTIVEUI%/include", 01027 "interface/%ACTIVEUI%/include", 01028 "local/interface/default/include", 01029 "interface/default/include", 01030 ); 01031 foreach ($Dirs as $Dir) 01032 { 01033 $Dir = str_replace("%ACTIVEUI%", $this->ActiveUI, $Dir); 01034 if (is_dir($Dir)) 01035 { 01036 $FileNames = scandir($Dir); 01037 foreach ($FileNames as $FileName) 01038 { 01039 if (preg_match("/^F-([A-Za-z_]+)\.php/", $FileName, $Matches) 01040 || preg_match("/^F-([A-Za-z_]+)\.html/", $FileName, $Matches)) 01041 { 01042 if (!function_exists($Matches[1])) 01043 { 01044 include_once($Dir."/".$FileName); 01045 } 01046 } 01047 } 01048 } 01049 } 01050 } 01051 01052 private function ProcessPeriodicEvent($EventName, $Callback) 01053 { 01054 # retrieve last execution time for event if available 01055 $Signature = self::GetCallbackSignature($Callback); 01056 $LastRun = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents" 01057 ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt"); 01058 01059 # determine whether enough time has passed for event to execute 01060 $EventPeriods = array( 01061 "EVENT_HOURLY" => 60*60, 01062 "EVENT_DAILY" => 60*60*24, 01063 "EVENT_WEEKLY" => 60*60*24*7, 01064 "EVENT_MONTHLY" => 60*60*24*30, 01065 "EVENT_PERIODIC" => 0, 01066 ); 01067 $ShouldExecute = (($LastRun === NULL) 01068 || (time() > (strtotime($LastRun) + $EventPeriods[$EventName]))) 01069 ? TRUE : FALSE; 01070 01071 # if event should run 01072 if ($ShouldExecute) 01073 { 01074 # add event to task queue 01075 $WrapperCallback = array("ApplicationFramework", "PeriodicEventWrapper"); 01076 $WrapperParameters = array( 01077 $EventName, $Callback, array("LastRunAt" => $LastRun)); 01078 $this->QueueUniqueTask($WrapperCallback, $WrapperParameters); 01079 } 01080 } 01081 01082 private static function PeriodicEventWrapper($EventName, $Callback, $Parameters) 01083 { 01084 static $DB; 01085 if (!isset($DB)) { $DB = new Database(); } 01086 01087 # run event 01088 $ReturnVal = call_user_func_array($Callback, $Parameters); 01089 01090 # if event is already in database 01091 $Signature = self::GetCallbackSignature($Callback); 01092 if ($DB->Query("SELECT COUNT(*) AS EventCount FROM PeriodicEvents" 01093 ." WHERE Signature = '".addslashes($Signature)."'", "EventCount")) 01094 { 01095 # update last run time for event 01096 $DB->Query("UPDATE PeriodicEvents SET LastRunAt = " 01097 .(($EventName == "EVENT_PERIODIC") 01098 ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'" 01099 : "NOW()") 01100 ." WHERE Signature = '".addslashes($Signature)."'"); 01101 } 01102 else 01103 { 01104 # add last run time for event to database 01105 $DB->Query("INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES " 01106 ."('".addslashes($Signature)."', " 01107 .(($EventName == "EVENT_PERIODIC") 01108 ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'" 01109 : "NOW()").")"); 01110 } 01111 } 01112 01113 private static function GetCallbackSignature($Callback) 01114 { 01115 return !is_array($Callback) ? $Callback 01116 : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0]) 01117 ."::".$Callback[1]; 01118 } 01119 01120 private function PrepForTSR() 01121 { 01122 # if HTML has been output and it's time to launch another task 01123 # (only TSR if HTML has been output because otherwise browsers 01124 # may misbehave after connection is closed) 01125 if (($this->JumpToPage || !$this->SuppressHTML) 01126 && (time() > (strtotime($this->Settings["LastTaskRunAt"]) 01127 + (ini_get("max_execution_time") 01128 / $this->Settings["MaxTasksRunning"]) + 5)) 01129 && $this->GetTaskQueueSize()) 01130 { 01131 # begin buffering output for TSR 01132 ob_start(); 01133 01134 # let caller know it is time to launch another task 01135 return TRUE; 01136 } 01137 else 01138 { 01139 # let caller know it is not time to launch another task 01140 return FALSE; 01141 } 01142 } 01143 01144 private function LaunchTSR() 01145 { 01146 # set needed headers and 01147 ignore_user_abort(TRUE); 01148 header("Connection: close"); 01149 header("Content-Length: ".ob_get_length()); 01150 01151 # output buffered content 01152 ob_end_flush(); 01153 flush(); 01154 01155 # write out any outstanding data and end HTTP session 01156 session_write_close(); 01157 01158 # if there is still a task in the queue 01159 if ($this->GetTaskQueueSize()) 01160 { 01161 # turn on output buffering to (hopefully) record any crash output 01162 ob_start(); 01163 01164 # lock tables and grab last task run time to double check 01165 $this->DB->Query("LOCK TABLES ApplicationFrameworkSettings WRITE"); 01166 $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings"); 01167 $this->Settings = $this->DB->FetchRow(); 01168 01169 # if still time to launch another task 01170 if (time() > (strtotime($this->Settings["LastTaskRunAt"]) 01171 + (ini_get("max_execution_time") 01172 / $this->Settings["MaxTasksRunning"]) + 5)) 01173 { 01174 # update the "last run" time and release tables 01175 $this->DB->Query("UPDATE ApplicationFrameworkSettings" 01176 ." SET LastTaskRunAt = '".date("Y-m-d H:i:s")."'"); 01177 $this->DB->Query("UNLOCK TABLES"); 01178 01179 # run tasks while there is a task in the queue and enough time left 01180 do 01181 { 01182 # run the next task 01183 $this->RunNextTask(); 01184 } 01185 while ($this->GetTaskQueueSize() 01186 && ($this->GetSecondsBeforeTimeout() > 65)); 01187 } 01188 else 01189 { 01190 # release tables 01191 $this->DB->Query("UNLOCK TABLES"); 01192 } 01193 } 01194 } 01195 01203 private function GetTaskList($DBQuery, $Count, $Offset) 01204 { 01205 $this->DB->Query($DBQuery." LIMIT ".intval($Offset).",".intval($Count)); 01206 $Tasks = array(); 01207 while ($Row = $this->DB->FetchRow()) 01208 { 01209 $Tasks[$Row["TaskId"]] = $Row; 01210 if ($Row["Callback"] == 01211 serialize(array("ApplicationFramework", "PeriodicEventWrapper"))) 01212 { 01213 $WrappedCallback = unserialize($Row["Parameters"]); 01214 $Tasks[$Row["TaskId"]]["Callback"] = $WrappedCallback[1]; 01215 $Tasks[$Row["TaskId"]]["Parameters"] = NULL; 01216 } 01217 else 01218 { 01219 $Tasks[$Row["TaskId"]]["Callback"] = unserialize($Row["Callback"]); 01220 $Tasks[$Row["TaskId"]]["Parameters"] = unserialize($Row["Parameters"]); 01221 } 01222 } 01223 return $Tasks; 01224 } 01225 01229 private function RunNextTask() 01230 { 01231 # look for task at head of queue 01232 $this->DB->Query("SELECT * FROM TaskQueue ORDER BY Priority, TaskId LIMIT 1"); 01233 $Task = $this->DB->FetchRow(); 01234 01235 # if there was a task available 01236 if ($Task) 01237 { 01238 # move task from queue to running tasks list 01239 $this->DB->Query("INSERT INTO RunningTasks " 01240 ."(TaskId,Callback,Parameters,Priority,Description) " 01241 ."SELECT * FROM TaskQueue WHERE TaskId = " 01242 .intval($Task["TaskId"])); 01243 $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = " 01244 .intval($Task["TaskId"])); 01245 01246 # unpack stored task info 01247 $Callback = unserialize($Task["Callback"]); 01248 $Parameters = unserialize($Task["Parameters"]); 01249 01250 # attempt to load task callback if not already available 01251 $this->LoadFunction($Callback); 01252 01253 # run task 01254 $this->RunningTask = $Task; 01255 if ($Parameters) 01256 { 01257 call_user_func_array($Callback, $Parameters); 01258 } 01259 else 01260 { 01261 call_user_func($Callback); 01262 } 01263 unset($this->RunningTask); 01264 01265 # remove task from running tasks list 01266 $this->DB->Query("DELETE FROM RunningTasks" 01267 ." WHERE TaskId = ".intval($Task["TaskId"])); 01268 01269 # prune running tasks list if necessary 01270 $RunningTasksCount = $this->DB->Query( 01271 "SELECT COUNT(*) AS TaskCount FROM RunningTasks", "TaskCount"); 01272 if ($RunningTasksCount > $this->MaxRunningTasksToTrack) 01273 { 01274 $this->DB->Query("DELETE FROM RunningTasks ORDER BY StartedAt" 01275 ." LIMIT ".($RunningTasksCount - $this->MaxRunningTasksToTrack)); 01276 } 01277 } 01278 } 01279 01285 function OnCrash() 01286 { 01287 if (isset($this->RunningTask)) 01288 { 01289 if (function_exists("error_get_last")) 01290 { 01291 $CrashInfo["LastError"] = error_get_last(); 01292 } 01293 if (ob_get_length() !== FALSE) 01294 { 01295 $CrashInfo["OutputBuffer"] = ob_get_contents(); 01296 } 01297 if (isset($CrashInfo)) 01298 { 01299 $DB = new Database(); 01300 $DB->Query("UPDATE RunningTasks SET CrashInfo = '" 01301 .addslashes(serialize($CrashInfo)) 01302 ."' WHERE TaskId = ".intval($this->RunningTask["TaskId"])); 01303 } 01304 } 01305 01306 print("\n"); 01307 return; 01308 01309 if (ob_get_length() !== FALSE) 01310 { 01311 ?> 01312 <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> 01313 <div style="font-size: 200%;">CRASH OUTPUT</div><?PHP 01314 ob_end_flush(); 01315 ?></td></tr></table><?PHP 01316 } 01317 } 01318 01319 private $CommonTemplateList = array( 01320 "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl", 01321 "local/interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html", 01322 "local/interface/%ACTIVEUI%/include/%PAGENAME%.tpl", 01323 "local/interface/%ACTIVEUI%/include/%PAGENAME%.html", 01324 "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl", 01325 "local/interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html", 01326 "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl", 01327 "local/interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html", 01328 "local/interface/%ACTIVEUI%/include/%PAGENAME%", 01329 "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.tpl", 01330 "interface/%ACTIVEUI%/include/StdPage%PAGENAME%.html", 01331 "interface/%ACTIVEUI%/include/%PAGENAME%.tpl", 01332 "interface/%ACTIVEUI%/include/%PAGENAME%.html", 01333 "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl", 01334 "interface/%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html", 01335 "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.tpl", 01336 "interface/%ACTIVEUI%/include/SPT--%PAGENAME%.html", 01337 "interface/%ACTIVEUI%/include/%PAGENAME%", 01338 "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.tpl", 01339 "SPTUI--%ACTIVEUI%/include/StdPage%PAGENAME%.html", 01340 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.tpl", 01341 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html", 01342 "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl", 01343 "SPTUI--%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html", 01344 "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.tpl", 01345 "SPTUI--%ACTIVEUI%/include/SPT--%PAGENAME%.html", 01346 "SPTUI--%ACTIVEUI%/include/%PAGENAME%", 01347 "%ACTIVEUI%/include/StdPage%PAGENAME%.tpl", 01348 "%ACTIVEUI%/include/StdPage%PAGENAME%.html", 01349 "%ACTIVEUI%/include/%PAGENAME%.tpl", 01350 "%ACTIVEUI%/include/%PAGENAME%.html", 01351 "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.tpl", 01352 "%ACTIVEUI%/include/SPT--StandardPage%PAGENAME%.html", 01353 "%ACTIVEUI%/include/SPT--%PAGENAME%.tpl", 01354 "%ACTIVEUI%/include/SPT--%PAGENAME%.html", 01355 "%ACTIVEUI%/include/%PAGENAME%", 01356 "local/interface/default/include/StdPage%PAGENAME%.tpl", 01357 "local/interface/default/include/StdPage%PAGENAME%.html", 01358 "local/interface/default/include/%PAGENAME%.tpl", 01359 "local/interface/default/include/%PAGENAME%.html", 01360 "local/interface/default/include/SPT--StandardPage%PAGENAME%.tpl", 01361 "local/interface/default/include/SPT--StandardPage%PAGENAME%.html", 01362 "local/interface/default/include/SPT--%PAGENAME%.tpl", 01363 "local/interface/default/include/SPT--%PAGENAME%.html", 01364 "local/interface/default/include/%PAGENAME%", 01365 "interface/default/include/StdPage%PAGENAME%.tpl", 01366 "interface/default/include/StdPage%PAGENAME%.html", 01367 "interface/default/include/%PAGENAME%.tpl", 01368 "interface/default/include/%PAGENAME%.html", 01369 "interface/default/include/SPT--StandardPage%PAGENAME%.tpl", 01370 "interface/default/include/SPT--StandardPage%PAGENAME%.html", 01371 "interface/default/include/SPT--%PAGENAME%.tpl", 01372 "interface/default/include/SPT--%PAGENAME%.html", 01373 "interface/default/include/%PAGENAME%", 01374 ); 01375 private $ContentTemplateList = array( 01376 "local/interface/%ACTIVEUI%/%PAGENAME%.tpl", 01377 "local/interface/%ACTIVEUI%/%PAGENAME%.html", 01378 "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl", 01379 "local/interface/%ACTIVEUI%/SPT--%PAGENAME%.html", 01380 "interface/%ACTIVEUI%/%PAGENAME%.tpl", 01381 "interface/%ACTIVEUI%/%PAGENAME%.html", 01382 "interface/%ACTIVEUI%/SPT--%PAGENAME%.tpl", 01383 "interface/%ACTIVEUI%/SPT--%PAGENAME%.html", 01384 "SPTUI--%ACTIVEUI%/%PAGENAME%.tpl", 01385 "SPTUI--%ACTIVEUI%/%PAGENAME%.html", 01386 "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.tpl", 01387 "SPTUI--%ACTIVEUI%/SPT--%PAGENAME%.html", 01388 "%ACTIVEUI%/%PAGENAME%.tpl", 01389 "%ACTIVEUI%/%PAGENAME%.html", 01390 "%ACTIVEUI%/SPT--%PAGENAME%.tpl", 01391 "%ACTIVEUI%/SPT--%PAGENAME%.html", 01392 "local/interface/default/%PAGENAME%.tpl", 01393 "local/interface/default/%PAGENAME%.html", 01394 "local/interface/default/SPT--%PAGENAME%.tpl", 01395 "local/interface/default/SPT--%PAGENAME%.html", 01396 "interface/default/%PAGENAME%.tpl", 01397 "interface/default/%PAGENAME%.html", 01398 "interface/default/SPT--%PAGENAME%.tpl", 01399 "interface/default/SPT--%PAGENAME%.html", 01400 ); 01401 private $ImageFileList = array( 01402 "local/interface/%ACTIVEUI%/images/%PAGENAME%", 01403 "interface/%ACTIVEUI%/images/%PAGENAME%", 01404 "SPTUI--%ACTIVEUI%/images/%PAGENAME%", 01405 "%ACTIVEUI%/images/%PAGENAME%", 01406 "local/interface/default/images/%PAGENAME%", 01407 "interface/default/images/%PAGENAME%", 01408 ); 01409 private $FunctionFileList = array( 01410 "local/interface/%ACTIVEUI%/include/%PAGENAME%.php", 01411 "local/interface/%ACTIVEUI%/include/%PAGENAME%.html", 01412 "interface/%ACTIVEUI%/include/%PAGENAME%.php", 01413 "interface/%ACTIVEUI%/include/%PAGENAME%.html", 01414 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.php", 01415 "SPTUI--%ACTIVEUI%/include/%PAGENAME%.html", 01416 "%ACTIVEUI%/include/%PAGENAME%.php", 01417 "%ACTIVEUI%/include/%PAGENAME%.html", 01418 "local/interface/default/include/%PAGENAME%.php", 01419 "local/interface/default/include/%PAGENAME%.html", 01420 "local/include/%PAGENAME%.php", 01421 "local/include/%PAGENAME%.html", 01422 "interface/default/include/%PAGENAME%.php", 01423 "interface/default/include/%PAGENAME%.html", 01424 "include/%PAGENAME%.php", 01425 "include/%PAGENAME%.html", 01426 ); 01427 01428 const NOVALUE = ".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-."; 01429 }; 01430 01431 01432 ?>