3 # FILE: ApplicationFramework.php
5 # Part of the ScoutLib application support library
6 # Copyright 2009-2013 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
16 # ---- PUBLIC INTERFACE --------------------------------------------------
24 function __construct()
26 # save execution start time
27 $this->ExecutionStartTime = microtime(TRUE);
29 # begin/restore PHP session
30 $SessionDomain = isset($_SERVER[
"SERVER_NAME"]) ? $_SERVER[
"SERVER_NAME"]
31 : isset($_SERVER[
"HTTP_HOST"]) ? $_SERVER[
"HTTP_HOST"]
33 if (is_writable(session_save_path()))
35 $SessionStorage = session_save_path()
36 .
"/".self::$AppName.
"_".md5($SessionDomain.dirname(__FILE__));
37 if (!is_dir($SessionStorage)) { mkdir($SessionStorage, 0700 ); }
38 if (is_writable($SessionStorage)) { session_save_path($SessionStorage); }
40 ini_set(
"session.gc_maxlifetime", self::$SessionLifetime);
41 session_set_cookie_params(
42 self::$SessionLifetime,
"/", $SessionDomain);
45 # set up object file autoloader
46 $this->SetUpObjectAutoloading();
48 # set up function to output any buffered text in case of crash
49 register_shutdown_function(array($this,
"OnCrash"));
51 # set up our internal environment
54 # set up our exception handler
55 set_exception_handler(array($this,
"GlobalExceptionHandler"));
57 # perform any work needed to undo PHP magic quotes
58 $this->UndoMagicQuotes();
60 # load our settings from database
61 $this->LoadSettings();
63 # set PHP maximum execution time
66 # register events we handle internally
78 # if template location cache is flagged to be saved
79 if ($this->SaveTemplateLocationCache)
81 # write template location cache out and update cache expiration
82 $this->DB->Query(
"UPDATE ApplicationFrameworkSettings"
83 .
" SET TemplateLocationCache = '"
84 .addslashes(serialize(
85 $this->Settings[
"TemplateLocationCache"])).
"',"
86 .
" TemplateLocationCacheExpiration = "
88 .$this->Settings[
"TemplateLocationCacheInterval"]
92 # if object location cache is flagged to be saved
93 if (self::$SaveObjectLocationCache)
95 # write object location cache out and update cache expiration
96 $this->DB->Query(
"UPDATE ApplicationFrameworkSettings"
97 .
" SET ObjectLocationCache = '"
98 .addslashes(serialize(
99 self::$ObjectLocationCache)).
"',"
100 .
" ObjectLocationCacheExpiration = "
101 .
" NOW() + INTERVAL "
102 .self::$ObjectLocationCacheInterval
112 function GlobalExceptionHandler($Exception)
114 # display exception info
115 $Location = $Exception->getFile().
"[".$Exception->getLine().
"]";
116 ?><table width=
"100%" cellpadding=
"5"
117 style=
"border: 2px solid #666666; background: #CCCCCC;
118 font-family: Courier New, Courier, monospace;
119 margin-top: 10px;"><tr><td>
120 <div style=
"color: #666666;">
121 <span style=
"font-size: 150%;">
122 <b>Uncaught Exception</b></span><br />
123 <b>
Message:</b> <i><?
PHP print $Exception->getMessage(); ?></i><br />
124 <b>Location:</b> <i><?
PHP print $Location; ?></i><br />
126 <blockquote><pre><?
PHP print $Exception->getTraceAsString();
127 ?></pre></blockquote>
129 </td></tr></table><?
PHP
131 # log exception if possible
132 $LogMsg =
"Uncaught exception (".$Exception->getMessage().
").";
133 $this->
LogError(self::LOGLVL_ERROR, $LogMsg);
149 $Dir, $Prefix =
"", $ClassPattern = NULL, $ClassReplacement = NULL)
151 # make sure directory has trailing slash
152 $Dir = $Dir.((substr($Dir, -1) !=
"/") ?
"/" :
"");
154 # add directory to directory list
155 self::$ObjectDirectories = array_merge(
158 "ClassPattern" => $ClassPattern,
159 "ClassReplacement" => $ClassReplacement,
161 self::$ObjectDirectories);
185 # add directories to existing image directory list
186 $this->ImageDirList = $this->AddToDirList(
187 $this->ImageDirList, $Dir, $SearchLast, $SkipSlashCheck);
212 # add directories to existing image directory list
213 $this->IncludeDirList = $this->AddToDirList(
214 $this->IncludeDirList, $Dir, $SearchLast, $SkipSlashCheck);
238 # add directories to existing image directory list
239 $this->InterfaceDirList = $this->AddToDirList(
240 $this->InterfaceDirList, $Dir, $SearchLast, $SkipSlashCheck);
264 # add directories to existing image directory list
265 $this->FunctionDirList = $this->AddToDirList(
266 $this->FunctionDirList, $Dir, $SearchLast, $SkipSlashCheck);
276 $this->BrowserDetectFunc = $DetectionFunc;
287 if (is_callable($Callback))
289 $this->UnbufferedCallbacks[] = array($Callback, $Parameters);
301 return $this->
UpdateSetting(
"TemplateLocationCacheInterval", $NewInterval);
312 return $this->
UpdateSetting(
"ObjectLocationCacheInterval", $NewInterval);
328 $BacktraceOptions = 0, $BacktraceLimit = 0)
330 if (version_compare(PHP_VERSION,
"5.4.0",
">="))
332 $this->SavedContext = debug_backtrace(
333 $BacktraceOptions, $BacktraceLimit);
337 $this->SavedContext = debug_backtrace($BacktraceOptions);
339 array_shift($this->SavedContext);
348 # perform any clean URL rewriting
349 $PageName = $this->RewriteCleanUrls($PageName);
351 # sanitize incoming page name and save local copy
352 $PageName = preg_replace(
"/[^a-zA-Z0-9_.-]/",
"", $PageName);
353 $this->PageName = $PageName;
355 # buffer any output from includes or PHP file
358 # include any files needed to set up execution environment
359 foreach ($this->EnvIncludes as $IncludeFile)
361 include($IncludeFile);
365 $this->
SignalEvent(
"EVENT_PAGE_LOAD", array(
"PageName" => $PageName));
367 # signal PHP file load
368 $SignalResult = $this->
SignalEvent(
"EVENT_PHP_FILE_LOAD", array(
369 "PageName" => $PageName));
371 # if signal handler returned new page name value
372 $NewPageName = $PageName;
373 if (($SignalResult[
"PageName"] != $PageName)
374 && strlen($SignalResult[
"PageName"]))
376 # if new page name value is page file
377 if (file_exists($SignalResult[
"PageName"]))
379 # use new value for PHP file name
380 $PageFile = $SignalResult[
"PageName"];
384 # use new value for page name
385 $NewPageName = $SignalResult[
"PageName"];
388 # update local copy of page name
389 $this->PageName = $NewPageName;
392 # if we do not already have a PHP file
393 if (!isset($PageFile))
395 # look for PHP file for page
396 $OurPageFile =
"pages/".$NewPageName.
".php";
397 $LocalPageFile =
"local/pages/".$NewPageName.
".php";
398 $PageFile = file_exists($LocalPageFile) ? $LocalPageFile
399 : (file_exists($OurPageFile) ? $OurPageFile
400 :
"pages/".$this->DefaultPage.
".php");
406 # save buffered output to be displayed later after HTML file loads
407 $PageOutput = ob_get_contents();
410 # signal PHP file load is complete
412 $Context[
"Variables"] = get_defined_vars();
414 array(
"PageName" => $PageName,
"Context" => $Context));
415 $PageCompleteOutput = ob_get_contents();
418 # set up for possible TSR (Terminate and Stay Resident :))
419 $ShouldTSR = $this->PrepForTSR();
421 # if PHP file indicated we should autorefresh to somewhere else
422 if ($this->JumpToPage)
424 if (!strlen(trim($PageOutput)))
428 <meta http-equiv=
"refresh" content=
"0; URL=<?PHP
429 print($this->JumpToPage); ?>">
431 <body bgcolor=
"white">
436 # else if HTML loading is not suppressed
437 elseif (!$this->SuppressHTML)
439 # set content-type to get rid of diacritic errors
440 header(
"Content-Type: text/html; charset="
443 # load common HTML file (defines common functions) if available
444 $CommonHtmlFile = $this->FindFile($this->IncludeDirList,
445 "Common", array(
"tpl",
"html"));
446 if ($CommonHtmlFile) { include($CommonHtmlFile); }
449 $this->LoadUIFunctions();
451 # begin buffering content
454 # signal HTML file load
455 $SignalResult = $this->
SignalEvent(
"EVENT_HTML_FILE_LOAD", array(
456 "PageName" => $PageName));
458 # if signal handler returned new page name value
459 $NewPageName = $PageName;
460 $PageContentFile = NULL;
461 if (($SignalResult[
"PageName"] != $PageName)
462 && strlen($SignalResult[
"PageName"]))
464 # if new page name value is HTML file
465 if (file_exists($SignalResult[
"PageName"]))
467 # use new value for HTML file name
468 $PageContentFile = $SignalResult[
"PageName"];
472 # use new value for page name
473 $NewPageName = $SignalResult[
"PageName"];
477 # load page content HTML file if available
478 if ($PageContentFile === NULL)
480 $PageContentFile = $this->FindFile(
481 $this->InterfaceDirList, $NewPageName,
482 array(
"tpl",
"html"));
484 if ($PageContentFile)
486 include($PageContentFile);
490 print
"<h2>ERROR: No HTML/TPL template found"
491 .
" for this page.</h2>";
494 # signal HTML file load complete
495 $SignalResult = $this->
SignalEvent(
"EVENT_HTML_FILE_LOAD_COMPLETE");
497 # stop buffering and save output
498 $PageContentOutput = ob_get_contents();
501 # load page start HTML file if available
503 $PageStartFile = $this->FindFile($this->IncludeDirList,
"Start",
504 array(
"tpl",
"html"), array(
"StdPage",
"StandardPage"));
505 if ($PageStartFile) { include($PageStartFile); }
506 $PageStartOutput = ob_get_contents();
509 # load page end HTML file if available
511 $PageEndFile = $this->FindFile($this->IncludeDirList,
"End",
512 array(
"tpl",
"html"), array(
"StdPage",
"StandardPage"));
513 if ($PageEndFile) { include($PageEndFile); }
514 $PageEndOutput = ob_get_contents();
517 # get list of any required files not loaded
518 $RequiredFiles = $this->GetRequiredFilesNotYetLoaded($PageContentFile);
520 # if a browser detection function has been made available
521 if (is_callable($this->BrowserDetectFunc))
523 # call function to get browser list
524 $Browsers = call_user_func($this->BrowserDetectFunc);
526 # for each required file
527 $NewRequiredFiles = array();
528 foreach ($RequiredFiles as $File)
530 # if file name includes browser keyword
531 if (preg_match(
"/%BROWSER%/", $File))
534 foreach ($Browsers as $Browser)
536 # substitute in browser name and add to new file list
537 $NewRequiredFiles[] = preg_replace(
538 "/%BROWSER%/", $Browser, $File);
543 # add to new file list
544 $NewRequiredFiles[] = $File;
547 $RequiredFiles = $NewRequiredFiles;
550 # for each required file
551 foreach ($RequiredFiles as $File)
553 # locate specific file to use
554 $FilePath = $this->
GUIFile($File);
559 # determine file type
560 $NamePieces = explode(
".", $File);
561 $FileSuffix = strtolower(array_pop($NamePieces));
563 # add file to HTML output based on file type
564 $FilePath = htmlspecialchars($FilePath);
568 $Tag =
'<script type="text/javascript" src="'
569 .$FilePath.
'"></script>';
570 $PageEndOutput = preg_replace(
571 "#</body>#i", $Tag.
"\n</body>", $PageEndOutput, 1);
575 $Tag =
'<link rel="stylesheet" type="text/css"'
576 .
' media="all" href="'.$FilePath.
'">';
577 $PageStartOutput = preg_replace(
578 "#</head>#i", $Tag.
"\n</head>", $PageStartOutput, 1);
585 $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
587 # perform any regular expression replacements in output
588 $FullPageOutput = preg_replace($this->OutputModificationPatterns,
589 $this->OutputModificationReplacements, $FullPageOutput);
591 # perform any callback replacements in output
592 foreach ($this->OutputModificationCallbacks as $Info)
594 $this->OutputModificationCallbackInfo = $Info;
595 $FullPageOutput = preg_replace_callback($Info[
"SearchPattern"],
596 array($this,
"OutputModificationCallbackShell"),
600 # if relative paths may not work because we were invoked via clean URL
601 if ($this->CleanUrlRewritePerformed || self::WasUrlRewritten())
603 # if using the <base> tag is okay
607 # add <base> tag to header
608 $PageStartOutput = preg_replace(
"%<head>%",
609 "<head><base href=\"".$BaseUrl.
"\" />",
612 # re-assemble full page with new header
613 $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
615 # the absolute URL to the current page
618 # make HREF attribute values with just a fragment ID
619 # absolute since they don't work with the <base> tag because
620 # they are relative to the current page/URL, not the site
622 $FullPageOutput = preg_replace(
623 array(
"%href=\"(#[^:\" ]+)\"%i",
"%href='(#[^:' ]+)'%i"),
624 array(
"href=\"".$FullUrl.
"$1\"",
"href='".$FullUrl.
"$1'"),
629 # try to fix any relative paths throughout code
630 $FullPageOutput = preg_replace(array(
631 "%src=\"/?([^?*:;{}\\\\\" ]+)\.(js|css|gif|png|jpg)\"%i",
632 "%src='/?([^?*:;{}\\\\' ]+)\.(js|css|gif|png|jpg)'%i",
633 # don
't rewrite HREF attributes that are just
634 # fragment IDs because they are relative to the
635 # current page/URL, not the site root
636 "%href=\"/?([^#][^:\" ]*)\"%i",
637 "%href='/?([^#][^:
' ]*)'%i
",
638 "%action=\
"/?([^#][^:\" ]*)\"%i",
639 "%action='/?([^#][^:' ]*)'%i",
640 "%@import\s+url\(\"/?([^:\" ]+)\"\s*\)%i",
641 "%@import\s+url\('/?([^:\" ]+)'\s*\)%i",
642 "%@import\s+\"/?([^:\" ]+)\"\s*%i",
643 "%@import\s+'/?([^:\" ]+)'\s*%i",
646 "src=\"".$BaseUrl.
"$1.$2\"",
647 "src=\"".$BaseUrl.
"$1.$2\"",
648 "href=\"".$BaseUrl.
"$1\"",
649 "href=\"".$BaseUrl.
"$1\"",
650 "action=\"".$BaseUrl.
"$1\"",
651 "action=\"".$BaseUrl.
"$1\"",
652 "@import url(\"".$BaseUrl.
"$1\")",
653 "@import url('".$BaseUrl.
"$1')",
654 "@import \"".$BaseUrl.
"$1\"",
655 "@import '".$BaseUrl.
"$1'",
661 # provide the opportunity to modify full page output
662 $SignalResult = $this->
SignalEvent(
"EVENT_PAGE_OUTPUT_FILTER", array(
663 "PageOutput" => $FullPageOutput));
664 if (isset($SignalResult[
"PageOutput"])
665 && strlen($SignalResult[
"PageOutput"]))
667 $FullPageOutput = $SignalResult[
"PageOutput"];
670 # write out full page
671 print $FullPageOutput;
674 # run any post-processing routines
675 foreach ($this->PostProcessingFuncs as $Func)
677 call_user_func_array($Func[
"FunctionName"], $Func[
"Arguments"]);
680 # write out any output buffered from page code execution
681 if (strlen($PageOutput))
683 if (!$this->SuppressHTML)
685 ?><table width=
"100%" cellpadding=
"5"
686 style=
"border: 2px solid #666666; background: #CCCCCC;
687 font-family: Courier New, Courier, monospace;
688 margin-top: 10px;"><tr><td><?
PHP
690 if ($this->JumpToPage)
692 ?><div style=
"color: #666666;"><span style=
"font-size: 150%;">
693 <b>Page Jump Aborted</b></span>
694 (because of error or other unexpected output)<br />
696 <i><?
PHP print($this->JumpToPage); ?></i></div><?
PHP
699 if (!$this->SuppressHTML)
701 ?></td></tr></table><?
PHP
705 # write out any output buffered from the page code execution complete signal
706 if (!$this->JumpToPage && !$this->SuppressHTML && strlen($PageCompleteOutput))
708 print $PageCompleteOutput;
711 # execute callbacks that should not have their output buffered
712 foreach ($this->UnbufferedCallbacks as $Callback)
714 call_user_func_array($Callback[0], $Callback[1]);
717 # log high memory usage
718 if (function_exists(
"memory_get_peak_usage"))
723 && (memory_get_peak_usage() >= $MemoryThreshold))
725 $HighMemUsageMsg =
"High peak memory usage ("
726 .intval(memory_get_peak_usage()).
") for "
727 .$this->FullUrl().
" from "
728 .$_SERVER[
"REMOTE_ADDR"];
729 $this->
LogMessage(self::LOGLVL_INFO, $HighMemUsageMsg);
733 # log slow page loads
738 $SlowPageLoadMsg =
"Slow page load ("
740 .$this->FullUrl().
" from "
741 .$_SERVER[
"REMOTE_ADDR"];
742 $this->
LogMessage(self::LOGLVL_INFO, $SlowPageLoadMsg);
745 # terminate and stay resident (TSR!) if indicated and HTML has been output
746 # (only TSR if HTML has been output because otherwise browsers will misbehave)
747 if ($ShouldTSR) { $this->LaunchTSR(); }
757 return $this->PageName;
767 # retrieve current URL
770 # remove the base path if present
771 $BasePath = $this->Settings[
"BasePath"];
772 if (stripos($Url, $BasePath) === 0)
774 $Url = substr($Url, strlen($BasePath));
802 && (strpos($Page,
"?") === FALSE)
803 && ((strpos($Page,
"=") !== FALSE)
804 || ((stripos($Page,
".php") === FALSE)
805 && (stripos($Page,
".htm") === FALSE)
806 && (strpos($Page,
"/") === FALSE)))
807 && (stripos($Page,
"http://") !== 0)
808 && (stripos($Page,
"https://") !== 0))
810 $this->JumpToPage = self::BaseUrl() .
"index.php?P=".$Page;
814 $this->JumpToPage = $Page;
824 return ($this->JumpToPage === NULL) ? FALSE : TRUE;
838 if ($NewSetting !== NULL) { $this->
HtmlCharset = $NewSetting; }
839 return $this->HtmlCharset;
851 return $this->UseMinimizedJavascript;
866 if ($NewValue !== NULL) { $this->
UseBaseTag = $NewValue ? TRUE : FALSE; }
867 return $this->UseBaseTag;
878 $this->SuppressHTML = $NewSetting;
889 if ($UIName !== NULL)
891 $this->ActiveUI = preg_replace(
"/^SPTUI--/",
"", $UIName);
893 return $this->ActiveUI;
903 # possible UI directories
904 $InterfaceDirs = array(
908 # start out with an empty list
909 $Interfaces = array();
911 # for each possible UI directory
912 foreach ($InterfaceDirs as $InterfaceDir)
914 $Dir = dir($InterfaceDir);
916 # for each file in current directory
917 while (($DirEntry = $Dir->read()) !== FALSE)
919 $InterfacePath = $InterfaceDir.
"/".$DirEntry;
921 # skip anything that doesn't have a name in the required format
922 if (!preg_match(
'/^[a-zA-Z0-9]+$/', $DirEntry))
927 # skip anything that isn't a directory
928 if (!is_dir($InterfacePath))
933 # read the UI name (if available)
934 $UIName = @file_get_contents($InterfacePath.
"/NAME");
936 # use the directory name if the UI name isn't available
937 if ($UIName === FALSE || !strlen($UIName))
942 $Interfaces[$InterfacePath] = $UIName;
948 # return list to caller
968 &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE,
969 &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE,
970 &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE)
972 $FuncIndex = count($this->PostProcessingFuncs);
973 $this->PostProcessingFuncs[$FuncIndex][
"FunctionName"] = $FunctionName;
974 $this->PostProcessingFuncs[$FuncIndex][
"Arguments"] = array();
976 while (isset(${
"Arg".$Index}) && (${
"Arg".$Index} !== self::NOVALUE))
978 $this->PostProcessingFuncs[$FuncIndex][
"Arguments"][$Index]
991 $this->EnvIncludes[] = $FileName;
1002 # determine if the file is an image or JavaScript file
1003 $FileIsImage = preg_match(
"/\.(gif|jpg|png)$/", $FileName);
1004 $FileIsJavascript = preg_match(
"/\.js$/", $FileName);
1006 # determine which location to search based on file suffix
1007 $DirList = $FileIsImage ? $this->ImageDirList : $this->IncludeDirList;
1009 # if directed to get a minimized JavaScript file
1012 # first try to find the minimized JavaScript file
1013 $MinimizedFileName = substr_replace($FileName,
".min", -3, 0);
1014 $FoundFileName = $this->FindFile($DirList, $MinimizedFileName);
1016 # search for the regular file if a minimized file wasn't found
1017 if (is_null($FoundFileName))
1019 $FoundFileName = $this->FindFile($DirList, $FileName);
1023 # otherwise just search for the file
1026 $FoundFileName = $this->FindFile($DirList, $FileName);
1029 # add non-image files to list of found files (used for required files loading)
1030 if (!$FileIsImage) { $this->FoundUIFiles[] = basename($FoundFileName); }
1032 # return file name to caller
1033 return $FoundFileName;
1047 $FullFileName = $this->
GUIFile($FileName);
1048 if ($FullFileName) { print($FullFileName); }
1060 $this->AdditionalRequiredUIFiles[] = $FileName;
1073 # if specified function is not currently available
1074 if (!is_callable($Callback))
1076 # if function info looks legal
1077 if (is_string($Callback) && strlen($Callback))
1079 # start with function directory list
1080 $Locations = $this->FunctionDirList;
1082 # add object directories to list
1083 $Locations = array_merge(
1084 $Locations, array_keys(self::$ObjectDirectories));
1086 # look for function file
1087 $FunctionFileName = $this->FindFile($Locations,
"F-".$Callback,
1088 array(
"php",
"html"));
1090 # if function file was found
1091 if ($FunctionFileName)
1093 # load function file
1094 include_once($FunctionFileName);
1098 # log error indicating function load failed
1099 $this->
LogError(self::LOGLVL_ERROR,
"Unable to load function"
1100 .
" for callback \"".$Callback.
"\".");
1105 # log error indicating specified function info was bad
1106 $this->
LogError(self::LOGLVL_ERROR,
"Unloadable callback value"
1108 .
" passed to AF::LoadFunction().");
1112 # report to caller whether function load succeeded
1113 return is_callable($Callback);
1122 return microtime(TRUE) - $this->ExecutionStartTime;
1137 # ---- Logging -----------------------------------------------------------
1165 return $this->
UpdateSetting(
"SlowPageLoadThreshold", $NewValue);
1180 return $this->
UpdateSetting(
"LogHighMemoryUsage", $NewValue);
1193 return $this->
UpdateSetting(
"HighMemoryUsageThreshold", $NewValue);
1211 # if error level is at or below current logging level
1212 if ($this->Settings[
"LoggingLevel"] >= $Level)
1214 # attempt to log error message
1217 # if logging attempt failed and level indicated significant error
1218 if (($Result === FALSE) && ($Level <= self::LOGLVL_ERROR))
1220 # throw exception about inability to log error
1221 static $AlreadyThrewException = FALSE;
1222 if (!$AlreadyThrewException)
1224 $AlreadyThrewException = TRUE;
1225 throw new Exception(
"Unable to log error (".$Level.
": ".$Msg.
").");
1229 # report to caller whether message was logged
1234 # report to caller that message was not logged
1252 # if message level is at or below current logging level
1253 if ($this->Settings[
"LoggingLevel"] >= $Level)
1255 # attempt to open log file
1256 $FHndl = @fopen($this->LogFileName,
"a");
1258 # if log file could not be open
1259 if ($FHndl === FALSE)
1261 # report to caller that message was not logged
1267 $ErrorAbbrevs = array(
1268 self::LOGLVL_FATAL =>
"FTL",
1269 self::LOGLVL_ERROR =>
"ERR",
1270 self::LOGLVL_WARNING =>
"WRN",
1271 self::LOGLVL_INFO =>
"INF",
1272 self::LOGLVL_DEBUG =>
"DBG",
1273 self::LOGLVL_TRACE =>
"TRC",
1275 $LogEntry = date(
"Y-m-d H:i:s")
1276 .
" ".($this->RunningInBackground ?
"B" :
"F")
1277 .
" ".$ErrorAbbrevs[$Level]
1280 # write entry to log
1281 $Success = fwrite($FHndl, $LogEntry.
"\n");
1286 # report to caller whether message was logged
1287 return ($Success === FALSE) ? FALSE : TRUE;
1292 # report to caller that message was not logged
1320 # constrain new level (if supplied) to within legal bounds
1323 $NewValue = max(min($NewValue, 6), 1);
1326 # set new logging level (if supplied) and return current level to caller
1338 if ($NewValue !== NULL) { $this->LogFileName = $NewValue; }
1339 return $this->LogFileName;
1378 # ---- Event Handling ----------------------------------------------------
1424 # convert parameters to array if not already in that form
1425 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
1426 : array($EventsOrEventName => $EventType);
1429 foreach ($Events as $Name => $Type)
1431 # store event information
1432 $this->RegisteredEvents[$Name][
"Type"] = $Type;
1433 $this->RegisteredEvents[$Name][
"Hooks"] = array();
1445 return array_key_exists($EventName, $this->RegisteredEvents)
1457 # the event isn't hooked to if it isn't even registered
1463 # return TRUE if there is at least one callback hooked to the event
1464 return count($this->RegisteredEvents[$EventName][
"Hooks"]) > 0;
1480 function HookEvent($EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
1482 # convert parameters to array if not already in that form
1483 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
1484 : array($EventsOrEventName => $Callback);
1488 foreach ($Events as $EventName => $EventCallback)
1490 # if callback is valid
1491 if (is_callable($EventCallback))
1493 # if this is a periodic event we process internally
1494 if (isset($this->PeriodicEvents[$EventName]))
1497 $this->ProcessPeriodicEvent($EventName, $EventCallback);
1499 # if specified event has been registered
1500 elseif (isset($this->RegisteredEvents[$EventName]))
1502 # add callback for event
1503 $this->RegisteredEvents[$EventName][
"Hooks"][]
1504 = array(
"Callback" => $EventCallback,
"Order" => $Order);
1506 # sort callbacks by order
1507 if (count($this->RegisteredEvents[$EventName][
"Hooks"]) > 1)
1509 usort($this->RegisteredEvents[$EventName][
"Hooks"],
1510 array(
"ApplicationFramework",
"HookEvent_OrderCompare"));
1524 # report to caller whether all callbacks were hooked
1528 private static function HookEvent_OrderCompare($A, $B)
1530 if ($A[
"Order"] == $B[
"Order"]) {
return 0; }
1531 return ($A[
"Order"] < $B[
"Order"]) ? -1 : 1;
1546 $ReturnValue = NULL;
1548 # if event has been registered
1549 if (isset($this->RegisteredEvents[$EventName]))
1551 # set up default return value (if not NULL)
1552 switch ($this->RegisteredEvents[$EventName][
"Type"])
1554 case self::EVENTTYPE_CHAIN:
1555 $ReturnValue = $Parameters;
1558 case self::EVENTTYPE_NAMED:
1559 $ReturnValue = array();
1563 # for each callback for this event
1564 foreach ($this->RegisteredEvents[$EventName][
"Hooks"] as $Hook)
1567 $Callback = $Hook[
"Callback"];
1568 $Result = ($Parameters !== NULL)
1569 ? call_user_func_array($Callback, $Parameters)
1570 : call_user_func($Callback);
1572 # process return value based on event type
1573 switch ($this->RegisteredEvents[$EventName][
"Type"])
1575 case self::EVENTTYPE_CHAIN:
1576 if ($Result !== NULL)
1578 foreach ($Parameters as $Index => $Value)
1580 if (array_key_exists($Index, $Result))
1582 $Parameters[$Index] = $Result[$Index];
1585 $ReturnValue = $Parameters;
1589 case self::EVENTTYPE_FIRST:
1590 if ($Result !== NULL)
1592 $ReturnValue = $Result;
1597 case self::EVENTTYPE_NAMED:
1598 $CallbackName = is_array($Callback)
1599 ? (is_object($Callback[0])
1600 ? get_class($Callback[0])
1601 : $Callback[0]).
"::".$Callback[1]
1603 $ReturnValue[$CallbackName] = $Result;
1613 $this->
LogError(self::LOGLVL_WARNING,
1614 "Unregistered event signaled (".$EventName.
").");
1617 # return value if any to caller
1618 return $ReturnValue;
1628 return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE;
1643 # if event is not a periodic event report failure to caller
1644 if (!array_key_exists($EventName, $this->EventPeriods)) {
return FALSE; }
1646 # retrieve last execution time for event if available
1647 $Signature = self::GetCallbackSignature($Callback);
1648 $LastRunTime = $this->DB->Query(
"SELECT LastRunAt FROM PeriodicEvents"
1649 .
" WHERE Signature = '".addslashes($Signature).
"'",
"LastRunAt");
1651 # if event was not found report failure to caller
1652 if ($LastRunTime === NULL) {
return FALSE; }
1654 # calculate next run time based on event period
1655 $NextRunTime = strtotime($LastRunTime) + $this->EventPeriods[$EventName];
1657 # report next run time to caller
1658 return $NextRunTime;
1678 # retrieve last execution times
1679 $this->DB->Query(
"SELECT * FROM PeriodicEvents");
1680 $LastRunTimes = $this->DB->FetchColumn(
"LastRunAt",
"Signature");
1682 # for each known event
1684 foreach ($this->KnownPeriodicEvents as $Signature => $Info)
1686 # if last run time for event is available
1687 if (array_key_exists($Signature, $LastRunTimes))
1689 # calculate next run time for event
1690 $LastRun = strtotime($LastRunTimes[$Signature]);
1691 $NextRun = $LastRun + $this->EventPeriods[$Info[
"Period"]];
1692 if ($Info[
"Period"] ==
"EVENT_PERIODIC") { $LastRun = FALSE; }
1696 # set info to indicate run times are not known
1701 # add event info to list
1702 $Events[$Signature] = $Info;
1703 $Events[$Signature][
"LastRun"] = $LastRun;
1704 $Events[$Signature][
"NextRun"] = $NextRun;
1705 $Events[$Signature][
"Parameters"] = NULL;
1708 # return list of known events to caller
1715 # ---- Task Management ---------------------------------------------------
1741 $Priority = self::PRIORITY_LOW, $Description =
"")
1743 # pack task info and write to database
1744 if ($Parameters === NULL) { $Parameters = array(); }
1745 $this->DB->Query(
"INSERT INTO TaskQueue"
1746 .
" (Callback, Parameters, Priority, Description)"
1747 .
" VALUES ('".addslashes(serialize($Callback)).
"', '"
1748 .addslashes(serialize($Parameters)).
"', ".intval($Priority).
", '"
1749 .addslashes($Description).
"')");
1770 $Priority = self::PRIORITY_LOW, $Description =
"")
1774 $QueryResult = $this->DB->Query(
"SELECT TaskId,Priority FROM TaskQueue"
1775 .
" WHERE Callback = '".addslashes(serialize($Callback)).
"'"
1776 .($Parameters ?
" AND Parameters = '"
1777 .addslashes(serialize($Parameters)).
"'" :
""));
1778 if ($QueryResult !== FALSE)
1780 $Record = $this->DB->FetchRow();
1781 if ($Record[
"Priority"] > $Priority)
1783 $this->DB->Query(
"UPDATE TaskQueue"
1784 .
" SET Priority = ".intval($Priority)
1785 .
" WHERE TaskId = ".intval($Record[
"TaskId"]));
1792 $this->
QueueTask($Callback, $Parameters, $Priority, $Description);
1808 $QueuedCount = $this->DB->Query(
1809 "SELECT COUNT(*) AS FoundCount FROM TaskQueue"
1810 .
" WHERE Callback = '".addslashes(serialize($Callback)).
"'"
1811 .($Parameters ?
" AND Parameters = '"
1812 .addslashes(serialize($Parameters)).
"'" :
""),
1814 $RunningCount = $this->DB->Query(
1815 "SELECT COUNT(*) AS FoundCount FROM RunningTasks"
1816 .
" WHERE Callback = '".addslashes(serialize($Callback)).
"'"
1817 .($Parameters ?
" AND Parameters = '"
1818 .addslashes(serialize($Parameters)).
"'" :
""),
1820 $FoundCount = $QueuedCount + $RunningCount;
1821 return ($FoundCount ? TRUE : FALSE);
1843 return $this->GetTaskList(
"SELECT * FROM TaskQueue"
1844 .
" ORDER BY Priority, TaskId ", $Count, $Offset);
1861 $Parameters = NULL, $Priority = NULL, $Description = NULL)
1863 $Query =
"SELECT COUNT(*) AS TaskCount FROM TaskQueue";
1865 if ($Callback !== NULL)
1867 $Query .= $Sep.
" Callback = '".addslashes(serialize($Callback)).
"'";
1870 if ($Parameters !== NULL)
1872 $Query .= $Sep.
" Parameters = '".addslashes(serialize($Parameters)).
"'";
1875 if ($Priority !== NULL)
1877 $Query .= $Sep.
" Priority = ".intval($Priority);
1880 if ($Description !== NULL)
1882 $Query .= $Sep.
" Description = '".addslashes($Description).
"'";
1884 return $this->DB->Query($Query,
"TaskCount");
1896 return $this->GetTaskList(
"SELECT * FROM RunningTasks"
1897 .
" WHERE StartedAt >= '".date(
"Y-m-d H:i:s",
1898 (time() - ini_get(
"max_execution_time"))).
"'"
1899 .
" ORDER BY StartedAt", $Count, $Offset);
1911 return $this->GetTaskList(
"SELECT * FROM RunningTasks"
1912 .
" WHERE StartedAt < '".date(
"Y-m-d H:i:s",
1913 (time() - ini_get(
"max_execution_time"))).
"'"
1914 .
" ORDER BY StartedAt", $Count, $Offset);
1923 return $this->DB->Query(
"SELECT COUNT(*) AS Count FROM RunningTasks"
1924 .
" WHERE StartedAt < '".date(
"Y-m-d H:i:s",
1925 (time() - ini_get(
"max_execution_time"))).
"'",
1936 $this->DB->Query(
"LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
1937 $this->DB->Query(
"INSERT INTO TaskQueue"
1938 .
" (Callback,Parameters,Priority,Description) "
1939 .
"SELECT Callback, Parameters, Priority, Description"
1940 .
" FROM RunningTasks WHERE TaskId = ".intval($TaskId));
1941 if ($NewPriority !== NULL)
1943 $NewTaskId = $this->DB->LastInsertId();
1944 $this->DB->Query(
"UPDATE TaskQueue SET Priority = "
1945 .intval($NewPriority)
1946 .
" WHERE TaskId = ".intval($NewTaskId));
1948 $this->DB->Query(
"DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
1949 $this->DB->Query(
"UNLOCK TABLES");
1958 $this->DB->Query(
"DELETE FROM TaskQueue WHERE TaskId = ".intval($TaskId));
1959 $this->DB->Query(
"DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
1971 # assume task will not be found
1974 # look for task in task queue
1975 $this->DB->Query(
"SELECT * FROM TaskQueue WHERE TaskId = ".intval($TaskId));
1977 # if task was not found in queue
1978 if (!$this->DB->NumRowsSelected())
1980 # look for task in running task list
1981 $this->DB->Query(
"SELECT * FROM RunningTasks WHERE TaskId = "
1986 if ($this->DB->NumRowsSelected())
1988 # if task was periodic
1989 $Row = $this->DB->FetchRow();
1990 if ($Row[
"Callback"] ==
1991 serialize(array(
"ApplicationFramework",
"PeriodicEventWrapper")))
1993 # unpack periodic task callback
1994 $WrappedCallback = unserialize($Row[
"Parameters"]);
1995 $Task[
"Callback"] = $WrappedCallback[1];
1996 $Task[
"Parameters"] = $WrappedCallback[2];
2000 # unpack task callback and parameters
2001 $Task[
"Callback"] = unserialize($Row[
"Callback"]);
2002 $Task[
"Parameters"] = unserialize($Row[
"Parameters"]);
2006 # return task to caller
2019 return $this->
UpdateSetting(
"TaskExecutionEnabled", $NewValue);
2041 if (func_num_args() && !ini_get(
"safe_mode"))
2043 if ($NewValue != $this->Settings[
"MaxExecTime"])
2045 $this->Settings[
"MaxExecTime"] = max($NewValue, 5);
2046 $this->DB->Query(
"UPDATE ApplicationFrameworkSettings"
2047 .
" SET MaxExecTime = '"
2048 .intval($this->Settings[
"MaxExecTime"]).
"'");
2050 ini_set(
"max_execution_time", $this->Settings[
"MaxExecTime"]);
2051 set_time_limit($this->Settings[
"MaxExecTime"]);
2053 return ini_get(
"max_execution_time");
2059 # ---- Clean URL Support -------------------------------------------------
2089 function AddCleanUrl($Pattern, $Page, $GetVars = NULL, $Template = NULL)
2091 # save clean URL mapping parameters
2092 $this->CleanUrlMappings[] = array(
2093 "Pattern" => $Pattern,
2095 "GetVars" => $GetVars,
2098 # if replacement template specified
2099 if ($Template !== NULL)
2101 # if GET parameters specified
2102 if (count($GetVars))
2104 # retrieve all possible permutations of GET parameters
2105 $GetPerms = $this->ArrayPermutations(array_keys($GetVars));
2107 # for each permutation of GET parameters
2108 foreach ($GetPerms as $VarPermutation)
2110 # construct search pattern for permutation
2111 $SearchPattern =
"/href=([\"'])index\\.php\\?P=".$Page;
2112 $GetVarSegment =
"";
2113 foreach ($VarPermutation as $GetVar)
2115 if (preg_match(
"%\\\$[0-9]+%", $GetVars[$GetVar]))
2117 $GetVarSegment .=
"&".$GetVar.
"=((?:(?!\\1)[^&])+)";
2121 $GetVarSegment .=
"&".$GetVar.
"=".$GetVars[$GetVar];
2124 $SearchPattern .= $GetVarSegment.
"\\1/i";
2126 # if template is actually a callback
2127 if (is_callable($Template))
2129 # add pattern to HTML output mod callbacks list
2130 $this->OutputModificationCallbacks[] = array(
2131 "Pattern" => $Pattern,
2133 "SearchPattern" => $SearchPattern,
2134 "Callback" => $Template,
2139 # construct replacement string for permutation
2140 $Replacement = $Template;
2142 foreach ($VarPermutation as $GetVar)
2144 $Replacement = str_replace(
2145 "\$".$GetVar,
"\$".$Index, $Replacement);
2148 $Replacement =
"href=\"".$Replacement.
"\"";
2150 # add pattern to HTML output modifications list
2151 $this->OutputModificationPatterns[] = $SearchPattern;
2152 $this->OutputModificationReplacements[] = $Replacement;
2158 # construct search pattern
2159 $SearchPattern =
"/href=\"index\\.php\\?P=".$Page.
"\"/i";
2161 # if template is actually a callback
2162 if (is_callable($Template))
2164 # add pattern to HTML output mod callbacks list
2165 $this->OutputModificationCallbacks[] = array(
2166 "Pattern" => $Pattern,
2168 "SearchPattern" => $SearchPattern,
2169 "Callback" => $Template,
2174 # add simple pattern to HTML output modifications list
2175 $this->OutputModificationPatterns[] = $SearchPattern;
2176 $this->OutputModificationReplacements[] =
"href=\"".$Template.
"\"";
2189 foreach ($this->CleanUrlMappings as $Info)
2191 if (preg_match($Info[
"Pattern"], $Path))
2207 # the search patterns and callbacks require a specific format
2208 $Format =
"href=\"".str_replace(
"&",
"&", $Path).
"\"";
2211 # perform any regular expression replacements on the search string
2212 $Search = preg_replace(
2213 $this->OutputModificationPatterns,
2214 $this->OutputModificationReplacements,
2217 # only run the callbacks if a replacement hasn't already been performed
2218 if ($Search == $Format)
2220 # perform any callback replacements on the search string
2221 foreach ($this->OutputModificationCallbacks as $Info)
2223 # make the information available to the callback
2224 $this->OutputModificationCallbackInfo = $Info;
2226 # execute the callback
2227 $Search = preg_replace_callback(
2228 $Info[
"SearchPattern"],
2229 array($this,
"OutputModificationCallbackShell"),
2234 # return the path untouched if no replacements were performed
2235 if ($Search == $Format)
2240 # remove the bits added to the search string to get it recognized by
2241 # the replacement expressions and callbacks
2242 $Result = substr($Search, 6, -1);
2255 # for each clean URL mapping
2256 foreach ($this->CleanUrlMappings as $Info)
2258 # if current path matches the clean URL pattern
2259 if (preg_match($Info[
"Pattern"], $Path, $Matches))
2261 # the GET parameters for the URL, starting with the page name
2262 $GetVars = array(
"P" => $Info[
"Page"]);
2264 # if additional $_GET variables specified for clean URL
2265 if ($Info[
"GetVars"] !== NULL)
2267 # for each $_GET variable specified for clean URL
2268 foreach ($Info[
"GetVars"] as $VarName => $VarTemplate)
2270 # start with template for variable value
2271 $Value = $VarTemplate;
2273 # for each subpattern matched in current URL
2274 foreach ($Matches as $Index => $Match)
2276 # if not first (whole) match
2279 # make any substitutions in template
2280 $Value = str_replace(
"$".$Index, $Match, $Value);
2284 # add the GET variable
2285 $GetVars[$VarName] = $Value;
2289 # return the unclean URL
2290 return "index.php?" . http_build_query($GetVars);
2294 # return the path unchanged
2314 $GetVars = array(
"P" => $this->
GetPageName()) + $_GET;
2315 return "index.php?" . http_build_query($GetVars);
2321 # ---- Server Environment ------------------------------------------------
2332 if ($NewValue !== NULL)
2334 self::$SessionLifetime = $NewValue;
2336 return self::$SessionLifetime;
2346 # HTACCESS_SUPPORT is set in the .htaccess file
2347 return isset($_SERVER[
"HTACCESS_SUPPORT"]);
2359 # return override value if one is set
2360 if (self::$RootUrlOverride !== NULL)
2362 return self::$RootUrlOverride;
2365 # determine scheme name
2366 $Protocol = (isset($_SERVER[
"HTTPS"]) ?
"https" :
"http");
2368 # if HTTP_HOST is preferred or SERVER_NAME points to localhost
2369 # and HTTP_HOST is set
2370 if ((self::$PreferHttpHost || ($_SERVER[
"SERVER_NAME"] ==
"127.0.0.1"))
2371 && isset($_SERVER[
"HTTP_HOST"]))
2373 # use HTTP_HOST for domain name
2374 $DomainName = $_SERVER[
"HTTP_HOST"];
2378 # use SERVER_NAME for domain name
2379 $DomainName = $_SERVER[
"HTTP_HOST"];
2382 # build URL root and return to caller
2383 return $Protocol.
"://".$DomainName;
2402 if ($NewValue !== self::NOVALUE)
2404 self::$RootUrlOverride = strlen(trim($NewValue)) ? $NewValue : NULL;
2406 return self::$RootUrlOverride;
2420 $BaseUrl = self::RootUrl().dirname($_SERVER[
"SCRIPT_NAME"]);
2421 if (substr($BaseUrl, -1) !=
"/") { $BaseUrl .=
"/"; }
2434 return self::RootUrl().$_SERVER[
"REQUEST_URI"];
2449 if ($NewValue !== NULL)
2451 self::$PreferHttpHost = ($NewValue ? TRUE : FALSE);
2453 return self::$PreferHttpHost;
2462 $BasePath = dirname($_SERVER[
"SCRIPT_NAME"]);
2464 if (substr($BasePath, -1) !=
"/")
2479 if (array_key_exists(
"SCRIPT_URL", $_SERVER))
2481 return $_SERVER[
"SCRIPT_URL"];
2483 elseif (array_key_exists(
"REDIRECT_URL", $_SERVER))
2485 return $_SERVER[
"REDIRECT_URL"];
2487 elseif (array_key_exists(
"REQUEST_URI", $_SERVER))
2489 $Pieces = parse_url($_SERVER[
"REQUEST_URI"]);
2490 return $Pieces[
"path"];
2508 # needed to get the path of the URL minus the query and fragment pieces
2509 $Components = parse_url(self::GetScriptUrl());
2511 # if parsing was successful and a path is set
2512 if (is_array($Components) && isset($Components[
"path"]))
2514 $BasePath = self::BasePath();
2515 $Path = $Components[
"path"];
2517 # the URL was rewritten if the path isn't the base path, i.e., the
2518 # home page, and the file in the URL isn't the script generating the
2520 if ($BasePath != $Path && basename($Path) != $ScriptName)
2526 # the URL wasn't rewritten
2537 return self::GetPhpMemoryLimit() - memory_get_usage();
2547 $Str = strtoupper(ini_get(
"memory_limit"));
2548 if (substr($Str, -1) ==
"B") { $Str = substr($Str, 0, strlen($Str) - 1); }
2549 switch (substr($Str, -1))
2551 case "K": $MemoryLimit = (int)$Str * 1024;
break;
2552 case "M": $MemoryLimit = (int)$Str * 1048576;
break;
2553 case "G": $MemoryLimit = (int)$Str * 1073741824;
break;
2554 default: $MemoryLimit = (int)$Str;
break;
2556 return $MemoryLimit;
2562 # ---- Backward Compatibility --------------------------------------------
2572 return $this->FindFile(
2573 $this->IncludeDirList, $BaseName, array(
"tpl",
"html"));
2579 # ---- PRIVATE INTERFACE -------------------------------------------------
2581 private $ActiveUI =
"default";
2582 private $BrowserDetectFunc;
2583 private $CleanUrlMappings = array();
2584 private $CleanUrlRewritePerformed = FALSE;
2586 private $DefaultPage =
"Home";
2587 private $EnvIncludes = array();
2588 private $ExecutionStartTime;
2589 private $FoundUIFiles = array();
2590 private $AdditionalRequiredUIFiles = array();
2591 private $HtmlCharset =
"UTF-8";
2592 private $JumpToPage = NULL;
2593 private $LogFileName =
"local/logs/site.log";
2594 private $MaxRunningTasksToTrack = 250;
2595 private $OutputModificationPatterns = array();
2596 private $OutputModificationReplacements = array();
2597 private $OutputModificationCallbacks = array();
2598 private $OutputModificationCallbackInfo;
2600 private $PostProcessingFuncs = array();
2601 private $RunningInBackground = FALSE;
2602 private $RunningTask;
2603 private $SavedContext;
2605 private $SuppressHTML = FALSE;
2606 private $SaveTemplateLocationCache = FALSE;
2607 private $UnbufferedCallbacks = array();
2608 private $UseBaseTag = FALSE;
2609 private $UseMinimizedJavascript = FALSE;
2611 private static $AppName =
"ScoutAF";
2612 private static $ObjectDirectories = array();
2613 private static $ObjectLocationCache;
2614 private static $ObjectLocationCacheInterval = 60;
2615 private static $ObjectLocationCacheExpiration;
2616 private static $PreferHttpHost = FALSE;
2617 private static $RootUrlOverride = NULL;
2618 private static $SaveObjectLocationCache = FALSE;
2619 private static $SessionLifetime = 1440;
2625 private $NoTSR = FALSE;
2627 private $KnownPeriodicEvents = array();
2628 private $PeriodicEvents = array(
2629 "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT,
2630 "EVENT_DAILY" => self::EVENTTYPE_DEFAULT,
2631 "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT,
2632 "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT,
2633 "EVENT_PERIODIC" => self::EVENTTYPE_NAMED,
2635 private $EventPeriods = array(
2636 "EVENT_HOURLY" => 3600,
2637 "EVENT_DAILY" => 86400,
2638 "EVENT_WEEKLY" => 604800,
2639 "EVENT_MONTHLY" => 2592000,
2640 "EVENT_PERIODIC" => 0,
2642 private $UIEvents = array(
2643 "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT,
2644 "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN,
2645 "EVENT_PHP_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
2646 "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN,
2647 "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
2648 "EVENT_PAGE_OUTPUT_FILTER" => self::EVENTTYPE_CHAIN,
2654 private function LoadSettings()
2656 # read settings in from database
2657 $this->DB->Query(
"SELECT * FROM ApplicationFrameworkSettings");
2658 $this->Settings = $this->DB->FetchRow();
2660 # if settings were not previously initialized
2661 if (!$this->Settings)
2663 # initialize settings in database
2664 $this->DB->Query(
"INSERT INTO ApplicationFrameworkSettings"
2665 .
" (LastTaskRunAt) VALUES ('2000-01-02 03:04:05')");
2667 # read new settings in from database
2668 $this->DB->Query(
"SELECT * FROM ApplicationFrameworkSettings");
2669 $this->Settings = $this->DB->FetchRow();
2672 # if base path was not previously set or we appear to have moved
2673 if (!array_key_exists(
"BasePath", $this->Settings)
2674 || (!strlen($this->Settings[
"BasePath"]))
2675 || (!array_key_exists(
"BasePathCheck", $this->Settings))
2676 || (__FILE__ != $this->Settings[
"BasePathCheck"]))
2678 # attempt to extract base path from Apache .htaccess file
2679 if (is_readable(
".htaccess"))
2681 $Lines = file(
".htaccess");
2682 foreach ($Lines as $Line)
2684 if (preg_match(
"/\\s*RewriteBase\\s+/", $Line))
2686 $Pieces = preg_split(
2687 "/\\s+/", $Line, NULL, PREG_SPLIT_NO_EMPTY);
2688 $BasePath = $Pieces[1];
2693 # if base path was found
2694 if (isset($BasePath))
2696 # save base path locally
2697 $this->Settings[
"BasePath"] = $BasePath;
2699 # save base path to database
2700 $this->DB->Query(
"UPDATE ApplicationFrameworkSettings"
2701 .
" SET BasePath = '".addslashes($BasePath).
"'"
2702 .
", BasePathCheck = '".addslashes(__FILE__).
"'");
2706 # if template location cache has been saved to database
2707 if (isset($this->Settings[
"TemplateLocationCache"]))
2709 # unserialize cache values into array and use if valid
2710 $Cache = unserialize($this->Settings[
"TemplateLocationCache"]);
2711 $this->Settings[
"TemplateLocationCache"] =
2712 count($Cache) ? $Cache : array();
2716 # start with empty cache
2717 $this->Settings[
"TemplateLocationCache"] = array();
2720 # if object location cache has been saved to database
2721 if (isset($this->Settings[
"ObjectLocationCache"]))
2723 # unserialize cache values into array and use if valid
2724 $Cache = unserialize($this->Settings[
"ObjectLocationCache"]);
2725 $this->Settings[
"ObjectLocationCache"] =
2726 count($Cache) ? $Cache : array();
2728 # store static versions for use when autoloading objects
2729 self::$ObjectLocationCache =
2730 $this->Settings[
"ObjectLocationCache"];
2731 self::$ObjectLocationCacheInterval =
2732 $this->Settings[
"ObjectLocationCacheInterval"];
2733 self::$ObjectLocationCacheExpiration =
2734 $this->Settings[
"ObjectLocationCacheExpiration"];
2738 # start with empty cache
2739 $this->Settings[
"ObjectLocationCache"] = array();
2749 private function RewriteCleanUrls($PageName)
2751 # if URL rewriting is supported by the server
2754 # retrieve current URL and remove base path if present
2757 # for each clean URL mapping
2758 foreach ($this->CleanUrlMappings as $Info)
2760 # if current URL matches clean URL pattern
2761 if (preg_match($Info[
"Pattern"], $Url, $Matches))
2764 $PageName = $Info[
"Page"];
2766 # if $_GET variables specified for clean URL
2767 if ($Info[
"GetVars"] !== NULL)
2769 # for each $_GET variable specified for clean URL
2770 foreach ($Info[
"GetVars"] as $VarName => $VarTemplate)
2772 # start with template for variable value
2773 $Value = $VarTemplate;
2775 # for each subpattern matched in current URL
2776 foreach ($Matches as $Index => $Match)
2778 # if not first (whole) match
2781 # make any substitutions in template
2782 $Value = str_replace(
"$".$Index, $Match, $Value);
2786 # set $_GET variable
2787 $_GET[$VarName] = $Value;
2791 # set flag indicating clean URL mapped
2792 $this->CleanUrlRewritePerformed = TRUE;
2794 # stop looking for a mapping
2800 # return (possibly) updated page name to caller
2820 private function FindFile($DirectoryList, $BaseName,
2821 $PossibleSuffixes = NULL, $PossiblePrefixes = NULL)
2823 # generate template cache index for this page
2824 $CacheIndex = md5(serialize($DirectoryList))
2825 .
":".$this->ActiveUI.
":".$BaseName;
2827 # if we have cached location and cache expiration time has not elapsed
2828 if (($this->Settings[
"TemplateLocationCacheInterval"] > 0)
2829 && count($this->Settings[
"TemplateLocationCache"])
2830 && array_key_exists($CacheIndex,
2831 $this->Settings[
"TemplateLocationCache"])
2832 && (time() < strtotime(
2833 $this->Settings[
"TemplateLocationCacheExpiration"])))
2835 # use template location from cache
2836 $FoundFileName = $this->Settings[
2837 "TemplateLocationCache"][$CacheIndex];
2841 # if suffixes specified and base name does not include suffix
2842 if (count($PossibleSuffixes)
2843 && !preg_match(
"/\.[a-zA-Z0-9]+$/", $BaseName))
2845 # add versions of file names with suffixes to file name list
2846 $FileNames = array();
2847 foreach ($PossibleSuffixes as $Suffix)
2849 $FileNames[] = $BaseName.
".".$Suffix;
2854 # use base name as file name
2855 $FileNames = array($BaseName);
2858 # if prefixes specified
2859 if (count($PossiblePrefixes))
2861 # add versions of file names with prefixes to file name list
2862 $NewFileNames = array();
2863 foreach ($FileNames as $FileName)
2865 foreach ($PossiblePrefixes as $Prefix)
2867 $NewFileNames[] = $Prefix.$FileName;
2870 $FileNames = $NewFileNames;
2873 # for each possible location
2874 $FoundFileName = NULL;
2875 foreach ($DirectoryList as $Dir)
2877 # substitute active UI name into path
2878 $Dir = str_replace(
"%ACTIVEUI%", $this->ActiveUI, $Dir);
2880 # for each possible file name
2881 foreach ($FileNames as $File)
2883 # if template is found at location
2884 if (file_exists($Dir.$File))
2886 # save full template file name and stop looking
2887 $FoundFileName = $Dir.$File;
2893 # save location in cache
2894 $this->Settings[
"TemplateLocationCache"][$CacheIndex]
2897 # set flag indicating that cache should be saved
2898 $this->SaveTemplateLocationCache = TRUE;
2901 # return full template file name to caller
2902 return $FoundFileName;
2911 private function GetRequiredFilesNotYetLoaded($PageContentFile)
2913 # start out assuming no files required
2914 $RequiredFiles = array();
2916 # if page content file supplied
2917 if ($PageContentFile)
2919 # if file containing list of required files is available
2920 $Path = dirname($PageContentFile);
2921 $RequireListFile = $Path.
"/REQUIRES";
2922 if (file_exists($RequireListFile))
2924 # read in list of required files
2925 $RequestedFiles = file($RequireListFile);
2927 # for each line in required file list
2928 foreach ($RequestedFiles as $Line)
2930 # if line is not a comment
2931 $Line = trim($Line);
2932 if (!preg_match(
"/^#/", $Line))
2934 # if file has not already been loaded
2935 if (!in_array($Line, $this->FoundUIFiles))
2937 # add to list of required files
2938 $RequiredFiles[] = $Line;
2945 # add in additional required files if any
2946 if (count($this->AdditionalRequiredUIFiles))
2948 # make sure there are no duplicates
2949 $AdditionalRequiredUIFiles = array_unique(
2950 $this->AdditionalRequiredUIFiles);
2952 $RequiredFiles = array_merge(
2953 $RequiredFiles, $AdditionalRequiredUIFiles);
2956 # return list of required files to caller
2957 return $RequiredFiles;
2963 private function SetUpObjectAutoloading()
2966 function __autoload($ClassName)
2979 # if caching is not turned off
2980 # and we have a cached location for class
2981 # and cache expiration has not elapsed
2982 # and file at cached location is readable
2983 if ((self::$ObjectLocationCacheInterval > 0)
2984 && count(self::$ObjectLocationCache)
2985 && array_key_exists($ClassName,
2986 self::$ObjectLocationCache)
2987 && (time() < strtotime(
2988 self::$ObjectLocationCacheExpiration))
2989 && is_readable(self::$ObjectLocationCache[$ClassName]))
2991 # use object location from cache
2992 require_once(self::$ObjectLocationCache[$ClassName]);
2996 # for each possible object file directory
2998 foreach (self::$ObjectDirectories as $Location => $Info)
3000 # if directory looks valid
3001 if (is_dir($Location))
3003 # build class file name
3004 $NewClassName = ($Info[
"ClassPattern"] && $Info[
"ClassReplacement"])
3005 ? preg_replace($Info[
"ClassPattern"],
3006 $Info[
"ClassReplacement"], $ClassName)
3009 # read in directory contents if not already retrieved
3010 if (!isset($FileLists[$Location]))
3012 $FileLists[$Location] = self::ReadDirectoryTree(
3013 $Location,
'/^.+\.php$/i');
3016 # for each file in target directory
3017 $FileNames = $FileLists[$Location];
3018 $TargetName = strtolower($Info[
"Prefix"].$NewClassName.
".php");
3019 foreach ($FileNames as $FileName)
3021 # if file matches our target object file name
3022 if (strtolower($FileName) == $TargetName)
3024 # include object file
3025 require_once($Location.$FileName);
3027 # save location to cache
3028 self::$ObjectLocationCache[$ClassName]
3029 = $Location.$FileName;
3031 # set flag indicating that cache should be saved
3032 self::$SaveObjectLocationCache = TRUE;
3051 private static function ReadDirectoryTree($Directory, $Pattern)
3053 $CurrentDir = getcwd();
3055 $DirIter =
new RecursiveDirectoryIterator(
".");
3056 $IterIter =
new RecursiveIteratorIterator($DirIter);
3057 $RegexResults =
new RegexIterator($IterIter, $Pattern,
3058 RecursiveRegexIterator::GET_MATCH);
3059 $FileList = array();
3060 foreach ($RegexResults as $Result)
3062 $FileList[] = substr($Result[0], 2);
3071 private function UndoMagicQuotes()
3073 # if this PHP version has magic quotes support
3074 if (version_compare(PHP_VERSION,
"5.4.0",
"<"))
3076 # turn off runtime magic quotes if on
3077 if (get_magic_quotes_runtime())
3080 set_magic_quotes_runtime(FALSE);
3084 # if magic quotes GPC is on
3085 if (get_magic_quotes_gpc())
3087 # strip added slashes from incoming variables
3088 $GPC = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
3089 array_walk_recursive($GPC,
3090 array($this,
"UndoMagicQuotes_StripCallback"));
3094 private function UndoMagicQuotes_StripCallback(&$Value)
3096 $Value = stripslashes($Value);
3103 private function LoadUIFunctions()
3106 "local/interface/%ACTIVEUI%/include",
3107 "interface/%ACTIVEUI%/include",
3108 "local/interface/default/include",
3109 "interface/default/include",
3111 foreach ($Dirs as $Dir)
3113 $Dir = str_replace(
"%ACTIVEUI%", $this->ActiveUI, $Dir);
3116 $FileNames = scandir($Dir);
3117 foreach ($FileNames as $FileName)
3119 if (preg_match(
"/^F-([A-Za-z0-9_]+)\.php/", $FileName, $Matches)
3120 || preg_match(
"/^F-([A-Za-z0-9_]+)\.html/", $FileName, $Matches))
3122 if (!function_exists($Matches[1]))
3124 include_once($Dir.
"/".$FileName);
3137 private function ProcessPeriodicEvent($EventName, $Callback)
3139 # retrieve last execution time for event if available
3140 $Signature = self::GetCallbackSignature($Callback);
3141 $LastRun = $this->DB->Query(
"SELECT LastRunAt FROM PeriodicEvents"
3142 .
" WHERE Signature = '".addslashes($Signature).
"'",
"LastRunAt");
3144 # determine whether enough time has passed for event to execute
3145 $ShouldExecute = (($LastRun === NULL)
3146 || (time() > (strtotime($LastRun) + $this->EventPeriods[$EventName])))
3149 # if event should run
3152 # add event to task queue
3153 $WrapperCallback = array(
"ApplicationFramework",
"PeriodicEventWrapper");
3154 $WrapperParameters = array(
3155 $EventName, $Callback, array(
"LastRunAt" => $LastRun));
3159 # add event to list of periodic events
3160 $this->KnownPeriodicEvents[$Signature] = array(
3161 "Period" => $EventName,
3162 "Callback" => $Callback,
3163 "Queued" => $ShouldExecute);
3173 private static function PeriodicEventWrapper($EventName, $Callback, $Parameters)
3176 if (!isset($DB)) { $DB =
new Database(); }
3179 $ReturnVal = call_user_func_array($Callback, $Parameters);
3181 # if event is already in database
3182 $Signature = self::GetCallbackSignature($Callback);
3183 if ($DB->Query(
"SELECT COUNT(*) AS EventCount FROM PeriodicEvents"
3184 .
" WHERE Signature = '".addslashes($Signature).
"'",
"EventCount"))
3186 # update last run time for event
3187 $DB->Query(
"UPDATE PeriodicEvents SET LastRunAt = "
3188 .(($EventName ==
"EVENT_PERIODIC")
3189 ?
"'".date(
"Y-m-d H:i:s", time() + ($ReturnVal * 60)).
"'"
3191 .
" WHERE Signature = '".addslashes($Signature).
"'");
3195 # add last run time for event to database
3196 $DB->Query(
"INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES "
3197 .
"('".addslashes($Signature).
"', "
3198 .(($EventName ==
"EVENT_PERIODIC")
3199 ?
"'".date(
"Y-m-d H:i:s", time() + ($ReturnVal * 60)).
"'"
3209 private static function GetCallbackSignature($Callback)
3211 return !is_array($Callback) ? $Callback
3212 : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0])
3220 private function PrepForTSR()
3222 # if HTML has been output and it's time to launch another task
3223 # (only TSR if HTML has been output because otherwise browsers
3224 # may misbehave after connection is closed)
3225 if (($this->JumpToPage || !$this->SuppressHTML)
3226 && (time() > (strtotime($this->Settings[
"LastTaskRunAt"])
3227 + (ini_get(
"max_execution_time")
3228 / $this->Settings[
"MaxTasksRunning"]) + 5))
3230 && $this->Settings[
"TaskExecutionEnabled"])
3232 # begin buffering output for TSR
3235 # let caller know it is time to launch another task
3240 # let caller know it is not time to launch another task
3249 private function LaunchTSR()
3251 # set headers to close out connection to browser
3254 ignore_user_abort(TRUE);
3255 header(
"Connection: close");
3256 header(
"Content-Length: ".ob_get_length());
3259 # output buffered content
3260 while (ob_get_level()) { ob_end_flush(); }
3263 # write out any outstanding data and end HTTP session
3264 session_write_close();
3266 # set flag indicating that we are now running in background
3267 $this->RunningInBackground = TRUE;
3269 # if there is still a task in the queue
3272 # turn on output buffering to (hopefully) record any crash output
3275 # lock tables and grab last task run time to double check
3276 $this->DB->Query(
"LOCK TABLES ApplicationFrameworkSettings WRITE");
3277 $this->LoadSettings();
3279 # if still time to launch another task
3280 if (time() > (strtotime($this->Settings[
"LastTaskRunAt"])
3281 + (ini_get(
"max_execution_time")
3282 / $this->Settings[
"MaxTasksRunning"]) + 5))
3284 # update the "last run" time and release tables
3285 $this->DB->Query(
"UPDATE ApplicationFrameworkSettings"
3286 .
" SET LastTaskRunAt = '".date(
"Y-m-d H:i:s").
"'");
3287 $this->DB->Query(
"UNLOCK TABLES");
3289 # run tasks while there is a task in the queue and enough time left
3293 $this->RunNextTask();
3301 $this->DB->Query(
"UNLOCK TABLES");
3313 private function GetTaskList($DBQuery, $Count, $Offset)
3315 $this->DB->Query($DBQuery.
" LIMIT ".intval($Offset).
",".intval($Count));
3317 while ($Row = $this->DB->FetchRow())
3319 $Tasks[$Row[
"TaskId"]] = $Row;
3320 if ($Row[
"Callback"] ==
3321 serialize(array(
"ApplicationFramework",
"PeriodicEventWrapper")))
3323 $WrappedCallback = unserialize($Row[
"Parameters"]);
3324 $Tasks[$Row[
"TaskId"]][
"Callback"] = $WrappedCallback[1];
3325 $Tasks[$Row[
"TaskId"]][
"Parameters"] = NULL;
3329 $Tasks[$Row[
"TaskId"]][
"Callback"] = unserialize($Row[
"Callback"]);
3330 $Tasks[$Row[
"TaskId"]][
"Parameters"] = unserialize($Row[
"Parameters"]);
3339 private function RunNextTask()
3341 # lock tables to prevent same task from being run by multiple sessions
3342 $this->DB->Query(
"LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
3344 # look for task at head of queue
3345 $this->DB->Query(
"SELECT * FROM TaskQueue ORDER BY Priority, TaskId LIMIT 1");
3346 $Task = $this->DB->FetchRow();
3348 # if there was a task available
3351 # move task from queue to running tasks list
3352 $this->DB->Query(
"INSERT INTO RunningTasks "
3353 .
"(TaskId,Callback,Parameters,Priority,Description) "
3354 .
"SELECT * FROM TaskQueue WHERE TaskId = "
3355 .intval($Task[
"TaskId"]));
3356 $this->DB->Query(
"DELETE FROM TaskQueue WHERE TaskId = "
3357 .intval($Task[
"TaskId"]));
3359 # release table locks to again allow other sessions to run tasks
3360 $this->DB->Query(
"UNLOCK TABLES");
3362 # unpack stored task info
3363 $Callback = unserialize($Task[
"Callback"]);
3364 $Parameters = unserialize($Task[
"Parameters"]);
3366 # attempt to load task callback if not already available
3370 $this->RunningTask = $Task;
3373 call_user_func_array($Callback, $Parameters);
3377 call_user_func($Callback);
3379 unset($this->RunningTask);
3381 # remove task from running tasks list
3382 $this->DB->Query(
"DELETE FROM RunningTasks"
3383 .
" WHERE TaskId = ".intval($Task[
"TaskId"]));
3385 # prune running tasks list if necessary
3386 $RunningTasksCount = $this->DB->Query(
3387 "SELECT COUNT(*) AS TaskCount FROM RunningTasks",
"TaskCount");
3388 if ($RunningTasksCount > $this->MaxRunningTasksToTrack)
3390 $this->DB->Query(
"DELETE FROM RunningTasks ORDER BY StartedAt"
3391 .
" LIMIT ".($RunningTasksCount - $this->MaxRunningTasksToTrack));
3396 # release table locks to again allow other sessions to run tasks
3397 $this->DB->Query(
"UNLOCK TABLES");
3408 # attempt to remove any memory limits
3409 ini_set(
"memory_limit", -1);
3411 # if there is a background task currently running
3412 if (isset($this->RunningTask))
3414 # add info about error that caused crash (if available)
3415 if (function_exists(
"error_get_last"))
3417 $CrashInfo[
"LastError"] = error_get_last();
3420 # add info about current output buffer contents (if available)
3421 if (ob_get_length() !== FALSE)
3423 $CrashInfo[
"OutputBuffer"] = ob_get_contents();
3426 # if backtrace info is available for the crash
3427 $Backtrace = debug_backtrace();
3428 if (count($Backtrace) > 1)
3430 # discard the current context from the backtrace
3431 array_shift($Backtrace);
3433 # add the backtrace to the crash info
3434 $CrashInfo[
"Backtrace"] = $Backtrace;
3436 # else if saved backtrace info is available
3437 elseif (isset($this->SavedContext))
3439 # add the saved backtrace to the crash info
3440 $CrashInfo[
"Backtrace"] = $this->SavedContext;
3443 # if we have crash info to recod
3444 if (isset($CrashInfo))
3446 # save crash info for currently running task
3448 $DB->Query(
"UPDATE RunningTasks SET CrashInfo = '"
3449 .addslashes(serialize($CrashInfo))
3450 .
"' WHERE TaskId = ".intval($this->RunningTask[
"TaskId"]));
3474 private function AddToDirList($DirList, $Dir, $SearchLast, $SkipSlashCheck)
3476 # convert incoming directory to array of directories (if needed)
3477 $Dirs = is_array($Dir) ? $Dir : array($Dir);
3479 # reverse array so directories are searched in specified order
3480 $Dirs = array_reverse($Dirs);
3482 # for each directory
3483 foreach ($Dirs as $Location)
3485 # make sure directory includes trailing slash
3486 if (!$SkipSlashCheck)
3488 $Location = $Location
3489 .((substr($Location, -1) !=
"/") ?
"/" :
"");
3492 # remove directory from list if already present
3493 if (in_array($Location, $DirList))
3495 $DirList = array_diff(
3496 $DirList, array($Location));
3499 # add directory to list of directories
3502 array_push($DirList, $Location);
3506 array_unshift($DirList, $Location);
3510 # return updated directory list to caller
3521 private function ArrayPermutations(
$Items, $Perms = array())
3525 $Result = array($Perms);
3530 for ($Index = count(
$Items) - 1; $Index >= 0; --$Index)
3534 list($Segment) = array_splice($NewItems, $Index, 1);
3535 array_unshift($NewPerms, $Segment);
3536 $Result = array_merge($Result,
3537 $this->ArrayPermutations($NewItems, $NewPerms));
3549 private function OutputModificationCallbackShell($Matches)
3551 # call previously-stored external function
3552 return call_user_func($this->OutputModificationCallbackInfo[
"Callback"],
3554 $this->OutputModificationCallbackInfo[
"Pattern"],
3555 $this->OutputModificationCallbackInfo[
"Page"],
3556 $this->OutputModificationCallbackInfo[
"SearchPattern"]);
3567 return $this->DB->UpdateValue(
"ApplicationFrameworkSettings",
3568 $FieldName, $NewValue, NULL, $this->Settings);
3572 private $InterfaceDirList = array(
3573 "local/interface/%ACTIVEUI%/",
3574 "interface/%ACTIVEUI%/",
3575 "local/interface/default/",
3576 "interface/default/",
3582 private $IncludeDirList = array(
3583 "local/interface/%ACTIVEUI%/include/",
3584 "interface/%ACTIVEUI%/include/",
3585 "local/interface/default/include/",
3586 "interface/default/include/",
3589 private $ImageDirList = array(
3590 "local/interface/%ACTIVEUI%/images/",
3591 "interface/%ACTIVEUI%/images/",
3592 "local/interface/default/images/",
3593 "interface/default/images/",
3596 private $FunctionDirList = array(
3597 "local/interface/%ACTIVEUI%/include/",
3598 "interface/%ACTIVEUI%/include/",
3599 "local/interface/default/include/",
3600 "interface/default/include/",
3605 const NOVALUE =
".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-.";
MaxTasks($NewValue=DB_NOVALUE)
Get/set maximum number of tasks to have running simultaneously.
const LOGLVL_ERROR
ERROR error logging level.
GetOrphanedTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
SuppressHTMLOutput($NewSetting=TRUE)
Suppress loading of HTML files.
AddInterfaceDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for user interface (HTML/TPL) files.
AddIncludeDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for user interface include (CSS, JavaScript, common PHP, common HTML, etc) files.
static GetScriptUrl()
Retrieve SCRIPT_URL server value, pulling it from elsewhere if that variable isn't set...
const LOGLVL_INFO
INFO error logging level.
AddUnbufferedCallback($Callback, $Parameters=array())
Add a callback that will be executed after buffered content has been output and that won't have its o...
QueueUniqueTask($Callback, $Parameters=NULL, $Priority=self::PRIORITY_LOW, $Description="")
Add task to queue if not already in queue or currently running.
const LOGLVL_FATAL
FATAL error logging level.
UseMinimizedJavascript($NewSetting=NULL)
Get/set whether or not to check for and use minimized JavaScript files when getting a JavaScript UI f...
AddPostProcessingCall($FunctionName, &$Arg1=self::NOVALUE, &$Arg2=self::NOVALUE, &$Arg3=self::NOVALUE, &$Arg4=self::NOVALUE, &$Arg5=self::NOVALUE, &$Arg6=self::NOVALUE, &$Arg7=self::NOVALUE, &$Arg8=self::NOVALUE, &$Arg9=self::NOVALUE)
Add function to be called after HTML has been loaded.
GetCleanUrlForPath($Path)
Get the clean URL mapped for a path.
const PRIORITY_LOW
Lower priority.
Abstraction for forum messages and resource comments.
GetQueuedTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
LogFile($NewValue=NULL)
Get/set log file name.
GetCleanUrl()
Get the clean URL for the current page if one is available.
MaxExecutionTime($NewValue=NULL)
Get/set maximum PHP execution time.
static PreferHttpHost($NewValue=NULL)
Get/set whether to prefer $_SERVER["HTTP_HOST"] (if available) over $_SERVER["SERVER_NAME"] when dete...
RequireUIFile($FileName)
Add file to list of required UI files.
static FullUrl()
Get current full URL, before any clean URL remapping and with any query string (e.g.
Top-level framework for web applications.
static BaseUrl()
Get current base URL (the part before index.php) (e.g.
GetOrphanedTaskCount()
Retrieve current number of orphaned tasks.
SQL database abstraction object with smart query caching.
LogSlowPageLoads($NewValue=DB_NOVALUE)
Get/set whether logging of long page load times is enabled.
SlowPageLoadThreshold($NewValue=DB_NOVALUE)
Get/set how long a page load can take before it should be considered "slow" and may be logged...
GetTaskQueueSize($Priority=NULL)
Retrieve current number of tasks in queue.
static RootUrlOverride($NewValue=self::NOVALUE)
Get/set root URL override.
GetQueuedTaskCount($Callback=NULL, $Parameters=NULL, $Priority=NULL, $Description=NULL)
Get number of queued tasks that match supplied values.
static AutoloadObjects($ClassName)
DeleteTask($TaskId)
Remove task from task queues.
const LOGLVL_DEBUG
DEBUG error logging leve.
const EVENTTYPE_NAMED
Named result event type.
const EVENTTYPE_FIRST
First response event type.
static WasUrlRewritten($ScriptName="index.php")
Determine if the URL was rewritten, i.e., the script is being accessed through a URL that isn't direc...
const EVENTTYPE_DEFAULT
Default event type.
GetPageUrl()
Get the full URL to the page.
IsStaticOnlyEvent($EventName)
Report whether specified event only allows static callbacks.
IsRegisteredEvent($EventName)
Check if event has been registered (is available to be signaled).
static AddObjectDirectory($Dir, $Prefix="", $ClassPattern=NULL, $ClassReplacement=NULL)
Add directory to be searched for object files when autoloading.
SignalEvent($EventName, $Parameters=NULL)
Signal that an event has occured.
static BasePath()
Get current base path (usually the part after the host name).
const LOGLVL_TRACE
TRACE error logging level.
const LOGLVL_WARNING
WARNING error logging level.
const PRIORITY_MEDIUM
Medium (default) priority.
LogError($Level, $Msg)
Write error message to log.
OnCrash()
Called automatically at program termination to ensure output is written out.
GetKnownPeriodicEvents()
Get list of known periodic events.
const EVENTTYPE_CHAIN
Result chaining event type.
GetSecondsBeforeTimeout()
Get remaining available (PHP) execution time.
TaskIsInQueue($Callback, $Parameters=NULL)
Check if task is already in queue or currently running.
AddFunctionDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for function ("F-") files.
LoggingLevel($NewValue=DB_NOVALUE)
Get/set logging level.
const ORDER_MIDDLE
Run hooked function after ORDER_FIRST and before ORDER_LAST events.
RegisterEvent($EventsOrEventName, $EventType=NULL)
Register one or more events that may be signaled.
SetJumpToPage($Page, $IsLiteral=FALSE)
Set URL of page to autoload after PHP page file is executed.
GetPageName()
Get name of page being loaded.
CleanUrlIsMapped($Path)
Report whether clean URL has already been mapped.
const PRIORITY_HIGH
Highest priority.
LoadFunction($Callback)
Attempt to load code for function or method if not currently available.
GetRunningTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
FindCommonTemplate($BaseName)
Preserved for backward compatibility for use with code written prior to October 2012.
EventWillNextRunAt($EventName, $Callback)
Get date/time a periodic event will next run.
static SessionLifetime($NewValue=NULL)
Get/set session timeout in seconds.
HookEvent($EventsOrEventName, $Callback=NULL, $Order=self::ORDER_MIDDLE)
Hook one or more functions to be called when the specified event is signaled.
IsHookedEvent($EventName)
Check if an event is registered and is hooked to.
static GetFreeMemory()
Get current amount of free memory.
AddImageDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for image files.
HtmlCharset($NewSetting=NULL)
Get/set HTTP character encoding value.
const ORDER_FIRST
Run hooked function first (i.e.
GetUncleanUrlForPath($Path)
Get the unclean URL for mapped for a path.
TemplateLocationCacheExpirationInterval($NewInterval=DB_NOVALUE)
Get/set UI template location cache expiration period in minutes.
GetUncleanUrl()
Get the unclean URL for the current page.
RecordContextInCaseOfCrash($BacktraceOptions=0, $BacktraceLimit=0)
Record the current execution context in case of crash.
JumpToPageIsSet()
Report whether a page to autoload has been set.
const ORDER_LAST
Run hooked function last (i.e.
UseBaseTag($NewValue=NULL)
Get/set whether or not to use the "base" tag to ensure relative URL paths are correct.
static HtaccessSupport()
Determine if .htaccess files are enabled.
UpdateSetting($FieldName, $NewValue=DB_NOVALUE)
Convenience function for getting/setting our settings.
GetTask($TaskId)
Retrieve task info from queue (either running or queued tasks).
TaskExecutionEnabled($NewValue=DB_NOVALUE)
Get/set whether automatic task execution is enabled.
static GetPhpMemoryLimit()
Get PHP memory limit in bytes.
LoadPage($PageName)
Load page PHP and HTML/TPL files.
AddEnvInclude($FileName)
Add file to be included to set up environment.
ReQueueOrphanedTask($TaskId, $NewPriority=NULL)
Move orphaned task back into queue.
GUIFile($FileName)
Search UI directories for specified image or CSS file and return name of correct file.
QueueTask($Callback, $Parameters=NULL, $Priority=self::PRIORITY_LOW, $Description="")
Add task to queue.
GetUserInterfaces()
Get the list of available user interfaces.
PUIFile($FileName)
Search UI directories for specified image or CSS file and print name of correct file.
LogHighMemoryUsage($NewValue=DB_NOVALUE)
Get/set whether logging of high memory usage is enabled.
ActiveUserInterface($UIName=NULL)
Get/set name of current active user interface.
GetPageLocation()
Get the URL path to the page without the base path, if present.
ObjectLocationCacheExpirationInterval($NewInterval=DB_NOVALUE)
Get/set object file location cache expiration period in minutes.
GetElapsedExecutionTime()
Get time elapsed since constructor was called.
SetBrowserDetectionFunc($DetectionFunc)
Specify function to use to detect the web browser type.
static RootUrl()
Get portion of current URL through host name, with no trailing slash (e.g.
AddCleanUrl($Pattern, $Page, $GetVars=NULL, $Template=NULL)
Add clean URL mapping.
HighMemoryUsageThreshold($NewValue=DB_NOVALUE)
Get/set what percentage of max memory (set via the memory_limit PHP configuration directive) a page l...
const PRIORITY_BACKGROUND
Lowest priority.
LogMessage($Level, $Msg)
Write status message to log.