3 # FILE: ApplicationFramework.php 5 # Part of the ScoutLib application support library 6 # Copyright 2009-2016 Edward Almasy and Internet Scout Research Group 7 # http://scout.wisc.edu 17 # ---- PUBLIC INTERFACE -------------------------------------------------- 25 public function __construct()
27 # save execution start time 28 $this->ExecutionStartTime = microtime(TRUE);
30 # set up default object file search locations 31 self::AddObjectDirectory(
"local/interface/%ACTIVEUI%/objects");
32 self::AddObjectDirectory(
"interface/%ACTIVEUI%/objects");
33 self::AddObjectDirectory(
"local/interface/%DEFAULTUI%/objects");
34 self::AddObjectDirectory(
"interface/%DEFAULTUI%/objects");
35 self::AddObjectDirectory(
"local/objects");
36 self::AddObjectDirectory(
"objects");
38 # set up object file autoloader 39 spl_autoload_register(array($this,
"AutoloadObjects"));
41 # set up function to output any buffered text in case of crash 42 register_shutdown_function(array($this,
"OnCrash"));
44 # if we were not invoked via command line interface 45 if (php_sapi_name() !==
"cli")
47 # build cookie domain string 48 $SessionDomain = isset($_SERVER[
"SERVER_NAME"]) ? $_SERVER[
"SERVER_NAME"]
49 : isset($_SERVER[
"HTTP_HOST"]) ? $_SERVER[
"HTTP_HOST"]
52 # if it appears our session storage area is writable 53 if (is_writable(session_save_path()))
55 # store our session files in a subdirectory to avoid 56 # accidentally sharing sessions with other installations 58 $SessionStorage = session_save_path()
59 .
"/".self::$AppName.
"_".md5($SessionDomain.dirname(__FILE__));
61 # create session storage subdirectory if not found 62 if (!is_dir($SessionStorage)) { mkdir($SessionStorage, 0700 ); }
64 # if session storage subdirectory is writable 65 if (is_writable($SessionStorage))
67 # save parameters of our session storage as instance variables 69 $this->SessionGcProbability =
70 ini_get(
"session.gc_probability") / ini_get(
"session.gc_divisor");
71 # require a gc probability of at least MIN_GC_PROBABILITY 72 if ($this->SessionGcProbability < self::MIN_GC_PROBABILITY)
74 $this->SessionGcProbability = self::MIN_GC_PROBABILITY;
77 $this->SessionStorage = $SessionStorage;
79 # set the new session storage location 80 session_save_path($SessionStorage);
82 # disable PHP's garbage collection, as it does not handle 83 # subdirectories (instead, we'll do the cleanup as we run 85 ini_set(
"session.gc_probability", 0);
89 # set garbage collection max period to our session lifetime 90 ini_set(
"session.gc_maxlifetime", self::$SessionLifetime);
91 session_set_cookie_params(
92 self::$SessionLifetime,
"/", $SessionDomain);
94 # attempt to start session 95 $SessionStarted = @session_start();
97 # if session start failed 100 # regenerate session ID and attempt to start session again 101 session_regenerate_id(TRUE);
106 # set up our internal environment 109 # set up our exception handler 110 set_exception_handler(array($this,
"GlobalExceptionHandler"));
112 # perform any work needed to undo PHP magic quotes 113 $this->UndoMagicQuotes();
115 # load our settings from database 116 $this->LoadSettings();
118 # set PHP maximum execution time 119 ini_set(
"max_execution_time", $this->Settings[
"MaxExecTime"]);
120 set_time_limit($this->Settings[
"MaxExecTime"]);
122 # register events we handle internally 126 # attempt to create SCSS cache directory if needed and it does not exist 128 { @mkdir(self::$ScssCacheDir, 0777, TRUE); }
130 # attempt to create minimized JS cache directory if needed and it does not exist 133 && !is_dir(self::$JSMinCacheDir))
135 @mkdir(self::$JSMinCacheDir, 0777, TRUE);
144 public function __destruct()
146 # if template location cache is flagged to be saved 147 if ($this->SaveTemplateLocationCache)
149 # write template location cache out and update cache expiration 150 $this->DB->Query(
"UPDATE ApplicationFrameworkSettings" 151 .
" SET TemplateLocationCache = '" 152 .addslashes(serialize(
153 $this->TemplateLocationCache)).
"'," 154 .
" TemplateLocationCacheExpiration = '" 156 $this->TemplateLocationCacheExpiration).
"'");
159 # if object location cache is flagged to be saved 160 if (self::$SaveObjectLocationCache)
162 # write object location cache out and update cache expiration 163 $this->DB->Query(
"UPDATE ApplicationFrameworkSettings" 164 .
" SET ObjectLocationCache = '" 165 .addslashes(serialize(
166 self::$ObjectLocationCache)).
"'," 167 .
" ObjectLocationCacheExpiration = '" 169 self::$ObjectLocationCacheExpiration).
"'");
179 public function GlobalExceptionHandler($Exception)
181 # display exception info 182 $Location = str_replace(getcwd().
"/",
"",
183 $Exception->getFile().
"[".$Exception->getLine().
"]");
184 ?><table width=
"100%" cellpadding=
"5" 185 style=
"border: 2px solid #666666; background: #CCCCCC; 186 font-family: Courier New, Courier, monospace; 187 margin-top: 10px;"><tr><td>
188 <div style=
"color: #666666;">
189 <span style=
"font-size: 150%;">
190 <b>Uncaught Exception</b></span><br />
191 <b>
Message:</b> <i><?
PHP print $Exception->getMessage(); ?></i><br />
192 <b>Location:</b> <i><?
PHP print $Location; ?></i><br />
194 <blockquote><pre><?
PHP print preg_replace(
195 ":(#[0-9]+) ".getcwd().
"/".
":",
"$1 ",
196 $Exception->getTraceAsString());
197 ?></pre></blockquote>
199 </td></tr></table><?
PHP 201 # log exception if possible 202 $TraceString = $Exception->getTraceAsString();
203 $TraceString = str_replace(
"\n",
", ", $TraceString);
204 $TraceString = preg_replace(
":(#[0-9]+) ".getcwd().
"/".
":",
"$1 ", $TraceString);
205 $LogMsg =
"Uncaught exception (".$Exception->getMessage().
") at " 206 .$Location.
". TRACE: ".$TraceString.
" URL: ".$this->
FullUrl();
207 $this->
LogError(self::LOGLVL_ERROR, $LogMsg);
226 $Dir, $Prefix =
"", $ClassPattern = NULL, $ClassReplacement = NULL)
228 # make sure directory has trailing slash 229 $Dir = $Dir.((substr($Dir, -1) !=
"/") ?
"/" :
"");
231 # add directory to directory list 232 self::$ObjectDirectories[$Dir] = array(
234 "ClassPattern" => $ClassPattern,
235 "ClassReplacement" => $ClassReplacement,
259 $Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
261 # add directories to existing image directory list 262 $this->ImageDirList = $this->AddToDirList(
263 $this->ImageDirList, $Dir, $SearchLast, $SkipSlashCheck);
287 $Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
289 # add directories to existing image directory list 290 $this->IncludeDirList = $this->AddToDirList(
291 $this->IncludeDirList, $Dir, $SearchLast, $SkipSlashCheck);
314 $Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
316 # add directories to existing image directory list 317 $this->InterfaceDirList = $this->AddToDirList(
318 $this->InterfaceDirList, $Dir, $SearchLast, $SkipSlashCheck);
341 $Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
343 # add directories to existing image directory list 344 $this->FunctionDirList = $this->AddToDirList(
345 $this->FunctionDirList, $Dir, $SearchLast, $SkipSlashCheck);
355 $this->BrowserDetectFunc = $DetectionFunc;
366 if (is_callable($Callback))
368 $this->UnbufferedCallbacks[] = array($Callback, $Parameters);
380 return $this->UpdateSetting(
"TemplateLocationCacheInterval", $NewInterval);
388 $this->TemplateLocationCache = array();
389 $this->SaveTemplateLocationCache = TRUE;
400 return $this->UpdateSetting(
"ObjectLocationCacheInterval", $NewInterval);
408 self::$ObjectLocationCache = array();
409 self::$SaveObjectLocationCache = TRUE;
420 return $this->UpdateSetting(
"UrlFingerprintingEnabled", $NewValue);
432 return $this->UpdateSetting(
"ScssSupportEnabled", $NewValue);
445 return $this->UpdateSetting(
"GenerateCompactCss", $NewValue);
458 return $this->UpdateSetting(
"UseMinimizedJavascript", $NewValue);
471 return $this->UpdateSetting(
"JavascriptMinimizationEnabled", $NewValue);
488 $BacktraceOptions = 0, $BacktraceLimit = 0)
490 if (version_compare(PHP_VERSION,
"5.4.0",
">="))
492 $this->SavedContext = debug_backtrace(
493 $BacktraceOptions, $BacktraceLimit);
497 $this->SavedContext = debug_backtrace($BacktraceOptions);
499 array_shift($this->SavedContext);
508 # perform any clean URL rewriting 509 $PageName = $this->RewriteCleanUrls($PageName);
511 # sanitize incoming page name and save local copy 512 $PageName = preg_replace(
"/[^a-zA-Z0-9_.-]/",
"", $PageName);
513 $this->PageName = $PageName;
515 # if page caching is turned on 518 # if we have a cached page 519 $CachedPage = $this->CheckForCachedPage($PageName);
520 if ($CachedPage !== NULL)
522 # set header to indicate cache hit was found 523 header(
"X-ScoutAF-Cache: HIT");
525 # display cached page and exit 531 # set header to indicate no cache hit was found 532 header(
"X-ScoutAF-Cache: MISS");
536 # buffer any output from includes or PHP file 539 # include any files needed to set up execution environment 540 foreach ($this->EnvIncludes as $IncludeFile)
542 include($IncludeFile);
546 $this->
SignalEvent(
"EVENT_PAGE_LOAD", array(
"PageName" => $PageName));
548 # signal PHP file load 549 $SignalResult = $this->
SignalEvent(
"EVENT_PHP_FILE_LOAD", array(
550 "PageName" => $PageName));
552 # if signal handler returned new page name value 553 $NewPageName = $PageName;
554 if (($SignalResult[
"PageName"] != $PageName)
555 && strlen($SignalResult[
"PageName"]))
557 # if new page name value is page file 558 if (file_exists($SignalResult[
"PageName"]))
560 # use new value for PHP file name 561 $PageFile = $SignalResult[
"PageName"];
565 # use new value for page name 566 $NewPageName = $SignalResult[
"PageName"];
569 # update local copy of page name 570 $this->PageName = $NewPageName;
573 # if we do not already have a PHP file 574 if (!isset($PageFile))
576 # look for PHP file for page 577 $OurPageFile =
"pages/".$NewPageName.
".php";
578 $LocalPageFile =
"local/pages/".$NewPageName.
".php";
579 $PageFile = file_exists($LocalPageFile) ? $LocalPageFile
580 : (file_exists($OurPageFile) ? $OurPageFile
581 :
"pages/".$this->DefaultPage.
".php");
587 # save buffered output to be displayed later after HTML file loads 588 $PageOutput = ob_get_contents();
591 # signal PHP file load is complete 593 $Context[
"Variables"] = get_defined_vars();
595 array(
"PageName" => $PageName,
"Context" => $Context));
596 $PageCompleteOutput = ob_get_contents();
599 # set up for possible TSR (Terminate and Stay Resident :)) 600 $ShouldTSR = $this->PrepForTSR();
602 # if PHP file indicated we should autorefresh to somewhere else 603 if (($this->JumpToPage) && ($this->JumpToPageDelay == 0))
605 if (!strlen(trim($PageOutput)))
607 # if client supports HTTP/1.1, use a 303 as it is most accurate 608 if ($_SERVER[
"SERVER_PROTOCOL"] ==
"HTTP/1.1")
610 header(
"HTTP/1.1 303 See Other");
611 header(
"Location: ".$this->JumpToPage);
615 # if the request was an HTTP/1.0 GET or HEAD, then 616 # use a 302 response code. 618 # NB: both RFC 2616 (HTTP/1.1) and RFC1945 (HTTP/1.0) 619 # explicitly prohibit automatic redirection via a 302 620 # if the request was not GET or HEAD. 621 if ($_SERVER[
"SERVER_PROTOCOL"] ==
"HTTP/1.0" &&
622 ($_SERVER[
"REQUEST_METHOD"] ==
"GET" ||
623 $_SERVER[
"REQUEST_METHOD"] ==
"HEAD") )
625 header(
"HTTP/1.0 302 Found");
626 header(
"Location: ".$this->JumpToPage);
629 # otherwise, fall back to a meta refresh 632 print
'<html><head><meta http-equiv="refresh" ' 633 .
'content="0; URL='.$this->JumpToPage.
'">' 634 .
'</head><body></body></html>';
639 # else if HTML loading is not suppressed 640 elseif (!$this->SuppressHTML)
642 # set content-type to get rid of diacritic errors 643 header(
"Content-Type: text/html; charset=" 646 # load common HTML file (defines common functions) if available 647 $CommonHtmlFile = $this->FindFile($this->IncludeDirList,
648 "Common", array(
"tpl",
"html"));
649 if ($CommonHtmlFile) { include($CommonHtmlFile); }
652 $this->LoadUIFunctions();
654 # begin buffering content 657 # signal HTML file load 658 $SignalResult = $this->
SignalEvent(
"EVENT_HTML_FILE_LOAD", array(
659 "PageName" => $PageName));
661 # if signal handler returned new page name value 662 $NewPageName = $PageName;
663 $PageContentFile = NULL;
664 if (($SignalResult[
"PageName"] != $PageName)
665 && strlen($SignalResult[
"PageName"]))
667 # if new page name value is HTML file 668 if (file_exists($SignalResult[
"PageName"]))
670 # use new value for HTML file name 671 $PageContentFile = $SignalResult[
"PageName"];
675 # use new value for page name 676 $NewPageName = $SignalResult[
"PageName"];
680 # load page content HTML file if available 681 if ($PageContentFile === NULL)
683 $PageContentFile = $this->FindFile(
684 $this->InterfaceDirList, $NewPageName,
685 array(
"tpl",
"html"));
687 if ($PageContentFile)
689 include($PageContentFile);
693 print
"<h2>ERROR: No HTML/TPL template found" 694 .
" for this page (".$NewPageName.
").</h2>";
697 # signal HTML file load complete 698 $SignalResult = $this->
SignalEvent(
"EVENT_HTML_FILE_LOAD_COMPLETE");
700 # stop buffering and save output 701 $PageContentOutput = ob_get_contents();
704 # load page start HTML file if available 706 $PageStartFile = $this->FindFile($this->IncludeDirList,
"Start",
707 array(
"tpl",
"html"), array(
"StdPage",
"StandardPage"));
708 if ($PageStartFile) { include($PageStartFile); }
709 $PageStartOutput = ob_get_contents();
712 # if page auto-refresh requested 713 if ($this->JumpToPage)
715 $RefreshLine =
'<meta http-equiv="refresh" content="' 716 .$this->JumpToPageDelay.
'; url='.$this->JumpToPage.
'">';
717 $PageStartOutput = str_replace(
"<head>",
"<head>\n".$RefreshLine,
721 # load page end HTML file if available 723 $PageEndFile = $this->FindFile($this->IncludeDirList,
"End",
724 array(
"tpl",
"html"), array(
"StdPage",
"StandardPage"));
725 if ($PageEndFile) { include($PageEndFile); }
726 $PageEndOutput = ob_get_contents();
729 # get list of any required files not loaded 730 $RequiredFiles = $this->GetRequiredFilesNotYetLoaded($PageContentFile);
732 # if a browser detection function has been made available 733 if (is_callable($this->BrowserDetectFunc))
735 # call function to get browser list 736 $Browsers = call_user_func($this->BrowserDetectFunc);
738 # for each required file 739 $NewRequiredFiles = array();
740 foreach ($RequiredFiles as $File)
742 # if file name includes browser keyword 743 if (preg_match(
"/%BROWSER%/", $File))
746 foreach ($Browsers as $Browser)
748 # substitute in browser name and add to new file list 749 $NewRequiredFiles[] = preg_replace(
750 "/%BROWSER%/", $Browser, $File);
755 # add to new file list 756 $NewRequiredFiles[] = $File;
759 $RequiredFiles = $NewRequiredFiles;
763 # filter out any files with browser keyword in their name 764 $NewRequiredFiles = array();
765 foreach ($RequiredFiles as $File)
767 if (!preg_match(
"/%BROWSER%/", $File))
769 $NewRequiredFiles[] = $File;
772 $RequiredFiles = $NewRequiredFiles;
775 # for each required file 776 foreach ($RequiredFiles as $File)
778 # locate specific file to use 779 $FilePath = $this->
GUIFile($File);
784 # generate tag for file 785 $Tag = $this->GetUIFileLoadingTag($FilePath);
787 # add file to HTML output based on file type 792 $PageStartOutput = preg_replace(
793 "#</head>#i", $Tag.
"\n</head>", $PageStartOutput, 1);
796 case self::FT_JAVASCRIPT:
797 $PageEndOutput = preg_replace(
798 "#</body>#i", $Tag.
"\n</body>", $PageEndOutput, 1);
805 $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
807 # perform any regular expression replacements in output 808 $NewFullPageOutput = preg_replace($this->OutputModificationPatterns,
809 $this->OutputModificationReplacements, $FullPageOutput);
811 # check to make sure replacements didn't fail 812 $FullPageOutput = $this->CheckOutputModification(
813 $FullPageOutput, $NewFullPageOutput,
814 "regular expression replacements");
816 # for each registered output modification callback 817 foreach ($this->OutputModificationCallbacks as $Info)
819 # set up data for callback 820 $this->OutputModificationCallbackInfo = $Info;
822 # perform output modification 823 $NewFullPageOutput = preg_replace_callback($Info[
"SearchPattern"],
824 array($this,
"OutputModificationCallbackShell"),
827 # check to make sure modification didn't fail 828 $ErrorInfo =
"callback info: ".print_r($Info, TRUE);
829 $FullPageOutput = $this->CheckOutputModification(
830 $FullPageOutput, $NewFullPageOutput, $ErrorInfo);
833 # provide the opportunity to modify full page output 834 $SignalResult = $this->
SignalEvent(
"EVENT_PAGE_OUTPUT_FILTER", array(
835 "PageOutput" => $FullPageOutput));
836 if (isset($SignalResult[
"PageOutput"])
837 && strlen(trim($SignalResult[
"PageOutput"])))
839 $FullPageOutput = $SignalResult[
"PageOutput"];
842 # if relative paths may not work because we were invoked via clean URL 843 if ($this->CleanUrlRewritePerformed || self::WasUrlRewritten())
845 # if using the <base> tag is okay 849 # add <base> tag to header 850 $PageStartOutput = str_replace(
"<head>",
851 "<head><base href=\"".$BaseUrl.
"\" />",
854 # re-assemble full page with new header 855 $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
857 # the absolute URL to the current page 860 # make HREF attribute values with just a fragment ID 861 # absolute since they don't work with the <base> tag because 862 # they are relative to the current page/URL, not the site 864 $NewFullPageOutput = preg_replace(
865 array(
"%href=\"(#[^:\" ]+)\"%i",
"%href='(#[^:' ]+)'%i"),
866 array(
"href=\"".$FullUrl.
"$1\"",
"href='".$FullUrl.
"$1'"),
869 # check to make sure HREF cleanup didn't fail 870 $FullPageOutput = $this->CheckOutputModification(
871 $FullPageOutput, $NewFullPageOutput,
876 # try to fix any relative paths throughout code 877 $RelativePathPatterns = array(
878 "%src=\"/?([^?*:;{}\\\\\" ]+)\.(js|css|gif|png|jpg)\"%i",
879 "%src='/?([^?*:;{}\\\\' ]+)\.(js|css|gif|png|jpg)'%i",
880 # don
't rewrite HREF attributes that are just 881 # fragment IDs because they are relative to the 882 # current page/URL, not the site root 883 "%href=\"/?([^#][^:\" ]*)\"%i", 884 "%href='/?([^#][^:
' ]*)'%i
", 885 "%action=\
"/?([^#][^:\" ]*)\"%i",
886 "%action='/?([^#][^:' ]*)'%i",
887 "%@import\s+url\(\"/?([^:\" ]+)\"\s*\)%i",
888 "%@import\s+url\('/?([^:\" ]+)'\s*\)%i",
889 "%src:\s+url\(\"/?([^:\" ]+)\"\s*\)%i",
890 "%src:\s+url\('/?([^:\" ]+)'\s*\)%i",
891 "%@import\s+\"/?([^:\" ]+)\"\s*%i",
892 "%@import\s+'/?([^:\" ]+)'\s*%i",
894 $RelativePathReplacements = array(
895 "src=\"".$BaseUrl.
"$1.$2\"",
896 "src=\"".$BaseUrl.
"$1.$2\"",
897 "href=\"".$BaseUrl.
"$1\"",
898 "href=\"".$BaseUrl.
"$1\"",
899 "action=\"".$BaseUrl.
"$1\"",
900 "action=\"".$BaseUrl.
"$1\"",
901 "@import url(\"".$BaseUrl.
"$1\")",
902 "@import url('".$BaseUrl.
"$1')",
903 "src: url(\"".$BaseUrl.
"$1\")",
904 "src: url('".$BaseUrl.
"$1')",
905 "@import \"".$BaseUrl.
"$1\"",
906 "@import '".$BaseUrl.
"$1'",
908 $NewFullPageOutput = preg_replace($RelativePathPatterns,
909 $RelativePathReplacements, $FullPageOutput);
911 # check to make sure relative path fixes didn't fail 912 $FullPageOutput = $this->CheckOutputModification(
913 $FullPageOutput, $NewFullPageOutput,
914 "relative path fixes");
918 # handle any necessary alternate domain rewriting 919 $FullPageOutput = $this->RewriteAlternateDomainUrls($FullPageOutput);
921 # update page cache for this page 922 $this->UpdatePageCache($PageName, $FullPageOutput);
924 # write out full page 925 print $FullPageOutput;
928 # run any post-processing routines 929 foreach ($this->PostProcessingFuncs as $Func)
931 call_user_func_array($Func[
"FunctionName"], $Func[
"Arguments"]);
934 # write out any output buffered from page code execution 935 if (strlen($PageOutput))
937 if (!$this->SuppressHTML)
939 ?><table width=
"100%" cellpadding=
"5" 940 style=
"border: 2px solid #666666; background: #CCCCCC; 941 font-family: Courier New, Courier, monospace; 942 margin-top: 10px;"><tr><td><?
PHP 944 if ($this->JumpToPage)
946 ?><div style=
"color: #666666;"><span style=
"font-size: 150%;">
947 <b>Page Jump Aborted</b></span>
948 (because of error or other unexpected output)<br />
950 <i><?
PHP print($this->JumpToPage); ?></i></div><?
PHP 953 if (!$this->SuppressHTML)
955 ?></td></tr></table><?
PHP 959 # write out any output buffered from the page code execution complete signal 960 if (!$this->JumpToPage && !$this->SuppressHTML && strlen($PageCompleteOutput))
962 print $PageCompleteOutput;
965 # log slow page loads 967 && !$this->DoNotLogSlowPageLoad
971 $RemoteHost = gethostbyaddr($_SERVER[
"REMOTE_ADDR"]);
972 if ($RemoteHost === FALSE)
974 $RemoteHost = $_SERVER[
"REMOTE_ADDR"];
976 elseif ($RemoteHost != $_SERVER[
"REMOTE_ADDR"])
978 $RemoteHost .=
" (".$_SERVER[
"REMOTE_ADDR"].
")";
980 $SlowPageLoadMsg =
"Slow page load (" 982 .$this->FullUrl().
" from ".$RemoteHost;
983 $this->
LogMessage(self::LOGLVL_INFO, $SlowPageLoadMsg);
986 # execute callbacks that should not have their output buffered 987 foreach ($this->UnbufferedCallbacks as $Callback)
989 call_user_func_array($Callback[0], $Callback[1]);
992 # log high memory usage 993 if (function_exists(
"memory_get_peak_usage"))
998 && (memory_get_peak_usage() >= $MemoryThreshold))
1000 $HighMemUsageMsg =
"High peak memory usage (" 1001 .intval(memory_get_peak_usage()).
") for " 1002 .$this->FullUrl().
" from " 1003 .$_SERVER[
"REMOTE_ADDR"];
1004 $this->
LogMessage(self::LOGLVL_INFO, $HighMemUsageMsg);
1008 # terminate and stay resident (TSR!) if indicated and HTML has been output 1009 # (only TSR if HTML has been output because otherwise browsers will misbehave) 1010 if ($ShouldTSR) { $this->LaunchTSR(); }
1020 return $this->PageName;
1030 # retrieve current URL 1031 $Url = self::GetScriptUrl();
1033 # remove the base path if present 1034 $BasePath = $this->Settings[
"BasePath"];
1035 if (stripos($Url, $BasePath) === 0)
1037 $Url = substr($Url, strlen($BasePath));
1040 # if we're being accessed via an alternate domain, 1041 # add the appropriate prefix in 1043 self::$RootUrlOverride !== NULL)
1045 $VHost = $_SERVER[
"SERVER_NAME"];
1046 if (isset($this->AlternateDomainPrefixes[$VHost]))
1048 $ThisPrefix = $this->AlternateDomainPrefixes[$VHost];
1049 $Url = $ThisPrefix.
"/".$Url;
1081 && (strpos($Page,
"?") === FALSE)
1082 && ((strpos($Page,
"=") !== FALSE)
1083 || ((stripos($Page,
".php") === FALSE)
1084 && (stripos($Page,
".htm") === FALSE)
1085 && (strpos($Page,
"/") === FALSE)))
1086 && (stripos($Page,
"http://") !== 0)
1087 && (stripos($Page,
"https://") !== 0))
1089 $this->JumpToPage = self::BaseUrl() .
"index.php?P=".$Page;
1093 $this->JumpToPage = $Page;
1095 $this->JumpToPageDelay = $Delay;
1104 return ($this->JumpToPage === NULL) ? FALSE : TRUE;
1118 if ($NewSetting !== NULL) { $this->
HtmlCharset = $NewSetting; }
1119 return $this->HtmlCharset;
1133 if (!is_array($File)) { $File = array($File); }
1134 $this->DoNotMinimizeList = array_merge($this->DoNotMinimizeList, $File);
1149 if ($NewValue !== NULL) { $this->
UseBaseTag = $NewValue ? TRUE : FALSE; }
1150 return $this->UseBaseTag;
1161 $this->SuppressHTML = $NewSetting;
1171 if ($UIName !== NULL)
1173 self::$DefaultUI = $UIName;
1175 return self::$DefaultUI;
1186 if ($UIName !== NULL)
1188 self::$ActiveUI = preg_replace(
"/^SPTUI--/",
"", $UIName);
1190 return self::$ActiveUI;
1207 if (!isset($Interfaces[$FilterExp]))
1209 # retrieve paths to user interface directories 1212 # start out with an empty list 1213 $Interfaces[$FilterExp] = array();
1215 # for each possible UI directory 1216 foreach ($Paths as $CanonicalName => $Path)
1218 # if name file available 1219 $LabelFile = $Path.
"/NAME";
1220 if (is_readable($LabelFile))
1223 $Label = file_get_contents($LabelFile);
1225 # if the UI name looks reasonable 1226 if (strlen(trim($Label)))
1229 $Interfaces[$FilterExp][$CanonicalName] = $Label;
1233 # if we do not have a name yet 1234 if (!isset($Interfaces[$FilterExp][$CanonicalName]))
1236 # use base directory for name 1237 $Interfaces[$FilterExp][$CanonicalName] = basename($Path);
1242 # return list to caller 1243 return $Interfaces[$FilterExp];
1256 static $InterfacePaths;
1258 if (!isset($InterfacePaths[$FilterExp]))
1260 # extract possible UI directories from interface directory list 1261 $InterfaceDirs = array();
1262 foreach ($this->InterfaceDirList as $Dir)
1265 if (preg_match(
"#([a-zA-Z0-9/]*interface)/[a-zA-Z0-9%/]*#",
1269 if (!in_array($Dir, $InterfaceDirs))
1271 $InterfaceDirs[] = $Dir;
1276 # reverse order of interface directories so that the directory 1277 # returned is the base directory for the interface 1278 $InterfaceDirs = array_reverse($InterfaceDirs);
1280 # start out with an empty list 1281 $InterfacePaths[$FilterExp] = array();
1282 $InterfacesFound = array();
1284 # for each possible UI directory 1285 foreach ($InterfaceDirs as $InterfaceDir)
1287 $Dir = dir($InterfaceDir);
1289 # for each file in current directory 1290 while (($DirEntry = $Dir->read()) !== FALSE)
1292 $InterfacePath = $InterfaceDir.
"/".$DirEntry;
1294 # skip anything we have already found 1295 # or that doesn't have a name in the required format 1296 # or that isn't a directory 1297 # or that doesn't match the filter regex (if supplied) 1298 if (in_array($DirEntry, $InterfacesFound)
1299 || !preg_match(
'/^[a-zA-Z0-9]+$/', $DirEntry)
1300 || !is_dir($InterfacePath)
1301 || (($FilterExp !== NULL)
1302 && !preg_match($FilterExp, $InterfacePath)))
1307 # add interface to list 1308 $InterfacePaths[$FilterExp][$DirEntry] = $InterfacePath;
1309 $InterfacesFound[] = $DirEntry;
1316 # return list to caller 1317 return $InterfacePaths[$FilterExp];
1345 &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE,
1346 &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE,
1347 &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE)
1349 $FuncIndex = count($this->PostProcessingFuncs);
1350 $this->PostProcessingFuncs[$FuncIndex][
"FunctionName"] = $FunctionName;
1351 $this->PostProcessingFuncs[$FuncIndex][
"Arguments"] = array();
1353 while (isset(${
"Arg".$Index}) && (${
"Arg".$Index} !== self::NOVALUE))
1355 $this->PostProcessingFuncs[$FuncIndex][
"Arguments"][$Index]
1368 $this->EnvIncludes[] = $FileName;
1379 # determine which location to search based on file suffix 1381 $DirList = ($FileType == self::FT_IMAGE)
1382 ? $this->ImageDirList : $this->IncludeDirList;
1384 # if directed to use minimized JavaScript file 1387 # look for minimized version of file 1388 $MinimizedFileName = substr_replace($FileName,
".min", -3, 0);
1389 $FoundFileName = $this->FindFile($DirList, $MinimizedFileName);
1391 # if minimized file was not found 1392 if (is_null($FoundFileName))
1394 # look for unminimized file 1395 $FoundFileName = $this->FindFile($DirList, $FileName);
1397 # if unminimized file found 1398 if (!is_null($FoundFileName))
1400 # if minimization enabled and supported 1402 && self::JsMinRewriteSupport())
1404 # attempt to create minimized file 1405 $MinFileName = $this->MinimizeJavascriptFile(
1408 # if minimization succeeded 1409 if ($MinFileName !== NULL)
1411 # use minimized version 1412 $FoundFileName = $MinFileName;
1414 # save file modification time if needed for fingerprinting 1417 $FileMTime = filemtime($FoundFileName);
1420 # strip off the cache location, allowing .htaccess 1421 # to handle that for us 1422 $FoundFileName = str_replace(
1423 self::$JSMinCacheDir.
"/",
"", $FoundFileName);
1429 # else if directed to use SCSS files 1432 # look for SCSS version of file 1433 $SourceFileName = preg_replace(
"/.css$/",
".scss", $FileName);
1434 $FoundSourceFileName = $this->FindFile($DirList, $SourceFileName);
1436 # if SCSS file not found 1437 if ($FoundSourceFileName === NULL)
1440 $FoundFileName = $this->FindFile($DirList, $FileName);
1444 # compile SCSS file (if updated) and return resulting CSS file 1445 $FoundFileName = $this->CompileScssFile($FoundSourceFileName);
1447 # save file modification time if needed for fingerprinting 1450 $FileMTime = filemtime($FoundFileName);
1453 # strip off the cache location, allowing .htaccess to handle that for us 1454 if (self::ScssRewriteSupport())
1456 $FoundFileName = str_replace(
1457 self::$ScssCacheDir.
"/",
"", $FoundFileName);
1461 # otherwise just search for the file 1464 $FoundFileName = $this->FindFile($DirList, $FileName);
1467 # add non-image files to list of found files (used for required files loading) 1468 if ($FileType != self::FT_IMAGE)
1469 { $this->FoundUIFiles[] = basename($FoundFileName); }
1471 # if UI file fingerprinting is enabled and supported 1473 && self::UrlFingerprintingRewriteSupport()
1474 && (isset($FileMTime) || file_exists($FoundFileName)))
1476 # if file does not appear to be a server-side inclusion 1477 if (!preg_match(
'/\.(html|php)$/i', $FoundFileName))
1479 # for each URL fingerprinting blacklist entry 1480 $OnBlacklist = FALSE;
1481 foreach ($this->UrlFingerprintBlacklist as $BlacklistEntry)
1483 # if entry looks like a regular expression pattern 1484 if ($BlacklistEntry[0] == substr($BlacklistEntry, -1))
1486 # check file name against regular expression 1487 if (preg_match($BlacklistEntry, $FoundFileName))
1489 $OnBlacklist = TRUE;
1495 # check file name directly against entry 1496 if (basename($FoundFileName) == $BlacklistEntry)
1498 $OnBlacklist = TRUE;
1504 # if file was not on blacklist 1507 # get file modification time if not already retrieved 1508 if (!isset($FileMTime))
1510 $FileMTime = filemtime($FoundFileName);
1513 # add timestamp fingerprint to file name 1514 $Fingerprint = sprintf(
"%06X",
1515 ($FileMTime % 0xFFFFFF));
1516 $FoundFileName = preg_replace(
"/^(.+)\.([a-z]+)$/",
1517 "$1.".$Fingerprint.
".$2",
1523 # return file name to caller 1524 return $FoundFileName;
1537 $FullFileName = $this->
GUIFile($FileName);
1538 if ($FullFileName) { print($FullFileName); }
1557 # convert file name to array if necessary 1558 if (!is_array($FileNames)) { $FileNames = array($FileNames); }
1560 # pad additional attributes if supplied 1561 $AddAttribs = $AdditionalAttributes ?
" ".$AdditionalAttributes :
"";
1564 foreach ($FileNames as $BaseFileName)
1566 # retrieve full file name 1567 $FileName = $this->
GUIFile($BaseFileName);
1572 # print appropriate tag 1573 print $this->GetUIFileLoadingTag(
1574 $FileName, $AdditionalAttributes);
1577 # if we are not already loading an override file 1578 if (!preg_match(
"/-Override.(css|scss|js)$/", $BaseFileName))
1580 # attempt to load override file if available 1585 $OverrideFileName = preg_replace(
1586 "/\.(css|scss)$/",
"-Override.$1",
1589 $AdditionalAttributes);
1592 case self::FT_JAVASCRIPT:
1593 $OverrideFileName = preg_replace(
1594 "/\.js$/",
"-Override.js",
1597 $AdditionalAttributes);
1612 $this->UrlFingerprintBlacklist[] = $Pattern;
1624 $this->AdditionalRequiredUIFiles[] = $FileName;
1634 static $FileTypeCache;
1635 if (isset($FileTypeCache[$FileName]))
1637 return $FileTypeCache[$FileName];
1640 $FileSuffix = strtolower(substr($FileName, -3));
1641 if ($FileSuffix ==
"css")
1643 $FileTypeCache[$FileName] = self::FT_CSS;
1645 elseif ($FileSuffix ==
".js")
1647 $FileTypeCache[$FileName] = self::FT_JAVASCRIPT;
1649 elseif (($FileSuffix ==
"gif")
1650 || ($FileSuffix ==
"jpg")
1651 || ($FileSuffix ==
"png"))
1653 $FileTypeCache[$FileName] = self::FT_IMAGE;
1657 $FileTypeCache[$FileName] = self::FT_OTHER;
1660 return $FileTypeCache[$FileName];
1681 # if specified function is not currently available 1682 if (!is_callable($Callback))
1684 # if function info looks legal 1685 if (is_string($Callback) && strlen($Callback))
1687 # start with function directory list 1688 $Locations = $this->FunctionDirList;
1690 # add object directories to list 1691 $Locations = array_merge(
1692 $Locations, array_keys(self::$ObjectDirectories));
1694 # look for function file 1695 $FunctionFileName = $this->FindFile($Locations,
"F-".$Callback,
1696 array(
"php",
"html"));
1698 # if function file was found 1699 if ($FunctionFileName)
1701 # load function file 1702 include_once($FunctionFileName);
1706 # log error indicating function load failed 1707 $this->
LogError(self::LOGLVL_ERROR,
"Unable to load function" 1708 .
" for callback \"".$Callback.
"\".");
1713 # log error indicating specified function info was bad 1714 $this->
LogError(self::LOGLVL_ERROR,
"Unloadable callback value" 1716 .
" passed to AF::LoadFunction() by " 1717 .StdLib::GetMyCaller().
".");
1721 # report to caller whether function load succeeded 1722 return is_callable($Callback);
1731 return microtime(TRUE) - $this->ExecutionStartTime;
1746 # ---- Page Caching ------------------------------------------------------ 1758 return $this->UpdateSetting(
"PageCacheEnabled", $NewValue);
1769 return $this->UpdateSetting(
"PageCacheExpirationPeriod", $NewValue);
1778 $this->CacheCurrentPage = FALSE;
1790 $Tag = strtolower($Tag);
1792 # if pages were supplied 1793 if ($Pages !== NULL)
1795 # add pages to list for this tag 1796 if (isset($this->PageCacheTags[$Tag]))
1798 $this->PageCacheTags[$Tag] = array_merge(
1799 $this->PageCacheTags[$Tag], $Pages);
1803 $this->PageCacheTags[$Tag] = $Pages;
1808 # add current page to list for this tag 1809 $this->PageCacheTags[$Tag][] =
"CURRENT";
1821 $TagId = $this->GetPageCacheTagId($Tag);
1823 # delete pages and tag/page connections for specified tag 1824 $this->DB->Query(
"DELETE CP, CPTI" 1825 .
" FROM AF_CachedPages CP, AF_CachedPageTagInts CPTI" 1826 .
" WHERE CPTI.TagId = ".intval($TagId)
1827 .
" AND CP.CacheId = CPTI.CacheId");
1835 # clear all page cache tables 1836 $this->DB->Query(
"TRUNCATE TABLE AF_CachedPages");
1837 $this->DB->Query(
"TRUNCATE TABLE AF_CachedPageTags");
1838 $this->DB->Query(
"TRUNCATE TABLE AF_CachedPageTagInts");
1849 $Length = $this->DB->Query(
"SELECT COUNT(*) AS CacheLen" 1850 .
" FROM AF_CachedPages",
"CacheLen");
1851 $Oldest = $this->DB->Query(
"SELECT CachedAt FROM AF_CachedPages" 1852 .
" ORDER BY CachedAt ASC LIMIT 1",
"CachedAt");
1854 "NumberOfEntries" => $Length,
1855 "OldestTimestamp" => strtotime($Oldest),
1862 # ---- Logging ----------------------------------------------------------- 1882 return $this->UpdateSetting(
1883 "LogSlowPageLoads", $NewValue, $Persistent);
1899 return $this->UpdateSetting(
1900 "SlowPageLoadThreshold", $NewValue, $Persistent);
1919 return $this->UpdateSetting(
1920 "LogHighMemoryUsage", $NewValue, $Persistent);
1937 return $this->UpdateSetting(
1938 "HighMemoryUsageThreshold", $NewValue, $Persistent);
1956 # if error level is at or below current logging level 1957 if ($this->Settings[
"LoggingLevel"] >= $Level)
1959 # attempt to log error message 1962 # if logging attempt failed and level indicated significant error 1963 if (($Result === FALSE) && ($Level <= self::LOGLVL_ERROR))
1965 # throw exception about inability to log error 1966 static $AlreadyThrewException = FALSE;
1967 if (!$AlreadyThrewException)
1969 $AlreadyThrewException = TRUE;
1970 throw new Exception(
"Unable to log error (".$Level.
": ".$Msg
1971 .
") to ".$this->LogFileName);
1975 # report to caller whether message was logged 1980 # report to caller that message was not logged 1998 # if message level is at or below current logging level 1999 if ($this->Settings[
"LoggingLevel"] >= $Level)
2001 # attempt to open log file 2002 $FHndl = @fopen($this->LogFileName,
"a");
2004 # if log file could not be open 2005 if ($FHndl === FALSE)
2007 # report to caller that message was not logged 2013 $ErrorAbbrevs = array(
2014 self::LOGLVL_FATAL =>
"FTL",
2015 self::LOGLVL_ERROR =>
"ERR",
2016 self::LOGLVL_WARNING =>
"WRN",
2017 self::LOGLVL_INFO =>
"INF",
2018 self::LOGLVL_DEBUG =>
"DBG",
2019 self::LOGLVL_TRACE =>
"TRC",
2021 $Msg = str_replace(array(
"\n",
"\t",
"\r"),
" ", $Msg);
2022 $Msg = substr(trim($Msg), 0, self::LOGFILE_MAX_LINE_LENGTH);
2023 $LogEntry = date(
"Y-m-d H:i:s")
2024 .
" ".($this->RunningInBackground ?
"B" :
"F")
2025 .
" ".$ErrorAbbrevs[$Level]
2028 # write entry to log 2029 $Success = fwrite($FHndl, $LogEntry.
"\n");
2034 # report to caller whether message was logged 2035 return ($Success === FALSE) ? FALSE : TRUE;
2040 # report to caller that message was not logged 2068 # constrain new level (if supplied) to within legal bounds 2071 $NewValue = max(min($NewValue, 6), 1);
2074 # set new logging level (if supplied) and return current level to caller 2075 return $this->UpdateSetting(
"LoggingLevel", $NewValue);
2086 if ($NewValue !== NULL) { $this->LogFileName = $NewValue; }
2087 return $this->LogFileName;
2101 # return no entries if there isn't a log file or we can't read it 2102 if (!is_readable($this->
LogFile()))
2107 # if max number of entries specified 2110 # load lines from file 2111 $FHandle = fopen($this->
LogFile(),
"r");
2112 $FileSize = filesize($this->
LogFile());
2113 $SeekPosition = max(0,
2114 ($FileSize - (self::LOGFILE_MAX_LINE_LENGTH * ($Limit + 1))));
2115 fseek($FHandle, $SeekPosition);
2116 $Block = fread($FHandle, ($FileSize - $SeekPosition));
2118 $Lines = explode(PHP_EOL, $Block);
2119 array_shift($Lines);
2121 # prune array back to requested number of entries 2122 $Lines = array_slice($Lines, (0 - $Limit));
2126 # load all lines from log file 2127 $Lines = file($this->
LogFile(), FILE_IGNORE_NEW_LINES);
2128 if ($Lines === FALSE)
2134 # reverse line order 2135 $Lines = array_reverse($Lines);
2137 # for each log file line 2139 foreach ($Lines as $Line)
2141 # attempt to parse line into component parts 2142 $Pieces = explode(
" ", $Line, 5);
2143 $Date = isset($Pieces[0]) ? $Pieces[0] :
"";
2144 $Time = isset($Pieces[1]) ? $Pieces[1] :
"";
2145 $Back = isset($Pieces[2]) ? $Pieces[2] :
"";
2146 $Level = isset($Pieces[3]) ? $Pieces[3] :
"";
2147 $Msg = isset($Pieces[4]) ? $Pieces[4] :
"";
2149 # skip line if it looks invalid 2150 $ErrorAbbrevs = array(
2151 "FTL" => self::LOGLVL_FATAL,
2152 "ERR" => self::LOGLVL_ERROR,
2153 "WRN" => self::LOGLVL_WARNING,
2154 "INF" => self::LOGLVL_INFO,
2155 "DBG" => self::LOGLVL_DEBUG,
2156 "TRC" => self::LOGLVL_TRACE,
2158 if ((($Back !=
"F") && ($Back !=
"B"))
2159 || !array_key_exists($Level, $ErrorAbbrevs)
2165 # convert parts into appropriate values and add to entries 2167 "Time" => strtotime($Date.
" ".$Time),
2168 "Background" => ($Back ==
"B") ? TRUE : FALSE,
2169 "Level" => $ErrorAbbrevs[$Level],
2174 # return entries to caller 2219 # ---- Event Handling ---------------------------------------------------- 2265 # convert parameters to array if not already in that form 2266 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
2267 : array($EventsOrEventName => $EventType);
2270 foreach ($Events as $Name => $Type)
2272 # store event information 2273 $this->RegisteredEvents[$Name][
"Type"] = $Type;
2274 $this->RegisteredEvents[$Name][
"Hooks"] = array();
2286 return array_key_exists($EventName, $this->RegisteredEvents)
2298 # the event isn't hooked to if it isn't even registered 2304 # return TRUE if there is at least one callback hooked to the event 2305 return count($this->RegisteredEvents[$EventName][
"Hooks"]) > 0;
2322 $EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
2324 # convert parameters to array if not already in that form 2325 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
2326 : array($EventsOrEventName => $Callback);
2330 foreach ($Events as $EventName => $EventCallback)
2332 # if callback is valid 2333 if (is_callable($EventCallback))
2335 # if this is a periodic event we process internally 2336 if (isset($this->PeriodicEvents[$EventName]))
2339 $this->ProcessPeriodicEvent($EventName, $EventCallback);
2341 # if specified event has been registered 2342 elseif (isset($this->RegisteredEvents[$EventName]))
2344 # add callback for event 2345 $this->RegisteredEvents[$EventName][
"Hooks"][]
2346 = array(
"Callback" => $EventCallback,
"Order" => $Order);
2348 # sort callbacks by order 2349 if (count($this->RegisteredEvents[$EventName][
"Hooks"]) > 1)
2351 usort($this->RegisteredEvents[$EventName][
"Hooks"],
2354 $A[
"Order"], $B[
"Order"]);
2369 # report to caller whether all callbacks were hooked 2387 $EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
2389 # convert parameters to array if not already in that form 2390 $Events = is_array($EventsOrEventName) ? $EventsOrEventName
2391 : array($EventsOrEventName => $Callback);
2395 foreach ($Events as $EventName => $EventCallback)
2397 # if this event has been registered and hooked 2398 if (isset($this->RegisteredEvents[$EventName])
2399 && count($this->RegisteredEvents[$EventName]))
2401 # if this callback has been hooked for this event 2402 $CallbackData = array(
"Callback" => $EventCallback,
"Order" => $Order);
2403 if (in_array($CallbackData,
2404 $this->RegisteredEvents[$EventName][
"Hooks"]))
2407 $HookIndex = array_search($CallbackData,
2408 $this->RegisteredEvents[$EventName][
"Hooks"]);
2409 unset($this->RegisteredEvents[$EventName][
"Hooks"][$HookIndex]);
2415 # report number of callbacks unhooked to caller 2416 return $UnhookCount;
2431 $ReturnValue = NULL;
2433 # if event has been registered 2434 if (isset($this->RegisteredEvents[$EventName]))
2436 # set up default return value (if not NULL) 2437 switch ($this->RegisteredEvents[$EventName][
"Type"])
2439 case self::EVENTTYPE_CHAIN:
2440 $ReturnValue = $Parameters;
2443 case self::EVENTTYPE_NAMED:
2444 $ReturnValue = array();
2448 # for each callback for this event 2449 foreach ($this->RegisteredEvents[$EventName][
"Hooks"] as $Hook)
2452 $Callback = $Hook[
"Callback"];
2453 $Result = ($Parameters !== NULL)
2454 ? call_user_func_array($Callback, $Parameters)
2455 : call_user_func($Callback);
2457 # process return value based on event type 2458 switch ($this->RegisteredEvents[$EventName][
"Type"])
2460 case self::EVENTTYPE_CHAIN:
2461 if ($Result !== NULL)
2463 foreach ($Parameters as $Index => $Value)
2465 if (array_key_exists($Index, $Result))
2467 $Parameters[$Index] = $Result[$Index];
2470 $ReturnValue = $Parameters;
2474 case self::EVENTTYPE_FIRST:
2475 if ($Result !== NULL)
2477 $ReturnValue = $Result;
2482 case self::EVENTTYPE_NAMED:
2483 $CallbackName = is_array($Callback)
2484 ? (is_object($Callback[0])
2485 ? get_class($Callback[0])
2486 : $Callback[0]).
"::".$Callback[1]
2488 $ReturnValue[$CallbackName] = $Result;
2498 $this->
LogError(self::LOGLVL_WARNING,
2499 "Unregistered event (".$EventName.
") signaled by " 2500 .StdLib::GetMyCaller().
".");
2503 # return value if any to caller 2504 return $ReturnValue;
2514 return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE;
2529 # if event is not a periodic event report failure to caller 2530 if (!array_key_exists($EventName, $this->EventPeriods)) {
return FALSE; }
2532 # retrieve last execution time for event if available 2533 $Signature = self::GetCallbackSignature($Callback);
2534 $LastRunTime = $this->DB->Query(
"SELECT LastRunAt FROM PeriodicEvents" 2535 .
" WHERE Signature = '".addslashes($Signature).
"'",
"LastRunAt");
2537 # if event was not found report failure to caller 2538 if ($LastRunTime === NULL) {
return FALSE; }
2540 # calculate next run time based on event period 2541 $NextRunTime = strtotime($LastRunTime) + $this->EventPeriods[$EventName];
2543 # report next run time to caller 2544 return $NextRunTime;
2564 # retrieve last execution times 2565 $this->DB->Query(
"SELECT * FROM PeriodicEvents");
2566 $LastRunTimes = $this->DB->FetchColumn(
"LastRunAt",
"Signature");
2568 # for each known event 2570 foreach ($this->KnownPeriodicEvents as $Signature => $Info)
2572 # if last run time for event is available 2573 if (array_key_exists($Signature, $LastRunTimes))
2575 # calculate next run time for event 2576 $LastRun = strtotime($LastRunTimes[$Signature]);
2577 $NextRun = $LastRun + $this->EventPeriods[$Info[
"Period"]];
2578 if ($Info[
"Period"] ==
"EVENT_PERIODIC") { $LastRun = FALSE; }
2582 # set info to indicate run times are not known 2587 # add event info to list 2588 $Events[$Signature] = $Info;
2589 $Events[$Signature][
"LastRun"] = $LastRun;
2590 $Events[$Signature][
"NextRun"] = $NextRun;
2591 $Events[$Signature][
"Parameters"] = NULL;
2594 # return list of known events to caller 2601 # ---- Task Management --------------------------------------------------- 2627 $Priority = self::PRIORITY_LOW, $Description =
"")
2629 # pack task info and write to database 2630 if ($Parameters === NULL) { $Parameters = array(); }
2631 $this->DB->Query(
"INSERT INTO TaskQueue" 2632 .
" (Callback, Parameters, Priority, Description)" 2633 .
" VALUES ('".addslashes(serialize($Callback)).
"', '" 2634 .addslashes(serialize($Parameters)).
"', ".intval($Priority).
", '" 2635 .addslashes($Description).
"')");
2656 $Priority = self::PRIORITY_LOW, $Description =
"")
2660 $QueryResult = $this->DB->Query(
"SELECT TaskId,Priority FROM TaskQueue" 2661 .
" WHERE Callback = '".addslashes(serialize($Callback)).
"'" 2662 .($Parameters ?
" AND Parameters = '" 2663 .addslashes(serialize($Parameters)).
"'" :
""));
2664 if ($QueryResult !== FALSE)
2666 $Record = $this->DB->FetchRow();
2667 if ($Record[
"Priority"] > $Priority)
2669 $this->DB->Query(
"UPDATE TaskQueue" 2670 .
" SET Priority = ".intval($Priority)
2671 .
" WHERE TaskId = ".intval($Record[
"TaskId"]));
2678 $this->
QueueTask($Callback, $Parameters, $Priority, $Description);
2694 $QueuedCount = $this->DB->Query(
2695 "SELECT COUNT(*) AS FoundCount FROM TaskQueue" 2696 .
" WHERE Callback = '".addslashes(serialize($Callback)).
"'" 2697 .($Parameters ?
" AND Parameters = '" 2698 .addslashes(serialize($Parameters)).
"'" :
""),
2700 $RunningCount = $this->DB->Query(
2701 "SELECT COUNT(*) AS FoundCount FROM RunningTasks" 2702 .
" WHERE Callback = '".addslashes(serialize($Callback)).
"'" 2703 .($Parameters ?
" AND Parameters = '" 2704 .addslashes(serialize($Parameters)).
"'" :
""),
2706 $FoundCount = $QueuedCount + $RunningCount;
2707 return ($FoundCount ? TRUE : FALSE);
2729 return $this->GetTaskList(
"SELECT * FROM TaskQueue" 2730 .
" ORDER BY Priority, TaskId ", $Count, $Offset);
2747 $Parameters = NULL, $Priority = NULL, $Description = NULL)
2749 $Query =
"SELECT COUNT(*) AS TaskCount FROM TaskQueue";
2751 if ($Callback !== NULL)
2753 $Query .= $Sep.
" Callback = '".addslashes(serialize($Callback)).
"'";
2756 if ($Parameters !== NULL)
2758 $Query .= $Sep.
" Parameters = '".addslashes(serialize($Parameters)).
"'";
2761 if ($Priority !== NULL)
2763 $Query .= $Sep.
" Priority = ".intval($Priority);
2766 if ($Description !== NULL)
2768 $Query .= $Sep.
" Description = '".addslashes($Description).
"'";
2770 return $this->DB->Query($Query,
"TaskCount");
2782 return $this->GetTaskList(
"SELECT * FROM RunningTasks" 2783 .
" WHERE StartedAt >= '".date(
"Y-m-d H:i:s",
2785 .
" ORDER BY StartedAt", $Count, $Offset);
2797 return $this->GetTaskList(
"SELECT * FROM RunningTasks" 2798 .
" WHERE StartedAt < '".date(
"Y-m-d H:i:s",
2800 .
" ORDER BY StartedAt", $Count, $Offset);
2809 return $this->DB->Query(
"SELECT COUNT(*) AS Count FROM RunningTasks" 2810 .
" WHERE StartedAt < '".date(
"Y-m-d H:i:s",
2822 $this->DB->Query(
"LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
2823 $this->DB->Query(
"INSERT INTO TaskQueue" 2824 .
" (Callback,Parameters,Priority,Description) " 2825 .
"SELECT Callback, Parameters, Priority, Description" 2826 .
" FROM RunningTasks WHERE TaskId = ".intval($TaskId));
2827 if ($NewPriority !== NULL)
2829 $NewTaskId = $this->DB->LastInsertId();
2830 $this->DB->Query(
"UPDATE TaskQueue SET Priority = " 2831 .intval($NewPriority)
2832 .
" WHERE TaskId = ".intval($NewTaskId));
2834 $this->DB->Query(
"DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
2835 $this->DB->Query(
"UNLOCK TABLES");
2844 $this->DB->Query(
"DELETE FROM TaskQueue WHERE TaskId = ".intval($TaskId));
2845 $this->DB->Query(
"DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
2857 # assume task will not be found 2860 # look for task in task queue 2861 $this->DB->Query(
"SELECT * FROM TaskQueue WHERE TaskId = ".intval($TaskId));
2863 # if task was not found in queue 2864 if (!$this->DB->NumRowsSelected())
2866 # look for task in running task list 2867 $this->DB->Query(
"SELECT * FROM RunningTasks WHERE TaskId = " 2872 if ($this->DB->NumRowsSelected())
2874 # if task was periodic 2875 $Row = $this->DB->FetchRow();
2876 if ($Row[
"Callback"] ==
2877 serialize(array(
"ApplicationFramework",
"PeriodicEventWrapper")))
2879 # unpack periodic task callback 2880 $WrappedCallback = unserialize($Row[
"Parameters"]);
2881 $Task[
"Callback"] = $WrappedCallback[1];
2882 $Task[
"Parameters"] = $WrappedCallback[2];
2886 # unpack task callback and parameters 2887 $Task[
"Callback"] = unserialize($Row[
"Callback"]);
2888 $Task[
"Parameters"] = unserialize($Row[
"Parameters"]);
2892 # return task to caller 2905 return $this->UpdateSetting(
"TaskExecutionEnabled", $NewValue);
2915 return $this->UpdateSetting(
"MaxTasksRunning", $NewValue);
2927 # if task callback is function use function name 2928 $Callback = $TaskInfo[
"Callback"];
2930 if (!is_array($Callback))
2936 # if task callback is object 2937 if (is_object($Callback[0]))
2939 # if task callback is encapsulated ask encapsulation for name 2940 if (method_exists($Callback[0],
"GetCallbackAsText"))
2942 $Name = $Callback[0]->GetCallbackAsText();
2944 # else assemble name from object 2947 $Name = get_class($Callback[0]) .
"::" . $Callback[1];
2950 # else assemble name from supplied info 2953 $Name= $Callback[0] .
"::" . $Callback[1];
2957 # if parameter array was supplied 2958 $Parameters = $TaskInfo[
"Parameters"];
2959 $ParameterString =
"";
2960 if (is_array($Parameters))
2962 # assemble parameter string 2964 foreach ($Parameters as $Parameter)
2966 $ParameterString .= $Separator;
2967 if (is_int($Parameter) || is_float($Parameter))
2969 $ParameterString .= $Parameter;
2971 else if (is_string($Parameter))
2973 $ParameterString .=
"\"".htmlspecialchars($Parameter).
"\"";
2975 else if (is_array($Parameter))
2977 $ParameterString .=
"ARRAY";
2979 else if (is_object($Parameter))
2981 $ParameterString .=
"OBJECT";
2983 else if (is_null($Parameter))
2985 $ParameterString .=
"NULL";
2987 else if (is_bool($Parameter))
2989 $ParameterString .= $Parameter ?
"TRUE" :
"FALSE";
2991 else if (is_resource($Parameter))
2993 $ParameterString .= get_resource_type($Parameter);
2997 $ParameterString .=
"????";
3003 # assemble name and parameters and return result to caller 3004 return $Name.
"(".$ParameterString.
")";
3013 return $this->RunningInBackground;
3023 return isset($this->RunningTask)
3024 ? $this->RunningTask[
"Priority"] : NULL;
3037 if ($Priority === NULL)
3040 if ($Priority === NULL)
3045 return ($Priority > self::PRIORITY_HIGH)
3046 ? ($Priority - 1) : self::PRIORITY_HIGH;
3059 if ($Priority === NULL)
3062 if ($Priority === NULL)
3067 return ($Priority < self::PRIORITY_BACKGROUND)
3068 ? ($Priority + 1) : self::PRIORITY_BACKGROUND;
3074 # ---- Clean URL Support ------------------------------------------------- 3104 public function AddCleanUrl($Pattern, $Page, $GetVars = NULL, $Template = NULL)
3106 # save clean URL mapping parameters 3107 $this->CleanUrlMappings[] = array(
3108 "Pattern" => $Pattern,
3110 "GetVars" => $GetVars,
3113 # if replacement template specified 3114 if ($Template !== NULL)
3116 # if GET parameters specified 3117 if (count($GetVars))
3119 # retrieve all possible permutations of GET parameters 3120 $GetPerms = $this->ArrayPermutations(array_keys($GetVars));
3122 # for each permutation of GET parameters 3123 foreach ($GetPerms as $VarPermutation)
3125 # construct search pattern for permutation 3126 $SearchPattern =
"/href=([\"'])index\\.php\\?P=".$Page;
3127 $GetVarSegment =
"";
3128 foreach ($VarPermutation as $GetVar)
3130 if (preg_match(
"%\\\$[0-9]+%", $GetVars[$GetVar]))
3132 $GetVarSegment .=
"&".$GetVar.
"=((?:(?!\\1)[^&])+)";
3136 $GetVarSegment .=
"&".$GetVar.
"=".$GetVars[$GetVar];
3139 $SearchPattern .= $GetVarSegment.
"\\1/i";
3141 # if template is actually a callback 3142 if (is_callable($Template))
3144 # add pattern to HTML output mod callbacks list 3145 $this->OutputModificationCallbacks[] = array(
3146 "Pattern" => $Pattern,
3148 "SearchPattern" => $SearchPattern,
3149 "Callback" => $Template,
3154 # construct replacement string for permutation 3155 $Replacement = $Template;
3157 foreach ($VarPermutation as $GetVar)
3159 $Replacement = str_replace(
3160 "\$".$GetVar,
"\$".$Index, $Replacement);
3163 $Replacement =
"href=\"".$Replacement.
"\"";
3165 # add pattern to HTML output modifications list 3166 $this->OutputModificationPatterns[] = $SearchPattern;
3167 $this->OutputModificationReplacements[] = $Replacement;
3173 # construct search pattern 3174 $SearchPattern =
"/href=\"index\\.php\\?P=".$Page.
"\"/i";
3176 # if template is actually a callback 3177 if (is_callable($Template))
3179 # add pattern to HTML output mod callbacks list 3180 $this->OutputModificationCallbacks[] = array(
3181 "Pattern" => $Pattern,
3183 "SearchPattern" => $SearchPattern,
3184 "Callback" => $Template,
3189 # add simple pattern to HTML output modifications list 3190 $this->OutputModificationPatterns[] = $SearchPattern;
3191 $this->OutputModificationReplacements[] =
"href=\"".$Template.
"\"";
3204 foreach ($this->CleanUrlMappings as $Info)
3206 if (preg_match($Info[
"Pattern"], $Path))
3225 # the search patterns and callbacks require a specific format 3226 $Format =
"href=\"".str_replace(
"&",
"&", $Path).
"\"";
3229 # perform any regular expression replacements on the search string 3230 $Search = preg_replace($this->OutputModificationPatterns,
3231 $this->OutputModificationReplacements, $Search);
3233 # only run the callbacks if a replacement hasn't already been performed 3234 if ($Search == $Format)
3236 # perform any callback replacements on the search string 3237 foreach ($this->OutputModificationCallbacks as $Info)
3239 # make the information available to the callback 3240 $this->OutputModificationCallbackInfo = $Info;
3242 # execute the callback 3243 $Search = preg_replace_callback($Info[
"SearchPattern"],
3244 array($this,
"OutputModificationCallbackShell"),
3249 # return the path untouched if no replacements were performed 3250 if ($Search == $Format)
3255 # remove the bits added to the search string to get it recognized by 3256 # the replacement expressions and callbacks 3257 $Result = substr($Search, 6, -1);
3270 # for each clean URL mapping 3271 foreach ($this->CleanUrlMappings as $Info)
3273 # if current path matches the clean URL pattern 3274 if (preg_match($Info[
"Pattern"], $Path, $Matches))
3276 # the GET parameters for the URL, starting with the page name 3277 $GetVars = array(
"P" => $Info[
"Page"]);
3279 # if additional $_GET variables specified for clean URL 3280 if ($Info[
"GetVars"] !== NULL)
3282 # for each $_GET variable specified for clean URL 3283 foreach ($Info[
"GetVars"] as $VarName => $VarTemplate)
3285 # start with template for variable value 3286 $Value = $VarTemplate;
3288 # for each subpattern matched in current URL 3289 foreach ($Matches as $Index => $Match)
3291 # if not first (whole) match 3294 # make any substitutions in template 3295 $Value = str_replace(
"$".$Index, $Match, $Value);
3299 # add the GET variable 3300 $GetVars[$VarName] = $Value;
3304 # return the unclean URL 3305 return "index.php?" . http_build_query($GetVars);
3309 # return the path unchanged 3329 $GetVars = array(
"P" => $this->
GetPageName()) + $_GET;
3330 return "index.php?" . http_build_query($GetVars);
3346 $this->AlternateDomainPrefixes[$Domain] = $Prefix;
3351 # ---- Server Environment ------------------------------------------------ 3362 if ($NewValue !== NULL)
3364 self::$SessionLifetime = $NewValue;
3366 return self::$SessionLifetime;
3376 return isset($_SERVER[
"HTACCESS_SUPPORT"])
3377 || isset($_SERVER[
"REDIRECT_HTACCESS_SUPPORT"]);
3388 return isset($_SERVER[
"URL_FINGERPRINTING_SUPPORT"])
3389 || isset($_SERVER[
"REDIRECT_URL_FINGERPRINTING_SUPPORT"]);
3400 return isset($_SERVER[
"SCSS_REWRITE_SUPPORT"])
3401 || isset($_SERVER[
"REDIRECT_SCSS_REWRITE_SUPPORT"]);
3412 return isset($_SERVER[
"JSMIN_REWRITE_SUPPORT"])
3413 || isset($_SERVER[
"REDIRECT_JSMIN_REWRITE_SUPPORT"]);
3425 # return override value if one is set 3426 if (self::$RootUrlOverride !== NULL)
3428 return self::$RootUrlOverride;
3431 # determine scheme name 3432 $Protocol = (isset($_SERVER[
"HTTPS"]) ?
"https" :
"http");
3434 # if HTTP_HOST is preferred or SERVER_NAME points to localhost 3435 # and HTTP_HOST is set 3436 if ((self::$PreferHttpHost || ($_SERVER[
"SERVER_NAME"] ==
"127.0.0.1"))
3437 && isset($_SERVER[
"HTTP_HOST"]))
3439 # use HTTP_HOST for domain name 3440 $DomainName = $_SERVER[
"HTTP_HOST"];
3444 # use SERVER_NAME for domain name 3445 $DomainName = $_SERVER[
"HTTP_HOST"];
3448 # build URL root and return to caller 3449 return $Protocol.
"://".$DomainName;
3468 if ($NewValue !== self::NOVALUE)
3470 self::$RootUrlOverride = strlen(trim($NewValue)) ? $NewValue : NULL;
3472 return self::$RootUrlOverride;
3486 $BaseUrl = self::RootUrl().dirname($_SERVER[
"SCRIPT_NAME"]);
3487 if (substr($BaseUrl, -1) !=
"/") { $BaseUrl .=
"/"; }
3500 return self::RootUrl().$_SERVER[
"REQUEST_URI"];
3515 if ($NewValue !== NULL)
3517 self::$PreferHttpHost = ($NewValue ? TRUE : FALSE);
3519 return self::$PreferHttpHost;
3528 $BasePath = dirname($_SERVER[
"SCRIPT_NAME"]);
3530 if (substr($BasePath, -1) !=
"/")
3545 if (array_key_exists(
"SCRIPT_URL", $_SERVER))
3547 return $_SERVER[
"SCRIPT_URL"];
3549 elseif (array_key_exists(
"REQUEST_URI", $_SERVER))
3551 $Pieces = parse_url($_SERVER[
"REQUEST_URI"]);
3552 return isset($Pieces[
"path"]) ? $Pieces[
"path"] : NULL;
3554 elseif (array_key_exists(
"REDIRECT_URL", $_SERVER))
3556 return $_SERVER[
"REDIRECT_URL"];
3574 # needed to get the path of the URL minus the query and fragment pieces 3575 $Components = parse_url(self::GetScriptUrl());
3577 # if parsing was successful and a path is set 3578 if (is_array($Components) && isset($Components[
"path"]))
3580 $BasePath = self::BasePath();
3581 $Path = $Components[
"path"];
3583 # the URL was rewritten if the path isn't the base path, i.e., the 3584 # home page, and the file in the URL isn't the script generating the 3586 if ($BasePath != $Path && basename($Path) != $ScriptName)
3592 # the URL wasn't rewritten 3603 return self::GetPhpMemoryLimit() - memory_get_usage();
3613 $Str = strtoupper(ini_get(
"memory_limit"));
3614 if (substr($Str, -1) ==
"B") { $Str = substr($Str, 0, strlen($Str) - 1); }
3615 switch (substr($Str, -1))
3618 $MemoryLimit = (int)$Str * 1024;
3622 $MemoryLimit = (int)$Str * 1048576;
3626 $MemoryLimit = (int)$Str * 1073741824;
3630 $MemoryLimit = (int)$Str;
3633 return $MemoryLimit;
3652 $NewValue = max($NewValue, 5);
3653 ini_set(
"max_execution_time", $NewValue);
3655 $this->UpdateSetting(
"MaxExecTime", $NewValue, $Persistent);
3657 return ini_get(
"max_execution_time");
3663 # ---- Utility ----------------------------------------------------------- 3678 public function DownloadFile($FilePath, $FileName = NULL, $MimeType = NULL)
3680 # check that file is readable 3681 if (!is_readable($FilePath))
3686 # if file name was not supplied 3687 if ($FileName === NULL)
3689 # extract file name from path 3690 $FileName = basename($FilePath);
3693 # if MIME type was not supplied 3694 if ($MimeType === NULL)
3696 # attempt to determine MIME type 3697 $FInfoHandle = finfo_open(FILEINFO_MIME);
3700 $FInfoMime = finfo_file($FInfoHandle, $FilePath);
3701 finfo_close($FInfoHandle);
3704 $MimeType = $FInfoMime;
3708 # use default if unable to determine MIME type 3709 if ($MimeType === NULL)
3711 $MimeType =
"application/octet-stream";
3715 # set headers to download file 3716 header(
"Content-Type: ".$MimeType);
3717 header(
"Content-Length: ".filesize($FilePath));
3718 if ($this->CleanUrlRewritePerformed)
3720 header(
'Content-Disposition: attachment; filename="'.$FileName.
'"');
3723 # make sure that apache does not attempt to compress file 3724 apache_setenv(
'no-gzip',
'1');
3726 # send file to user, but unbuffered to avoid memory issues 3729 $BlockSize = 512000;
3730 $Handle = @fopen($File,
"rb");
3731 if ($Handle === FALSE)
3735 while (!feof($Handle))
3737 print fread($Handle, $BlockSize);
3741 }, array($FilePath));
3743 # prevent HTML output that might interfere with download 3746 # set flag to indicate not to log a slow page load in case client 3747 # connection delays PHP execution because of header 3748 $this->DoNotLogSlowPageLoad = TRUE;
3750 # report no errors found to caller 3768 # assume we will not get a lock 3771 # clear out any stale locks 3772 static $CleanupHasBeenDone = FALSE;
3773 if (!$CleanupHasBeenDone)
3775 # (margin for clearing stale locks is twice the known 3776 # maximum PHP execution time, because the max time 3777 # techinically does not include external operations 3778 # like database queries) 3781 $this->DB->Query(
"DELETE FROM AF_Locks WHERE" 3782 .
" ObtainedAt < '".$ClearLocksObtainedBefore.
"' AND" 3783 .
" LockName = '".addslashes($LockName).
"'");
3788 # lock database table so nobody else can try to get a lock 3789 $this->DB->Query(
"LOCK TABLES AF_Locks WRITE");
3791 # look for lock with specified name 3792 $FoundCount = $this->DB->Query(
"SELECT COUNT(*) AS FoundCount" 3793 .
" FROM AF_Locks WHERE LockName = '" 3794 .addslashes($LockName).
"'",
"FoundCount");
3795 $LockFound = ($FoundCount > 0) ? TRUE : FALSE;
3800 # unlock database tables 3801 $this->DB->Query(
"UNLOCK TABLES");
3803 # if blocking was requested 3806 # wait to give someone else a chance to release lock 3812 # while lock was found and blocking was requested 3813 }
while ($LockFound && $Wait);
3816 # if lock was not found 3820 $this->DB->Query(
"INSERT INTO AF_Locks (LockName) VALUES ('" 3821 .addslashes($LockName).
"')");
3824 # unlock database tables 3825 $this->DB->Query(
"UNLOCK TABLES");
3828 # report to caller whether lock was obtained 3841 # release any existing locks 3842 $this->DB->Query(
"DELETE FROM AF_Locks WHERE LockName = '" 3843 .addslashes($LockName).
"'");
3845 # report to caller whether existing lock was released 3846 return $this->DB->NumRowsAffected() ? TRUE : FALSE;
3852 # ---- Backward Compatibility -------------------------------------------- 3864 return $this->FindFile(
3865 $this->IncludeDirList, $BaseName, array(
"tpl",
"html"));
3871 # ---- PRIVATE INTERFACE ------------------------------------------------- 3873 private $AdditionalRequiredUIFiles = array();
3874 private $BackgroundTaskMemLeakLogThreshold = 10; # percentage of max mem
3875 private $BackgroundTaskMinFreeMemPercent = 25;
3876 private $BrowserDetectFunc;
3877 private $CacheCurrentPage = TRUE;
3878 private $AlternateDomainPrefixes = array();
3879 private $CleanUrlMappings = array();
3880 private $CleanUrlRewritePerformed = FALSE;
3881 private $CssUrlFingerprintPath;
3883 private $DefaultPage =
"Home";
3884 private $DoNotMinimizeList = array();
3885 private $DoNotLogSlowPageLoad = FALSE;
3886 private $EnvIncludes = array();
3887 private $ExecutionStartTime;
3888 private $FoundUIFiles = array();
3889 private $HtmlCharset =
"UTF-8";
3890 private $JSMinimizerJavaScriptPackerAvailable = FALSE;
3891 private $JSMinimizerJShrinkAvailable = TRUE;
3892 private $JumpToPage = NULL;
3893 private $JumpToPageDelay = 0;
3894 private $LogFileName =
"local/logs/site.log";
3895 private $MaxRunningTasksToTrack = 250;
3896 private $OutputModificationCallbackInfo;
3897 private $OutputModificationCallbacks = array();
3898 private $OutputModificationPatterns = array();
3899 private $OutputModificationReplacements = array();
3900 private $PageCacheTags = array();
3902 private $PostProcessingFuncs = array();
3903 private $RunningInBackground = FALSE;
3904 private $RunningTask;
3905 private $SavedContext;
3906 private $SaveTemplateLocationCache = FALSE;
3907 private $SessionStorage;
3908 private $SessionGcProbability;
3910 private $SuppressHTML = FALSE;
3911 private $TemplateLocationCache;
3912 private $TemplateLocationCacheInterval = 60; # in minutes
3913 private $TemplateLocationCacheExpiration;
3914 private $UnbufferedCallbacks = array();
3915 private $UrlFingerprintBlacklist = array();
3916 private $UseBaseTag = FALSE;
3918 private static $ActiveUI =
"default";
3919 private static $AppName =
"ScoutAF";
3920 private static $DefaultUI =
"default";
3921 private static $JSMinCacheDir =
"local/data/caches/JSMin";
3922 private static $ObjectDirectories = array();
3923 private static $ObjectLocationCache;
3924 private static $ObjectLocationCacheInterval = 60;
3925 private static $ObjectLocationCacheExpiration;
3926 private static $PreferHttpHost = FALSE;
3927 private static $RootUrlOverride = NULL;
3928 private static $SaveObjectLocationCache = FALSE;
3929 private static $ScssCacheDir =
"local/data/caches/SCSS";
3930 private static $SessionLifetime = 1440; # in seconds
3932 # offset used to generate page cache tag IDs from numeric tags 3935 # minimum expired session garbage collection probability 3942 private $NoTSR = FALSE;
3944 private $KnownPeriodicEvents = array();
3945 private $PeriodicEvents = array(
3946 "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT,
3947 "EVENT_DAILY" => self::EVENTTYPE_DEFAULT,
3948 "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT,
3949 "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT,
3950 "EVENT_PERIODIC" => self::EVENTTYPE_NAMED,
3952 private $EventPeriods = array(
3953 "EVENT_HOURLY" => 3600,
3954 "EVENT_DAILY" => 86400,
3955 "EVENT_WEEKLY" => 604800,
3956 "EVENT_MONTHLY" => 2592000,
3957 "EVENT_PERIODIC" => 0,
3959 private $UIEvents = array(
3960 "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT,
3961 "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN,
3962 "EVENT_PHP_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
3963 "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN,
3964 "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
3965 "EVENT_PAGE_OUTPUT_FILTER" => self::EVENTTYPE_CHAIN,
3972 private function LoadSettings()
3974 # read settings in from database 3975 $this->DB->Query(
"SELECT * FROM ApplicationFrameworkSettings");
3976 $this->Settings = $this->DB->FetchRow();
3978 # if settings were not previously initialized 3979 if ($this->Settings === FALSE)
3981 # initialize settings in database 3982 $this->DB->Query(
"INSERT INTO ApplicationFrameworkSettings" 3983 .
" (LastTaskRunAt) VALUES ('2000-01-02 03:04:05')");
3985 # read new settings in from database 3986 $this->DB->Query(
"SELECT * FROM ApplicationFrameworkSettings");
3987 $this->Settings = $this->DB->FetchRow();
3989 # bail out if reloading new settings failed 3990 if ($this->Settings === FALSE)
3992 throw new Exception(
3993 "Unable to load application framework settings.");
3997 # if base path was not previously set or we appear to have moved 3998 if (!array_key_exists(
"BasePath", $this->Settings)
3999 || (!strlen($this->Settings[
"BasePath"]))
4000 || (!array_key_exists(
"BasePathCheck", $this->Settings))
4001 || (__FILE__ != $this->Settings[
"BasePathCheck"]))
4003 # attempt to extract base path from Apache .htaccess file 4004 if (is_readable(
".htaccess"))
4006 $Lines = file(
".htaccess");
4007 foreach ($Lines as $Line)
4009 if (preg_match(
"/\\s*RewriteBase\\s+/", $Line))
4011 $Pieces = preg_split(
4012 "/\\s+/", $Line, NULL, PREG_SPLIT_NO_EMPTY);
4013 $BasePath = $Pieces[1];
4018 # if base path was found 4019 if (isset($BasePath))
4021 # save base path locally 4022 $this->Settings[
"BasePath"] = $BasePath;
4024 # save base path to database 4025 $this->DB->Query(
"UPDATE ApplicationFrameworkSettings" 4026 .
" SET BasePath = '".addslashes($BasePath).
"'" 4027 .
", BasePathCheck = '".addslashes(__FILE__).
"'");
4031 # retrieve template location cache 4032 $this->TemplateLocationCache = unserialize(
4033 $this->Settings[
"TemplateLocationCache"]);
4034 $this->TemplateLocationCacheInterval =
4035 $this->Settings[
"TemplateLocationCacheInterval"];
4036 $this->TemplateLocationCacheExpiration =
4037 strtotime($this->Settings[
"TemplateLocationCacheExpiration"]);
4039 # if template location cache looks invalid or has expired 4040 $CurrentTime = time();
4041 if (!count($this->TemplateLocationCache)
4042 || ($CurrentTime >= $this->TemplateLocationCacheExpiration))
4044 # clear cache and reset cache expiration 4045 $this->TemplateLocationCache = array();
4046 $this->TemplateLocationCacheExpiration =
4047 $CurrentTime + ($this->TemplateLocationCacheInterval * 60);
4048 $this->SaveTemplateLocationCache = TRUE;
4051 # retrieve object location cache 4052 self::$ObjectLocationCache =
4053 unserialize($this->Settings[
"ObjectLocationCache"]);
4054 self::$ObjectLocationCacheInterval =
4055 $this->Settings[
"ObjectLocationCacheInterval"];
4056 self::$ObjectLocationCacheExpiration =
4057 strtotime($this->Settings[
"ObjectLocationCacheExpiration"]);
4059 # if object location cache looks invalid or has expired 4060 if (!count(self::$ObjectLocationCache)
4061 || ($CurrentTime >= self::$ObjectLocationCacheExpiration))
4063 # clear cache and reset cache expiration 4064 self::$ObjectLocationCache = array();
4065 self::$ObjectLocationCacheExpiration =
4066 $CurrentTime + (self::$ObjectLocationCacheInterval * 60);
4067 self::$SaveObjectLocationCache = TRUE;
4077 private function RewriteCleanUrls($PageName)
4079 # if URL rewriting is supported by the server 4082 # retrieve current URL and remove base path if present 4085 # for each clean URL mapping 4086 foreach ($this->CleanUrlMappings as $Info)
4088 # if current URL matches clean URL pattern 4089 if (preg_match($Info[
"Pattern"], $Url, $Matches))
4092 $PageName = $Info[
"Page"];
4094 # if $_GET variables specified for clean URL 4095 if ($Info[
"GetVars"] !== NULL)
4097 # for each $_GET variable specified for clean URL 4098 foreach ($Info[
"GetVars"] as $VarName => $VarTemplate)
4100 # start with template for variable value 4101 $Value = $VarTemplate;
4103 # for each subpattern matched in current URL 4104 foreach ($Matches as $Index => $Match)
4106 # if not first (whole) match 4109 # make any substitutions in template 4110 $Value = str_replace(
"$".$Index, $Match, $Value);
4114 # set $_GET variable 4115 $_GET[$VarName] = $Value;
4119 # set flag indicating clean URL mapped 4120 $this->CleanUrlRewritePerformed = TRUE;
4122 # stop looking for a mapping 4128 # return (possibly) updated page name to caller 4144 private function RewriteAlternateDomainUrls($Html)
4147 self::$RootUrlOverride !== NULL)
4149 $VHost = $_SERVER[
"SERVER_NAME"];
4150 if (isset($this->AlternateDomainPrefixes[$VHost]))
4152 $ThisPrefix = $this->AlternateDomainPrefixes[$VHost];
4154 # get the URL for the primary domain 4155 $RootUrl = $this->
RootUrl().
"/";
4157 # and figure out what protcol we were using 4158 $Protocol = (isset($_SERVER[
"HTTPS"]) ?
"https" :
"http");
4160 # convert all relative URLs absolute URLs within our 4161 # primary domain, then substitute in the alternate domain 4162 # for paths inside our configured prefix 4163 $RelativePathPatterns = array(
4164 "%src=\"(?!http://|https://)%i",
4165 "%src='(?!http://|https://)%i",
4166 "%href=\"(?!http://|https://)%i",
4167 "%href='(?!http://|https://)%i",
4168 "%action=\"(?!http://|https://)%i",
4169 "%action='(?!http://|https://)%i",
4170 "%@import\s+url\(\"(?!http://|https://)%i",
4171 "%@import\s+url\('(?!http://|https://)%i",
4172 "%src:\s+url\(\"(?!http://|https://)%i",
4173 "%src:\s+url\('(?!http://|https://)%i",
4174 "%@import\s+\"(?!http://|https://)%i",
4175 "%@import\s+'(?!http://|https://)%i",
4176 "%".preg_quote($RootUrl.$ThisPrefix.
"/",
"%").
"%",
4178 $RelativePathReplacements = array(
4183 "action=\"".$RootUrl,
4184 "action='".$RootUrl,
4185 "@import url(\"".$RootUrl,
4186 "@import url('".$RootUrl,
4187 "src: url(\"".$RootUrl,
4188 "src: url('".$RootUrl,
4189 "@import \"".$RootUrl,
4190 "@import '".$RootUrl,
4191 $Protocol.
"://".$VHost.
"/",
4194 $NewHtml = preg_replace(
4195 $RelativePathPatterns,
4196 $RelativePathReplacements,
4199 # check to make sure relative path fixes didn't fail 4200 $Html = $this->CheckOutputModification(
4202 "alternate domain substitutions");
4227 private function FindFile($DirectoryList, $BaseName,
4228 $PossibleSuffixes = NULL, $PossiblePrefixes = NULL)
4230 # generate template cache index for this page 4231 $CacheIndex = md5(serialize($DirectoryList))
4232 .
":".self::$ActiveUI.
":".$BaseName;
4234 # if caching is enabled and we have cached location 4235 if (($this->TemplateLocationCacheInterval > 0)
4236 && array_key_exists($CacheIndex,
4237 $this->TemplateLocationCache))
4239 # use template location from cache 4240 $FoundFileName = $this->TemplateLocationCache[$CacheIndex];
4244 # if suffixes specified and base name does not include suffix 4245 if (count($PossibleSuffixes)
4246 && !preg_match(
"/\.[a-zA-Z0-9]+$/", $BaseName))
4248 # add versions of file names with suffixes to file name list 4249 $FileNames = array();
4250 foreach ($PossibleSuffixes as $Suffix)
4252 $FileNames[] = $BaseName.
".".$Suffix;
4257 # use base name as file name 4258 $FileNames = array($BaseName);
4261 # if prefixes specified 4262 if (count($PossiblePrefixes))
4264 # add versions of file names with prefixes to file name list 4265 $NewFileNames = array();
4266 foreach ($FileNames as $FileName)
4268 foreach ($PossiblePrefixes as $Prefix)
4270 $NewFileNames[] = $Prefix.$FileName;
4273 $FileNames = $NewFileNames;
4276 # for each possible location 4277 $FoundFileName = NULL;
4278 foreach ($DirectoryList as $Dir)
4280 # substitute active or default UI name into path 4281 $Dir = str_replace(array(
"%ACTIVEUI%",
"%DEFAULTUI%"),
4282 array(self::$ActiveUI, self::$DefaultUI), $Dir);
4284 # for each possible file name 4285 foreach ($FileNames as $File)
4287 # if template is found at location 4288 if (file_exists($Dir.$File))
4290 # save full template file name and stop looking 4291 $FoundFileName = $Dir.$File;
4297 # save location in cache 4298 $this->TemplateLocationCache[$CacheIndex]
4301 # set flag indicating that cache should be saved 4302 $this->SaveTemplateLocationCache = TRUE;
4305 # return full template file name to caller 4306 return $FoundFileName;
4317 private function CompileScssFile($SrcFile)
4319 # build path to CSS file 4320 $DstFile = self::$ScssCacheDir.
"/".dirname($SrcFile)
4321 .
"/".basename($SrcFile);
4322 $DstFile = substr_replace($DstFile,
"css", -4);
4324 # if SCSS file is newer than CSS file 4325 if (!file_exists($DstFile)
4326 || (filemtime($SrcFile) > filemtime($DstFile)))
4328 # attempt to create CSS cache subdirectory if not present 4329 if (!is_dir(dirname($DstFile)))
4331 @mkdir(dirname($DstFile), 0777, TRUE);
4334 # if CSS cache directory and CSS file path appear writable 4335 static $CacheDirIsWritable;
4336 if (!isset($CacheDirIsWritable))
4337 { $CacheDirIsWritable = is_writable(self::$ScssCacheDir); }
4338 if (is_writable($DstFile)
4339 || (!file_exists($DstFile) && $CacheDirIsWritable))
4341 # load SCSS and compile to CSS 4342 $ScssCode = file_get_contents($SrcFile);
4343 $ScssCompiler =
new scssc();
4345 ?
"scss_formatter_compressed" :
"scss_formatter");
4348 $CssCode = $ScssCompiler->compile($ScssCode);
4350 # add fingerprinting for URLs in CSS 4351 $this->CssUrlFingerprintPath = dirname($SrcFile);
4352 $CssCode = preg_replace_callback(
4353 "/url\((['\"]*)(.+)\.([a-z]+)(['\"]*)\)/",
4354 array($this,
"CssUrlFingerprintInsertion"),
4357 # strip out comments from CSS (if requested) 4360 $CssCode = preg_replace(
'!/\*[^*]*\*+([^/][^*]*\*+)*/!',
4364 # write out CSS file 4365 file_put_contents($DstFile, $CssCode);
4367 catch (Exception $Ex)
4369 $this->
LogError(self::LOGLVL_ERROR,
4370 "Error compiling SCSS file ".$SrcFile.
": " 4371 .$Ex->getMessage());
4377 # log error and set CSS file path to indicate failure 4378 $this->
LogError(self::LOGLVL_ERROR,
4379 "Unable to write out CSS file (compiled from SCSS) to " 4385 # return CSS file path to caller 4396 private function MinimizeJavascriptFile($SrcFile)
4398 # bail out if file is on exclusion list 4399 foreach ($this->DoNotMinimizeList as $DNMFile)
4401 if (($SrcFile == $DNMFile) || (basename($SrcFile) == $DNMFile))
4407 # build path to minimized file 4408 $DstFile = self::$JSMinCacheDir.
"/".dirname($SrcFile)
4409 .
"/".basename($SrcFile);
4410 $DstFile = substr_replace($DstFile,
".min", -3, 0);
4412 # if original file is newer than minimized file 4413 if (!file_exists($DstFile)
4414 || (filemtime($SrcFile) > filemtime($DstFile)))
4416 # attempt to create cache subdirectory if not present 4417 if (!is_dir(dirname($DstFile)))
4419 @mkdir(dirname($DstFile), 0777, TRUE);
4422 # if cache directory and minimized file path appear writable 4423 static $CacheDirIsWritable;
4424 if (!isset($CacheDirIsWritable))
4425 { $CacheDirIsWritable = is_writable(self::$JSMinCacheDir); }
4426 if (is_writable($DstFile)
4427 || (!file_exists($DstFile) && $CacheDirIsWritable))
4429 # load JavaScript code 4430 $Code = file_get_contents($SrcFile);
4432 # decide which minimizer to use 4433 if ($this->JSMinimizerJavaScriptPackerAvailable
4434 && $this->JSMinimizerJShrinkAvailable)
4436 $Minimizer = (strlen($Code) < 5000)
4437 ?
"JShrink" :
"JavaScriptPacker";
4439 elseif ($this->JSMinimizerJShrinkAvailable)
4441 $Minimizer =
"JShrink";
4445 $Minimizer =
"NONE";
4451 case "JavaScriptMinimizer":
4453 $MinimizedCode = $Packer->pack();
4461 catch (Exception $Exception)
4463 unset($MinimizedCode);
4464 $MinimizeError = $Exception->getMessage();
4469 # if minimization succeeded 4470 if (isset($MinimizedCode))
4472 # write out minimized file 4473 file_put_contents($DstFile, $MinimizedCode);
4477 # log error and set destination file path to indicate failure 4478 $ErrMsg =
"Unable to minimize JavaScript file ".$SrcFile;
4479 if (isset($MinimizeError))
4481 $ErrMsg .=
" (".$MinimizeError.
")";
4483 $this->
LogError(self::LOGLVL_ERROR, $ErrMsg);
4489 # log error and set destination file path to indicate failure 4490 $this->
LogError(self::LOGLVL_ERROR,
4491 "Unable to write out minimized JavaScript to file ".$DstFile);
4496 # return CSS file path to caller 4507 private function CssUrlFingerprintInsertion($Matches)
4509 # generate fingerprint string from CSS file modification time 4510 $FileName = realpath($this->CssUrlFingerprintPath.
"/".
4511 $Matches[2].
".".$Matches[3]);
4512 $MTime = filemtime($FileName);
4513 $Fingerprint = sprintf(
"%06X", ($MTime % 0xFFFFFF));
4515 # build URL string with fingerprint and return it to caller 4516 return "url(".$Matches[1].$Matches[2].
".".$Fingerprint
4517 .
".".$Matches[3].$Matches[4].
")";
4526 private function GetRequiredFilesNotYetLoaded($PageContentFile)
4528 # start out assuming no files required 4529 $RequiredFiles = array();
4531 # if page content file supplied 4532 if ($PageContentFile)
4534 # if file containing list of required files is available 4535 $Path = dirname($PageContentFile);
4536 $RequireListFile = $Path.
"/REQUIRES";
4537 if (file_exists($RequireListFile))
4539 # read in list of required files 4540 $RequestedFiles = file($RequireListFile);
4542 # for each line in required file list 4543 foreach ($RequestedFiles as $Line)
4545 # if line is not a comment 4546 $Line = trim($Line);
4547 if (!preg_match(
"/^#/", $Line))
4549 # if file has not already been loaded 4550 if (!in_array($Line, $this->FoundUIFiles))
4552 # add to list of required files 4553 $RequiredFiles[] = $Line;
4560 # add in additional required files if any 4561 if (count($this->AdditionalRequiredUIFiles))
4563 # make sure there are no duplicates 4564 $AdditionalRequiredUIFiles = array_unique(
4565 $this->AdditionalRequiredUIFiles);
4567 $RequiredFiles = array_merge(
4568 $RequiredFiles, $AdditionalRequiredUIFiles);
4571 # return list of required files to caller 4572 return $RequiredFiles;
4585 private function GetUIFileLoadingTag($FileName, $AdditionalAttributes = NULL)
4587 # pad additional attributes if supplied 4588 $AddAttribs = $AdditionalAttributes ?
" ".$AdditionalAttributes :
"";
4590 # retrieve type of UI file 4593 # construct tag based on file type 4597 $Tag =
"<link rel=\"stylesheet\" type=\"text/css\"" 4598 .
" media=\"all\" href=\"".$FileName.
"\"" 4602 case self::FT_JAVASCRIPT:
4603 $Tag =
"<script type=\"text/javascript\"" 4604 .
" src=\"".$FileName.
"\"" 4605 .$AddAttribs.
"></script>";
4613 # return constructed tag to caller 4621 private function AutoloadObjects($ClassName)
4623 # if caching is not turned off 4624 # and we have a cached location for class 4625 # and file at cached location is readable 4626 if ((self::$ObjectLocationCacheInterval > 0)
4627 && array_key_exists($ClassName,
4628 self::$ObjectLocationCache)
4629 && is_readable(self::$ObjectLocationCache[$ClassName]))
4631 # use object location from cache 4632 require_once(self::$ObjectLocationCache[$ClassName]);
4636 # convert any namespace separators in class name 4637 $ClassName = str_replace(
"\\",
"-", $ClassName);
4639 # for each possible object file directory 4641 foreach (self::$ObjectDirectories as $Location => $Info)
4643 # make any needed replacements in directory path 4644 $Location = str_replace(array(
"%ACTIVEUI%",
"%DEFAULTUI%"),
4645 array(self::$ActiveUI, self::$DefaultUI), $Location);
4647 # if directory looks valid 4648 if (is_dir($Location))
4650 # build class file name 4651 $NewClassName = ($Info[
"ClassPattern"] && $Info[
"ClassReplacement"])
4652 ? preg_replace($Info[
"ClassPattern"],
4653 $Info[
"ClassReplacement"], $ClassName)
4656 # read in directory contents if not already retrieved 4657 if (!isset($FileLists[$Location]))
4659 $FileLists[$Location] = self::ReadDirectoryTree(
4660 $Location,
'/^.+\.php$/i');
4663 # for each file in target directory 4664 $FileNames = $FileLists[$Location];
4665 $TargetName = strtolower($Info[
"Prefix"].$NewClassName.
".php");
4666 foreach ($FileNames as $FileName)
4668 # if file matches our target object file name 4669 if (strtolower($FileName) == $TargetName)
4671 # include object file 4672 require_once($Location.$FileName);
4674 # save location to cache 4675 self::$ObjectLocationCache[$ClassName]
4676 = $Location.$FileName;
4678 # set flag indicating that cache should be saved 4679 self::$SaveObjectLocationCache = TRUE;
4697 private static function ReadDirectoryTree($Directory, $Pattern)
4699 $CurrentDir = getcwd();
4701 $DirIter =
new RecursiveDirectoryIterator(
".");
4702 $IterIter =
new RecursiveIteratorIterator($DirIter);
4703 $RegexResults =
new RegexIterator($IterIter, $Pattern,
4704 RecursiveRegexIterator::GET_MATCH);
4705 $FileList = array();
4706 foreach ($RegexResults as $Result)
4708 $FileList[] = substr($Result[0], 2);
4717 private function UndoMagicQuotes()
4719 # if this PHP version has magic quotes support 4720 if (version_compare(PHP_VERSION,
"5.4.0",
"<"))
4722 # turn off runtime magic quotes if on 4723 if (get_magic_quotes_runtime())
4726 set_magic_quotes_runtime(FALSE);
4730 # if magic quotes GPC is on 4731 if (get_magic_quotes_gpc())
4733 # strip added slashes from incoming variables 4734 $GPC = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
4735 array_walk_recursive($GPC,
4736 array($this,
"UndoMagicQuotes_StripCallback"));
4744 private function UndoMagicQuotes_StripCallback(&$Value)
4746 $Value = stripslashes($Value);
4753 private function LoadUIFunctions()
4756 "local/interface/%ACTIVEUI%/include",
4757 "interface/%ACTIVEUI%/include",
4758 "local/interface/%DEFAULTUI%/include",
4759 "interface/%DEFAULTUI%/include",
4761 foreach ($Dirs as $Dir)
4763 $Dir = str_replace(array(
"%ACTIVEUI%",
"%DEFAULTUI%"),
4764 array(self::$ActiveUI, self::$DefaultUI), $Dir);
4767 $FileNames = scandir($Dir);
4768 foreach ($FileNames as $FileName)
4770 if (preg_match(
"/^F-([A-Za-z0-9_]+)\.php/",
4771 $FileName, $Matches)
4772 || preg_match(
"/^F-([A-Za-z0-9_]+)\.html/",
4773 $FileName, $Matches))
4775 if (!function_exists($Matches[1]))
4777 include_once($Dir.
"/".$FileName);
4790 private function ProcessPeriodicEvent($EventName, $Callback)
4792 # retrieve last execution time for event if available 4793 $Signature = self::GetCallbackSignature($Callback);
4794 $LastRun = $this->DB->Query(
"SELECT LastRunAt FROM PeriodicEvents" 4795 .
" WHERE Signature = '".addslashes($Signature).
"'",
"LastRunAt");
4797 # determine whether enough time has passed for event to execute 4798 $ShouldExecute = (($LastRun === NULL)
4799 || (time() > (strtotime($LastRun) + $this->EventPeriods[$EventName])))
4802 # if event should run 4805 # add event to task queue 4806 $WrapperCallback = array(
"ApplicationFramework",
"PeriodicEventWrapper");
4807 $WrapperParameters = array(
4808 $EventName, $Callback, array(
"LastRunAt" => $LastRun));
4812 # add event to list of periodic events 4813 $this->KnownPeriodicEvents[$Signature] = array(
4814 "Period" => $EventName,
4815 "Callback" => $Callback,
4816 "Queued" => $ShouldExecute);
4826 private static function PeriodicEventWrapper($EventName, $Callback, $Parameters)
4829 if (!isset($DB)) { $DB =
new Database(); }
4832 $ReturnVal = call_user_func_array($Callback, $Parameters);
4834 # if event is already in database 4835 $Signature = self::GetCallbackSignature($Callback);
4836 if ($DB->Query(
"SELECT COUNT(*) AS EventCount FROM PeriodicEvents" 4837 .
" WHERE Signature = '".addslashes($Signature).
"'",
"EventCount"))
4839 # update last run time for event 4840 $DB->Query(
"UPDATE PeriodicEvents SET LastRunAt = " 4841 .(($EventName ==
"EVENT_PERIODIC")
4842 ?
"'".date(
"Y-m-d H:i:s", time() + ($ReturnVal * 60)).
"'" 4844 .
" WHERE Signature = '".addslashes($Signature).
"'");
4848 # add last run time for event to database 4849 $DB->Query(
"INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES " 4850 .
"('".addslashes($Signature).
"', " 4851 .(($EventName ==
"EVENT_PERIODIC")
4852 ?
"'".date(
"Y-m-d H:i:s", time() + ($ReturnVal * 60)).
"'" 4862 private static function GetCallbackSignature($Callback)
4864 return !is_array($Callback) ? $Callback
4865 : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0])
4873 private function PrepForTSR()
4875 # if HTML has been output and it's time to launch another task 4876 # (only TSR if HTML has been output because otherwise browsers 4877 # may misbehave after connection is closed) 4878 if ((PHP_SAPI !=
"cli")
4879 && ($this->JumpToPage || !$this->SuppressHTML)
4880 && (time() > (strtotime($this->Settings[
"LastTaskRunAt"])
4882 / $this->Settings[
"MaxTasksRunning"]) + 5))
4884 && $this->Settings[
"TaskExecutionEnabled"])
4886 # begin buffering output for TSR 4889 # let caller know it is time to launch another task 4894 # let caller know it is not time to launch another task 4903 private function LaunchTSR()
4905 # set headers to close out connection to browser 4908 ignore_user_abort(TRUE);
4909 header(
"Connection: close");
4910 header(
"Content-Length: ".ob_get_length());
4913 # output buffered content 4914 while (ob_get_level()) { ob_end_flush(); }
4917 # write out any outstanding data and end HTTP session 4918 session_write_close();
4920 # set flag indicating that we are now running in background 4921 $this->RunningInBackground = TRUE;
4923 # handle garbage collection for session data 4924 if (isset($this->SessionStorage) &&
4925 (rand()/getrandmax()) <= $this->SessionGcProbability)
4927 # determine when sessions will expire 4928 $ExpiredTime = strtotime(
"-". self::$SessionLifetime.
" seconds");
4930 # iterate over files in the session directory with a DirectoryIterator 4931 # NB: we cannot use scandir() here because it reads the 4932 # entire list of files into memory and may exceed the memory 4933 # limit for directories with very many files 4934 $DI =
new DirectoryIterator($this->SessionStorage);
4935 while ($DI->valid())
4937 if ((strpos($DI->getFilename(),
"sess_") === 0) &&
4939 $DI->getCTime() < $ExpiredTime)
4941 unlink($DI->getPathname());
4947 # if there is still a task in the queue 4950 # garbage collect to give as much memory as possible for tasks 4951 if (function_exists(
"gc_collect_cycles")) { gc_collect_cycles(); }
4953 # turn on output buffering to (hopefully) record any crash output 4956 # lock tables and grab last task run time to double check 4957 $this->DB->Query(
"LOCK TABLES ApplicationFrameworkSettings WRITE");
4958 $this->LoadSettings();
4960 # if still time to launch another task 4961 if (time() > (strtotime($this->Settings[
"LastTaskRunAt"])
4963 / $this->Settings[
"MaxTasksRunning"]) + 5))
4965 # update the "last run" time and release tables 4966 $this->DB->Query(
"UPDATE ApplicationFrameworkSettings" 4967 .
" SET LastTaskRunAt = '".date(
"Y-m-d H:i:s").
"'");
4968 $this->DB->Query(
"UNLOCK TABLES");
4970 # run tasks while there is a task in the queue 4971 # and enough time and memory left 4975 $this->RunNextTask();
4977 # calculate percentage of memory still available 4978 $PercentFreeMem = (self::GetFreeMemory()
4979 / self::GetPhpMemoryLimit()) * 100;
4983 && ($PercentFreeMem > $this->BackgroundTaskMinFreeMemPercent));
4988 $this->DB->Query(
"UNLOCK TABLES");
5002 private function GetTaskList($DBQuery, $Count, $Offset)
5004 $this->DB->Query($DBQuery.
" LIMIT ".intval($Offset).
",".intval($Count));
5006 while ($Row = $this->DB->FetchRow())
5008 $Tasks[$Row[
"TaskId"]] = $Row;
5009 if ($Row[
"Callback"] ==
5010 serialize(array(
"ApplicationFramework",
"PeriodicEventWrapper")))
5012 $WrappedCallback = unserialize($Row[
"Parameters"]);
5013 $Tasks[$Row[
"TaskId"]][
"Callback"] = $WrappedCallback[1];
5014 $Tasks[$Row[
"TaskId"]][
"Parameters"] = NULL;
5018 $Tasks[$Row[
"TaskId"]][
"Callback"] = unserialize($Row[
"Callback"]);
5019 $Tasks[$Row[
"TaskId"]][
"Parameters"] = unserialize($Row[
"Parameters"]);
5028 private function RunNextTask()
5030 # lock tables to prevent same task from being run by multiple sessions 5031 $this->DB->Query(
"LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
5033 # look for task at head of queue 5034 $this->DB->Query(
"SELECT * FROM TaskQueue ORDER BY Priority, TaskId LIMIT 1");
5035 $Task = $this->DB->FetchRow();
5037 # if there was a task available 5040 # move task from queue to running tasks list 5041 $this->DB->Query(
"INSERT INTO RunningTasks " 5042 .
"(TaskId,Callback,Parameters,Priority,Description) " 5043 .
"SELECT * FROM TaskQueue WHERE TaskId = " 5044 .intval($Task[
"TaskId"]));
5045 $this->DB->Query(
"DELETE FROM TaskQueue WHERE TaskId = " 5046 .intval($Task[
"TaskId"]));
5048 # release table locks to again allow other sessions to run tasks 5049 $this->DB->Query(
"UNLOCK TABLES");
5051 # unpack stored task info 5052 $Callback = unserialize($Task[
"Callback"]);
5053 $Parameters = unserialize($Task[
"Parameters"]);
5055 # attempt to load task callback if not already available 5058 # save amount of free memory for later comparison 5059 $BeforeFreeMem = self::GetFreeMemory();
5062 $this->RunningTask = $Task;
5065 call_user_func_array($Callback, $Parameters);
5069 call_user_func($Callback);
5071 unset($this->RunningTask);
5073 # log if task leaked significant memory 5074 if (function_exists(
"gc_collect_cycles")) { gc_collect_cycles(); }
5075 $AfterFreeMem = self::GetFreeMemory();
5076 $LeakThreshold = self::GetPhpMemoryLimit()
5077 * ($this->BackgroundTaskMemLeakLogThreshold / 100);
5078 if (($BeforeFreeMem - $AfterFreeMem) > $LeakThreshold)
5080 $this->
LogError(self::LOGLVL_DEBUG,
"Task " 5081 .self::GetTaskCallbackSynopsis(
5082 $this->
GetTask($Task[
"TaskId"])).
" leaked " 5083 .number_format($BeforeFreeMem - $AfterFreeMem).
" bytes.");
5086 # remove task from running tasks list 5087 $this->DB->Query(
"DELETE FROM RunningTasks" 5088 .
" WHERE TaskId = ".intval($Task[
"TaskId"]));
5090 # prune running tasks list if necessary 5091 $RunningTasksCount = $this->DB->Query(
5092 "SELECT COUNT(*) AS TaskCount FROM RunningTasks",
"TaskCount");
5093 if ($RunningTasksCount > $this->MaxRunningTasksToTrack)
5095 $this->DB->Query(
"DELETE FROM RunningTasks ORDER BY StartedAt" 5096 .
" LIMIT ".($RunningTasksCount - $this->MaxRunningTasksToTrack));
5101 # release table locks to again allow other sessions to run tasks 5102 $this->DB->Query(
"UNLOCK TABLES");
5113 # attempt to remove any memory limits 5115 ini_set(
"memory_limit", -1);
5117 # if there is a background task currently running 5118 if (isset($this->RunningTask))
5120 # add info about current page load 5122 $CrashInfo[
"FreeMemory"] = $FreeMemory;
5123 $CrashInfo[
"REMOTE_ADDR"] = $_SERVER[
"REMOTE_ADDR"];
5124 $CrashInfo[
"REQUEST_URI"] = $_SERVER[
"REQUEST_URI"];
5125 if (isset($_SERVER[
"REQUEST_TIME"]))
5127 $CrashInfo[
"REQUEST_TIME"] = $_SERVER[
"REQUEST_TIME"];
5129 if (isset($_SERVER[
"REMOTE_HOST"]))
5131 $CrashInfo[
"REMOTE_HOST"] = $_SERVER[
"REMOTE_HOST"];
5134 # add info about error that caused crash (if available) 5135 if (function_exists(
"error_get_last"))
5137 $CrashInfo[
"LastError"] = error_get_last();
5140 # add info about current output buffer contents (if available) 5141 if (ob_get_length() !== FALSE)
5143 $CrashInfo[
"OutputBuffer"] = ob_get_contents();
5146 # if backtrace info is available for the crash 5147 $Backtrace = debug_backtrace();
5148 if (count($Backtrace) > 1)
5150 # discard the current context from the backtrace 5151 array_shift($Backtrace);
5153 # add the backtrace to the crash info 5154 $CrashInfo[
"Backtrace"] = $Backtrace;
5156 # else if saved backtrace info is available 5157 elseif (isset($this->SavedContext))
5159 # add the saved backtrace to the crash info 5160 $CrashInfo[
"Backtrace"] = $this->SavedContext;
5163 # save crash info for currently running task 5165 $DB->Query(
"UPDATE RunningTasks SET CrashInfo = '" 5166 .addslashes(serialize($CrashInfo))
5167 .
"' WHERE TaskId = ".intval($this->RunningTask[
"TaskId"]));
5190 private function AddToDirList($DirList, $Dir, $SearchLast, $SkipSlashCheck)
5192 # convert incoming directory to array of directories (if needed) 5193 $Dirs = is_array($Dir) ? $Dir : array($Dir);
5195 # reverse array so directories are searched in specified order 5196 $Dirs = array_reverse($Dirs);
5198 # for each directory 5199 foreach ($Dirs as $Location)
5201 # make sure directory includes trailing slash 5202 if (!$SkipSlashCheck)
5204 $Location = $Location
5205 .((substr($Location, -1) !=
"/") ?
"/" :
"");
5208 # remove directory from list if already present 5209 if (in_array($Location, $DirList))
5211 $DirList = array_diff(
5212 $DirList, array($Location));
5215 # add directory to list of directories 5218 array_push($DirList, $Location);
5222 array_unshift($DirList, $Location);
5226 # return updated directory list to caller 5237 private function ArrayPermutations(
$Items, $Perms = array())
5241 $Result = array($Perms);
5246 for ($Index = count(
$Items) - 1; $Index >= 0; --$Index)
5250 list($Segment) = array_splice($NewItems, $Index, 1);
5251 array_unshift($NewPerms, $Segment);
5252 $Result = array_merge($Result,
5253 $this->ArrayPermutations($NewItems, $NewPerms));
5265 private function OutputModificationCallbackShell($Matches)
5267 # call previously-stored external function 5268 return call_user_func($this->OutputModificationCallbackInfo[
"Callback"],
5270 $this->OutputModificationCallbackInfo[
"Pattern"],
5271 $this->OutputModificationCallbackInfo[
"Page"],
5272 $this->OutputModificationCallbackInfo[
"SearchPattern"]);
5283 private function CheckOutputModification($Original, $Modified, $ErrorInfo)
5285 # if error was reported by regex engine 5286 if (preg_last_error() !== PREG_NO_ERROR)
5289 $this->
LogError(self::LOGLVL_ERROR,
5290 "Error reported by regex engine when modifying output." 5291 .
" (".$ErrorInfo.
")");
5293 # use unmodified version of output 5294 $OutputToUse = $Original;
5296 # else if modification reduced output by more than threshold 5297 elseif ((strlen(trim($Modified)) / strlen(trim($Original)))
5298 < self::OUTPUT_MODIFICATION_THRESHOLD)
5301 $this->
LogError(self::LOGLVL_WARNING,
5302 "Content reduced below acceptable threshold while modifying output." 5303 .
" (".$ErrorInfo.
")");
5305 # use unmodified version of output 5306 $OutputToUse = $Original;
5310 # use modified version of output 5311 $OutputToUse = $Modified;
5314 # return output to use to caller 5315 return $OutputToUse;
5330 private function UpdateSetting(
5331 $FieldName, $NewValue =
DB_NOVALUE, $Persistent = TRUE)
5333 static $LocalSettings;
5338 $LocalSettings[$FieldName] = $this->DB->UpdateValue(
5339 "ApplicationFrameworkSettings",
5340 $FieldName, $NewValue, NULL, $this->Settings);
5344 $LocalSettings[$FieldName] = $NewValue;
5347 elseif (!isset($LocalSettings[$FieldName]))
5349 $LocalSettings[$FieldName] = $this->DB->UpdateValue(
5350 "ApplicationFrameworkSettings",
5351 $FieldName, $NewValue, NULL, $this->Settings);
5353 return $LocalSettings[$FieldName];
5357 private $InterfaceDirList = array(
5358 "local/interface/%ACTIVEUI%/",
5359 "interface/%ACTIVEUI%/",
5360 "local/interface/%DEFAULTUI%/",
5361 "interface/%DEFAULTUI%/",
5367 private $IncludeDirList = array(
5368 "local/interface/%ACTIVEUI%/include/",
5369 "interface/%ACTIVEUI%/include/",
5370 "interface/%ACTIVEUI%/objects/",
5371 "local/interface/%DEFAULTUI%/include/",
5372 "interface/%DEFAULTUI%/include/",
5373 "interface/%DEFAULTUI%/objects/",
5376 private $ImageDirList = array(
5377 "local/interface/%ACTIVEUI%/images/",
5378 "interface/%ACTIVEUI%/images/",
5379 "local/interface/%DEFAULTUI%/images/",
5380 "interface/%DEFAULTUI%/images/",
5383 private $FunctionDirList = array(
5384 "local/interface/%ACTIVEUI%/include/",
5385 "interface/%ACTIVEUI%/include/",
5386 "local/interface/%DEFAULTUI%/include/",
5387 "interface/%DEFAULTUI%/include/",
5392 const NOVALUE =
".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-.";
5395 # ---- Page Caching (Internal Methods) ----------------------------------- 5402 private function CheckForCachedPage($PageName)
5404 # assume no cached page will be found 5407 # if returning a cached page is allowed 5408 if ($this->CacheCurrentPage)
5410 # get fingerprint for requested page 5411 $PageFingerprint = $this->GetPageFingerprint($PageName);
5413 # look for matching page in cache in database 5414 $this->DB->Query(
"SELECT * FROM AF_CachedPages" 5415 .
" WHERE Fingerprint = '".addslashes($PageFingerprint).
"'");
5417 # if matching page found 5418 if ($this->DB->NumRowsSelected())
5420 # if cached page has expired 5421 $Row = $this->DB->FetchRow();
5422 $ExpirationTime = strtotime(
5424 if (strtotime($Row[
"CachedAt"]) < $ExpirationTime)
5426 # clear expired pages from cache 5427 $ExpirationTimestamp = date(
"Y-m-d H:i:s", $ExpirationTime);
5428 $this->DB->Query(
"DELETE CP, CPTI FROM AF_CachedPages CP," 5429 .
" AF_CachedPageTagInts CPTI" 5430 .
" WHERE CP.CachedAt < '".$ExpirationTimestamp.
"'" 5431 .
" AND CPTI.CacheId = CP.CacheId");
5432 $this->DB->Query(
"DELETE FROM AF_CachedPages " 5433 .
" WHERE CachedAt < '".$ExpirationTimestamp.
"'");
5437 # display cached page and exit 5438 $CachedPage = $Row[
"PageContent"];
5443 # return any cached page found to caller 5452 private function UpdatePageCache($PageName, $PageContent)
5454 # if page caching is enabled and current page should be cached 5456 && $this->CacheCurrentPage
5457 && ($PageName !=
"404"))
5459 # if page content looks invalid 5460 if (strlen(trim(strip_tags($PageContent))) == 0)
5463 $LogMsg =
"Page not cached because content was empty." 5464 .
" (PAGE: ".$PageName.
", URL: ".$this->
FullUrl().
")";
5465 $this->
LogError(self::LOGLVL_ERROR, $LogMsg);
5469 # save page to cache 5470 $PageFingerprint = $this->GetPageFingerprint($PageName);
5471 $this->DB->Query(
"INSERT INTO AF_CachedPages" 5472 .
" (Fingerprint, PageContent) VALUES" 5473 .
" ('".$this->DB->EscapeString($PageFingerprint).
"', '" 5474 .$this->DB->EscapeString($PageContent).
"')");
5475 $CacheId = $this->DB->LastInsertId();
5477 # for each page cache tag that was added 5478 foreach ($this->PageCacheTags as $Tag => $Pages)
5480 # if current page is in list for tag 5481 if (in_array(
"CURRENT", $Pages) || in_array($PageName, $Pages))
5484 $TagId = $this->GetPageCacheTagId($Tag);
5486 # mark current page as associated with tag 5487 $this->DB->Query(
"INSERT INTO AF_CachedPageTagInts" 5488 .
" (TagId, CacheId) VALUES " 5489 .
" (".intval($TagId).
", ".intval($CacheId).
")");
5501 private function GetPageCacheTagId($Tag)
5503 # if tag is a non-negative integer 5504 if (is_numeric($Tag) && ($Tag > 0) && (intval($Tag) == $Tag))
5507 $Id = self::PAGECACHETAGIDOFFSET + $Tag;
5511 # look up ID in database 5512 $Id = $this->DB->Query(
"SELECT TagId FROM AF_CachedPageTags" 5513 .
" WHERE Tag = '".addslashes($Tag).
"'",
"TagId");
5515 # if ID was not found 5518 # add tag to database 5519 $this->DB->Query(
"INSERT INTO AF_CachedPageTags" 5520 .
" SET Tag = '".addslashes($Tag).
"'");
5521 $Id = $this->DB->LastInsertId();
5525 # return tag ID to caller 5534 private function GetPageFingerprint($PageName)
5536 # only get the environmental fingerprint once so that it is consistent 5537 # between page construction start and end 5538 static $EnvFingerprint;
5539 if (!isset($EnvFingerprint))
5541 $EnvData = json_encode($_GET).json_encode($_POST);
5543 # if alternate domain support is enabled 5546 # and if we were accessed via an alternate domain 5547 $VHost = $_SERVER[
"SERVER_NAME"];
5548 if (isset($this->AlternateDomainPrefixes[$VHost]))
5550 # then add the alternate domain that was used to our 5556 $EnvFingerprint = md5($EnvData);
5560 # build page fingerprint and return it to caller 5561 return $PageName.
"-".$EnvFingerprint;
UrlFingerprintingEnabled($NewValue=DB_NOVALUE)
Get/set whether URL fingerprinting is enabled.
MaxTasks($NewValue=DB_NOVALUE)
Get/set maximum number of tasks to have running simultaneously.
const LOGLVL_ERROR
ERROR error logging level.
UnhookEvent($EventsOrEventName, $Callback=NULL, $Order=self::ORDER_MIDDLE)
Unhook one or more functions that were previously hooked to be called when the specified event is sig...
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...
DoNotUrlFingerprint($Pattern)
Specify file or file name pattern to exclude from URL fingerprinting.
QueueUniqueTask($Callback, $Parameters=NULL, $Priority=self::PRIORITY_LOW, $Description="")
Add task to queue if not already in queue or currently running.
AddPrefixForAlternateDomain($Domain, $Prefix)
Add an alternate domain for the site which should map to a path tree under the main site URL...
const LOGLVL_FATAL
FATAL error logging level.
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.
IsRunningInBackground()
Determine whether currently running inside a background task.
Abstraction for forum messages and resource comments.
GetQueuedTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
JavascriptMinimizationEnabled($NewValue=DB_NOVALUE)
Get/set whether the application framework will attempt to generate minimized JavaScript.
LogFile($NewValue=NULL)
Get/set log file name.
GetCleanUrl()
Get the clean URL for the current page if one is available.
const PAGECACHETAGIDOFFSET
static PreferHttpHost($NewValue=NULL)
Get/set whether to prefer $_SERVER["HTTP_HOST"] (if available) over $_SERVER["SERVER_NAME"] when dete...
ClearObjectLocationCache()
Clear object (class) file location cache.
GetLock($LockName, $Wait=TRUE)
Get an exclusive ("write") lock on the specified name.
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.
static ActiveUserInterface($UIName=NULL)
Get/set name of current active user interface.
GetTaskQueueSize($Priority=NULL)
Retrieve current number of tasks in queue.
static SortCompare($A, $B)
Perform compare and return value appropriate for sort function callbacks.
static RootUrlOverride($NewValue=self::NOVALUE)
Get/set root URL override.
GetNextLowerBackgroundPriority($Priority=NULL)
Get next lower possible background task priority.
static UrlFingerprintingRewriteSupport()
Determine if rewrite support for URL fingerprinting is available.
GetQueuedTaskCount($Callback=NULL, $Parameters=NULL, $Priority=NULL, $Description=NULL)
Get number of queued tasks that match supplied values.
DeleteTask($TaskId)
Remove task from task queues.
const LOGLVL_DEBUG
DEBUG error logging level.
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.
static minify($js, $options=array())
Takes a string containing javascript and removes unneeded characters in order to shrink the code with...
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.
ReleaseLock($LockName)
Release lock with specified name.
ScssSupportEnabled($NewValue=DB_NOVALUE)
Get/set whether SCSS compilation support is enabled.
PageCacheEnabled($NewValue=DB_NOVALUE)
Enable/disable page caching.
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.
LogHighMemoryUsage($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether logging of high memory usage is enabled.
static ScssRewriteSupport()
Determine if SCSS rewrite support is available.
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.
SlowPageLoadThreshold($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set how long a page load can take before it should be considered "slow" and may be logged...
ClearPageCacheForTag($Tag)
Clear all cached pages associated with specified tag.
DoNotCacheCurrentPage()
Prevent the current page from being cached.
SCSS compiler written in PHP.
GetSecondsBeforeTimeout()
Get remaining available (PHP) execution time.
TaskIsInQueue($Callback, $Parameters=NULL)
Check if task is already in queue or currently running.
GetCurrentBackgroundPriority()
Determine current priority if running in background.
AddFunctionDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for function ("F-") files.
AddPageCacheTag($Tag, $Pages=NULL)
Add caching tag for current page or specified pages.
LoggingLevel($NewValue=DB_NOVALUE)
Get/set logging level.
GetPageCacheInfo()
Get page cache information.
PageCacheExpirationPeriod($NewValue=DB_NOVALUE)
Get/set page cache expiration period in seconds.
const ORDER_MIDDLE
Run hooked function after ORDER_FIRST and before ORDER_LAST events.
const OUTPUT_MODIFICATION_THRESHOLD
Threshold below which page output modifications are considered to have failed.
RegisterEvent($EventsOrEventName, $EventType=NULL)
Register one or more events that may be signaled.
static JsMinRewriteSupport()
Determine if rewrite support for JavaScript minification is available.
DownloadFile($FilePath, $FileName=NULL, $MimeType=NULL)
Send specified file for download by user.
GetPageName()
Get name of page being loaded.
LogSlowPageLoads($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set whether logging of long page load times is enabled.
CleanUrlIsMapped($Path)
Report whether clean URL has already been mapped.
HighMemoryUsageThreshold($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set what percentage of max memory (set via the memory_limit PHP configuration directive) a page l...
SetJumpToPage($Page, $Delay=0, $IsLiteral=FALSE)
Set URL of page to autoload after PHP page file is executed.
const PRIORITY_HIGH
Highest priority.
LoadFunction($Callback)
Attempt to load code for function or method if not currently available.
GetNextHigherBackgroundPriority($Priority=NULL)
Get next higher possible background task priority.
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.
static DefaultUserInterface($UIName=NULL)
Get/set name of current default user interface.
HookEvent($EventsOrEventName, $Callback=NULL, $Order=self::ORDER_MIDDLE)
Hook one or more functions to be called when the specified event is signaled.
ClearPageCache()
Clear all pages from page cache.
IsHookedEvent($EventName)
Check if an event is registered and is hooked to.
static GetFreeMemory()
Get current amount of free memory.
GenerateCompactCss($NewValue=DB_NOVALUE)
Get/set whether generating compact CSS (when compiling SCSS) is enabled.
const FT_JAVASCRIPT
JavaScript file type.
AddImageDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for image files.
const FT_CSS
CSS file type.
HtmlCharset($NewSetting=NULL)
Get/set HTTP character encoding value.
const ORDER_FIRST
Run hooked function first (i.e.
const SQL_DATE_FORMAT
Format to feed to date() to get SQL-compatible date/time string.
IncludeUIFile($FileNames, $AdditionalAttributes=NULL)
Search UI directories for specified JavaScript or CSS file and print HTML tag to load file...
GetUncleanUrlForPath($Path)
Get the unclean URL for mapped for a path.
const FT_IMAGE
Image (GIF/JPG/PNG) file type.
UseMinimizedJavascript($NewValue=DB_NOVALUE)
Get/set whether minimized JavaScript will be searched for and used if found.
TemplateLocationCacheExpirationInterval($NewInterval=DB_NOVALUE)
Get/set UI template location cache expiration period in minutes.
ClearTemplateLocationCache()
Clear template location cache.
static GetFileType($FileName)
Determine type of specified file based on the file name.
GetUncleanUrl()
Get the unclean URL for the current page.
DoNotMinimizeFile($File)
Specify file(s) to not attempt to minimize.
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.
GetTask($TaskId)
Retrieve task info from queue (either running or queued tasks).
TaskExecutionEnabled($NewValue=DB_NOVALUE)
Get/set whether automatic task execution is enabled.
MaxExecutionTime($NewValue=DB_NOVALUE, $Persistent=FALSE)
Get/set maximum PHP execution time.
static GetPhpMemoryLimit()
Get PHP memory limit in bytes.
LoadPage($PageName)
Load page PHP and HTML/TPL files.
GetUserInterfacePaths($FilterExp=NULL)
Get list of available user interfaces and the relative paths to the base directory for each interface...
AddEnvInclude($FileName)
Add file to be included to set up environment.
GetUserInterfaces($FilterExp=NULL)
Get list of available user interfaces and their labels.
ReQueueOrphanedTask($TaskId, $NewPriority=NULL)
Move orphaned task back into queue.
GetLogEntries($Limit=0)
Get log entries, in reverse chronological order.
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.
static GetTaskCallbackSynopsis($TaskInfo)
Get printable synopsis for task callback.
PUIFile($FileName)
Search UI directories for specified interface (image, CSS, JavaScript etc) file and print name of cor...
const LOGFILE_MAX_LINE_LENGTH
Maximum length for a line in the log file.
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.
const PRIORITY_BACKGROUND
Lowest priority.
LogMessage($Level, $Msg)
Write status message to log.
const FT_OTHER
File type other than CSS, image, or JavaScript.