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