CWIS Developer Documentation
ApplicationFramework.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: ApplicationFramework.php
4 #
5 # Part of the ScoutLib application support library
6 # Copyright 2009-2013 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu
8 #
9 
15 
16  # ---- PUBLIC INTERFACE --------------------------------------------------
17  /*@(*/
19 
24  function __construct()
25  {
26  # save execution start time
27  $this->ExecutionStartTime = microtime(TRUE);
28 
29  # begin/restore PHP session
30  $SessionDomain = isset($_SERVER["SERVER_NAME"]) ? $_SERVER["SERVER_NAME"]
31  : isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"]
32  : php_uname("n");
33  if (is_writable(session_save_path()))
34  {
35  $SessionStorage = session_save_path()
36  ."/".self::$AppName."_".md5($SessionDomain.dirname(__FILE__));
37  if (!is_dir($SessionStorage)) { mkdir($SessionStorage, 0700 ); }
38  if (is_writable($SessionStorage)) { session_save_path($SessionStorage); }
39  }
40  ini_set("session.gc_maxlifetime", self::$SessionLifetime);
41  session_set_cookie_params(
42  self::$SessionLifetime, "/", $SessionDomain);
43  session_start();
44 
45  # set up object file autoloader
46  $this->SetUpObjectAutoloading();
47 
48  # set up function to output any buffered text in case of crash
49  register_shutdown_function(array($this, "OnCrash"));
50 
51  # set up our internal environment
52  $this->DB = new Database();
53 
54  # set up our exception handler
55  set_exception_handler(array($this, "GlobalExceptionHandler"));
56 
57  # perform any work needed to undo PHP magic quotes
58  $this->UndoMagicQuotes();
59 
60  # load our settings from database
61  $this->LoadSettings();
62 
63  # set PHP maximum execution time
64  $this->MaxExecutionTime($this->Settings["MaxExecTime"]);
65 
66  # register events we handle internally
67  $this->RegisterEvent($this->PeriodicEvents);
68  $this->RegisterEvent($this->UIEvents);
69  }
76  function __destruct()
77  {
78  # if template location cache is flagged to be saved
79  if ($this->SaveTemplateLocationCache)
80  {
81  # write template location cache out and update cache expiration
82  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
83  ." SET TemplateLocationCache = '"
84  .addslashes(serialize(
85  $this->Settings["TemplateLocationCache"]))."',"
86  ." TemplateLocationCacheExpiration = "
87  ." NOW() + INTERVAL "
88  .$this->Settings["TemplateLocationCacheInterval"]
89  ." MINUTE");
90  }
91 
92  # if object location cache is flagged to be saved
93  if (self::$SaveObjectLocationCache)
94  {
95  # write object location cache out and update cache expiration
96  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
97  ." SET ObjectLocationCache = '"
98  .addslashes(serialize(
99  self::$ObjectLocationCache))."',"
100  ." ObjectLocationCacheExpiration = "
101  ." NOW() + INTERVAL "
102  .self::$ObjectLocationCacheInterval
103  ." MINUTE");
104  }
105  }
112  function GlobalExceptionHandler($Exception)
113  {
114  # display exception info
115  $Location = $Exception->getFile()."[".$Exception->getLine()."]";
116  ?><table width="100%" cellpadding="5"
117  style="border: 2px solid #666666; background: #CCCCCC;
118  font-family: Courier New, Courier, monospace;
119  margin-top: 10px;"><tr><td>
120  <div style="color: #666666;">
121  <span style="font-size: 150%;">
122  <b>Uncaught Exception</b></span><br />
123  <b>Message:</b> <i><?PHP print $Exception->getMessage(); ?></i><br />
124  <b>Location:</b> <i><?PHP print $Location; ?></i><br />
125  <b>Trace:</b>
126  <blockquote><pre><?PHP print $Exception->getTraceAsString();
127  ?></pre></blockquote>
128  </div>
129  </td></tr></table><?PHP
130 
131  # log exception if possible
132  $LogMsg = "Uncaught exception (".$Exception->getMessage().").";
133  $this->LogError(self::LOGLVL_ERROR, $LogMsg);
134  }
148  static function AddObjectDirectory(
149  $Dir, $Prefix = "", $ClassPattern = NULL, $ClassReplacement = NULL)
150  {
151  # make sure directory has trailing slash
152  $Dir = $Dir.((substr($Dir, -1) != "/") ? "/" : "");
153 
154  # add directory to directory list
155  self::$ObjectDirectories = array_merge(
156  array($Dir => array(
157  "Prefix" => $Prefix,
158  "ClassPattern" => $ClassPattern,
159  "ClassReplacement" => $ClassReplacement,
160  )),
161  self::$ObjectDirectories);
162  }
163 
183  function AddImageDirectories($Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
184  {
185  # add directories to existing image directory list
186  $this->ImageDirList = $this->AddToDirList(
187  $this->ImageDirList, $Dir, $SearchLast, $SkipSlashCheck);
188  }
189 
210  function AddIncludeDirectories($Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
211  {
212  # add directories to existing image directory list
213  $this->IncludeDirList = $this->AddToDirList(
214  $this->IncludeDirList, $Dir, $SearchLast, $SkipSlashCheck);
215  }
216 
236  function AddInterfaceDirectories($Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
237  {
238  # add directories to existing image directory list
239  $this->InterfaceDirList = $this->AddToDirList(
240  $this->InterfaceDirList, $Dir, $SearchLast, $SkipSlashCheck);
241  }
242 
262  function AddFunctionDirectories($Dir, $SearchLast = FALSE, $SkipSlashCheck = FALSE)
263  {
264  # add directories to existing image directory list
265  $this->FunctionDirList = $this->AddToDirList(
266  $this->FunctionDirList, $Dir, $SearchLast, $SkipSlashCheck);
267  }
268 
274  function SetBrowserDetectionFunc($DetectionFunc)
275  {
276  $this->BrowserDetectFunc = $DetectionFunc;
277  }
278 
285  function AddUnbufferedCallback($Callback, $Parameters=array())
286  {
287  if (is_callable($Callback))
288  {
289  $this->UnbufferedCallbacks[] = array($Callback, $Parameters);
290  }
291  }
292 
300  {
301  return $this->UpdateSetting("TemplateLocationCacheInterval", $NewInterval);
302  }
303 
311  {
312  return $this->UpdateSetting("ObjectLocationCacheInterval", $NewInterval);
313  }
314 
328  $BacktraceOptions = 0, $BacktraceLimit = 0)
329  {
330  if (version_compare(PHP_VERSION, "5.4.0", ">="))
331  {
332  $this->SavedContext = debug_backtrace(
333  $BacktraceOptions, $BacktraceLimit);
334  }
335  else
336  {
337  $this->SavedContext = debug_backtrace($BacktraceOptions);
338  }
339  array_shift($this->SavedContext);
340  }
341 
346  function LoadPage($PageName)
347  {
348  # perform any clean URL rewriting
349  $PageName = $this->RewriteCleanUrls($PageName);
350 
351  # sanitize incoming page name and save local copy
352  $PageName = preg_replace("/[^a-zA-Z0-9_.-]/", "", $PageName);
353  $this->PageName = $PageName;
354 
355  # buffer any output from includes or PHP file
356  ob_start();
357 
358  # include any files needed to set up execution environment
359  foreach ($this->EnvIncludes as $IncludeFile)
360  {
361  include($IncludeFile);
362  }
363 
364  # signal page load
365  $this->SignalEvent("EVENT_PAGE_LOAD", array("PageName" => $PageName));
366 
367  # signal PHP file load
368  $SignalResult = $this->SignalEvent("EVENT_PHP_FILE_LOAD", array(
369  "PageName" => $PageName));
370 
371  # if signal handler returned new page name value
372  $NewPageName = $PageName;
373  if (($SignalResult["PageName"] != $PageName)
374  && strlen($SignalResult["PageName"]))
375  {
376  # if new page name value is page file
377  if (file_exists($SignalResult["PageName"]))
378  {
379  # use new value for PHP file name
380  $PageFile = $SignalResult["PageName"];
381  }
382  else
383  {
384  # use new value for page name
385  $NewPageName = $SignalResult["PageName"];
386  }
387 
388  # update local copy of page name
389  $this->PageName = $NewPageName;
390  }
391 
392  # if we do not already have a PHP file
393  if (!isset($PageFile))
394  {
395  # look for PHP file for page
396  $OurPageFile = "pages/".$NewPageName.".php";
397  $LocalPageFile = "local/pages/".$NewPageName.".php";
398  $PageFile = file_exists($LocalPageFile) ? $LocalPageFile
399  : (file_exists($OurPageFile) ? $OurPageFile
400  : "pages/".$this->DefaultPage.".php");
401  }
402 
403  # load PHP file
404  include($PageFile);
405 
406  # save buffered output to be displayed later after HTML file loads
407  $PageOutput = ob_get_contents();
408  ob_end_clean();
409 
410  # signal PHP file load is complete
411  ob_start();
412  $Context["Variables"] = get_defined_vars();
413  $this->SignalEvent("EVENT_PHP_FILE_LOAD_COMPLETE",
414  array("PageName" => $PageName, "Context" => $Context));
415  $PageCompleteOutput = ob_get_contents();
416  ob_end_clean();
417 
418  # set up for possible TSR (Terminate and Stay Resident :))
419  $ShouldTSR = $this->PrepForTSR();
420 
421  # if PHP file indicated we should autorefresh to somewhere else
422  if ($this->JumpToPage)
423  {
424  if (!strlen(trim($PageOutput)))
425  {
426  ?><html>
427  <head>
428  <meta http-equiv="refresh" content="0; URL=<?PHP
429  print($this->JumpToPage); ?>">
430  </head>
431  <body bgcolor="white">
432  </body>
433  </html><?PHP
434  }
435  }
436  # else if HTML loading is not suppressed
437  elseif (!$this->SuppressHTML)
438  {
439  # set content-type to get rid of diacritic errors
440  header("Content-Type: text/html; charset="
441  .$this->HtmlCharset, TRUE);
442 
443  # load common HTML file (defines common functions) if available
444  $CommonHtmlFile = $this->FindFile($this->IncludeDirList,
445  "Common", array("tpl", "html"));
446  if ($CommonHtmlFile) { include($CommonHtmlFile); }
447 
448  # load UI functions
449  $this->LoadUIFunctions();
450 
451  # begin buffering content
452  ob_start();
453 
454  # signal HTML file load
455  $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD", array(
456  "PageName" => $PageName));
457 
458  # if signal handler returned new page name value
459  $NewPageName = $PageName;
460  $PageContentFile = NULL;
461  if (($SignalResult["PageName"] != $PageName)
462  && strlen($SignalResult["PageName"]))
463  {
464  # if new page name value is HTML file
465  if (file_exists($SignalResult["PageName"]))
466  {
467  # use new value for HTML file name
468  $PageContentFile = $SignalResult["PageName"];
469  }
470  else
471  {
472  # use new value for page name
473  $NewPageName = $SignalResult["PageName"];
474  }
475  }
476 
477  # load page content HTML file if available
478  if ($PageContentFile === NULL)
479  {
480  $PageContentFile = $this->FindFile(
481  $this->InterfaceDirList, $NewPageName,
482  array("tpl", "html"));
483  }
484  if ($PageContentFile)
485  {
486  include($PageContentFile);
487  }
488  else
489  {
490  print "<h2>ERROR: No HTML/TPL template found"
491  ." for this page.</h2>";
492  }
493 
494  # signal HTML file load complete
495  $SignalResult = $this->SignalEvent("EVENT_HTML_FILE_LOAD_COMPLETE");
496 
497  # stop buffering and save output
498  $PageContentOutput = ob_get_contents();
499  ob_end_clean();
500 
501  # load page start HTML file if available
502  ob_start();
503  $PageStartFile = $this->FindFile($this->IncludeDirList, "Start",
504  array("tpl", "html"), array("StdPage", "StandardPage"));
505  if ($PageStartFile) { include($PageStartFile); }
506  $PageStartOutput = ob_get_contents();
507  ob_end_clean();
508 
509  # load page end HTML file if available
510  ob_start();
511  $PageEndFile = $this->FindFile($this->IncludeDirList, "End",
512  array("tpl", "html"), array("StdPage", "StandardPage"));
513  if ($PageEndFile) { include($PageEndFile); }
514  $PageEndOutput = ob_get_contents();
515  ob_end_clean();
516 
517  # get list of any required files not loaded
518  $RequiredFiles = $this->GetRequiredFilesNotYetLoaded($PageContentFile);
519 
520  # if a browser detection function has been made available
521  if (is_callable($this->BrowserDetectFunc))
522  {
523  # call function to get browser list
524  $Browsers = call_user_func($this->BrowserDetectFunc);
525 
526  # for each required file
527  $NewRequiredFiles = array();
528  foreach ($RequiredFiles as $File)
529  {
530  # if file name includes browser keyword
531  if (preg_match("/%BROWSER%/", $File))
532  {
533  # for each browser
534  foreach ($Browsers as $Browser)
535  {
536  # substitute in browser name and add to new file list
537  $NewRequiredFiles[] = preg_replace(
538  "/%BROWSER%/", $Browser, $File);
539  }
540  }
541  else
542  {
543  # add to new file list
544  $NewRequiredFiles[] = $File;
545  }
546  }
547  $RequiredFiles = $NewRequiredFiles;
548  }
549 
550  # for each required file
551  foreach ($RequiredFiles as $File)
552  {
553  # locate specific file to use
554  $FilePath = $this->GUIFile($File);
555 
556  # if file was found
557  if ($FilePath)
558  {
559  # determine file type
560  $NamePieces = explode(".", $File);
561  $FileSuffix = strtolower(array_pop($NamePieces));
562 
563  # add file to HTML output based on file type
564  $FilePath = htmlspecialchars($FilePath);
565  switch ($FileSuffix)
566  {
567  case "js":
568  $Tag = '<script type="text/javascript" src="'
569  .$FilePath.'"></script>';
570  $PageEndOutput = preg_replace(
571  "#</body>#i", $Tag."\n</body>", $PageEndOutput, 1);
572  break;
573 
574  case "css":
575  $Tag = '<link rel="stylesheet" type="text/css"'
576  .' media="all" href="'.$FilePath.'">';
577  $PageStartOutput = preg_replace(
578  "#</head>#i", $Tag."\n</head>", $PageStartOutput, 1);
579  break;
580  }
581  }
582  }
583 
584  # assemble full page
585  $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
586 
587  # perform any regular expression replacements in output
588  $FullPageOutput = preg_replace($this->OutputModificationPatterns,
589  $this->OutputModificationReplacements, $FullPageOutput);
590 
591  # perform any callback replacements in output
592  foreach ($this->OutputModificationCallbacks as $Info)
593  {
594  $this->OutputModificationCallbackInfo = $Info;
595  $FullPageOutput = preg_replace_callback($Info["SearchPattern"],
596  array($this, "OutputModificationCallbackShell"),
597  $FullPageOutput);
598  }
599 
600  # if relative paths may not work because we were invoked via clean URL
601  if ($this->CleanUrlRewritePerformed || self::WasUrlRewritten())
602  {
603  # if using the <base> tag is okay
604  $BaseUrl = $this->BaseUrl();
605  if ($this->UseBaseTag)
606  {
607  # add <base> tag to header
608  $PageStartOutput = preg_replace("%<head>%",
609  "<head><base href=\"".$BaseUrl."\" />",
610  $PageStartOutput);
611 
612  # re-assemble full page with new header
613  $FullPageOutput = $PageStartOutput.$PageContentOutput.$PageEndOutput;
614 
615  # the absolute URL to the current page
616  $FullUrl = $BaseUrl . $this->GetPageLocation();
617 
618  # make HREF attribute values with just a fragment ID
619  # absolute since they don't work with the <base> tag because
620  # they are relative to the current page/URL, not the site
621  # root
622  $FullPageOutput = preg_replace(
623  array("%href=\"(#[^:\" ]+)\"%i", "%href='(#[^:' ]+)'%i"),
624  array("href=\"".$FullUrl."$1\"", "href='".$FullUrl."$1'"),
625  $FullPageOutput);
626  }
627  else
628  {
629  # try to fix any relative paths throughout code
630  $FullPageOutput = preg_replace(array(
631  "%src=\"/?([^?*:;{}\\\\\" ]+)\.(js|css|gif|png|jpg)\"%i",
632  "%src='/?([^?*:;{}\\\\' ]+)\.(js|css|gif|png|jpg)'%i",
633  # don't rewrite HREF attributes that are just
634  # fragment IDs because they are relative to the
635  # current page/URL, not the site root
636  "%href=\"/?([^#][^:\" ]*)\"%i",
637  "%href='/?([^#][^:' ]*)'%i",
638  "%action=\"/?([^#][^:\" ]*)\"%i",
639  "%action='/?([^#][^:' ]*)'%i",
640  "%@import\s+url\(\"/?([^:\" ]+)\"\s*\)%i",
641  "%@import\s+url\('/?([^:\" ]+)'\s*\)%i",
642  "%@import\s+\"/?([^:\" ]+)\"\s*%i",
643  "%@import\s+'/?([^:\" ]+)'\s*%i",
644  ),
645  array(
646  "src=\"".$BaseUrl."$1.$2\"",
647  "src=\"".$BaseUrl."$1.$2\"",
648  "href=\"".$BaseUrl."$1\"",
649  "href=\"".$BaseUrl."$1\"",
650  "action=\"".$BaseUrl."$1\"",
651  "action=\"".$BaseUrl."$1\"",
652  "@import url(\"".$BaseUrl."$1\")",
653  "@import url('".$BaseUrl."$1')",
654  "@import \"".$BaseUrl."$1\"",
655  "@import '".$BaseUrl."$1'",
656  ),
657  $FullPageOutput);
658  }
659  }
660 
661  # provide the opportunity to modify full page output
662  $SignalResult = $this->SignalEvent("EVENT_PAGE_OUTPUT_FILTER", array(
663  "PageOutput" => $FullPageOutput));
664  if (isset($SignalResult["PageOutput"])
665  && strlen($SignalResult["PageOutput"]))
666  {
667  $FullPageOutput = $SignalResult["PageOutput"];
668  }
669 
670  # write out full page
671  print $FullPageOutput;
672  }
673 
674  # run any post-processing routines
675  foreach ($this->PostProcessingFuncs as $Func)
676  {
677  call_user_func_array($Func["FunctionName"], $Func["Arguments"]);
678  }
679 
680  # write out any output buffered from page code execution
681  if (strlen($PageOutput))
682  {
683  if (!$this->SuppressHTML)
684  {
685  ?><table width="100%" cellpadding="5"
686  style="border: 2px solid #666666; background: #CCCCCC;
687  font-family: Courier New, Courier, monospace;
688  margin-top: 10px;"><tr><td><?PHP
689  }
690  if ($this->JumpToPage)
691  {
692  ?><div style="color: #666666;"><span style="font-size: 150%;">
693  <b>Page Jump Aborted</b></span>
694  (because of error or other unexpected output)<br />
695  <b>Jump Target:</b>
696  <i><?PHP print($this->JumpToPage); ?></i></div><?PHP
697  }
698  print $PageOutput;
699  if (!$this->SuppressHTML)
700  {
701  ?></td></tr></table><?PHP
702  }
703  }
704 
705  # write out any output buffered from the page code execution complete signal
706  if (!$this->JumpToPage && !$this->SuppressHTML && strlen($PageCompleteOutput))
707  {
708  print $PageCompleteOutput;
709  }
710 
711  # execute callbacks that should not have their output buffered
712  foreach ($this->UnbufferedCallbacks as $Callback)
713  {
714  call_user_func_array($Callback[0], $Callback[1]);
715  }
716 
717  # log high memory usage
718  if (function_exists("memory_get_peak_usage"))
719  {
720  $MemoryThreshold = ($this->HighMemoryUsageThreshold()
721  * $this->GetPhpMemoryLimit()) / 100;
722  if ($this->LogHighMemoryUsage()
723  && (memory_get_peak_usage() >= $MemoryThreshold))
724  {
725  $HighMemUsageMsg = "High peak memory usage ("
726  .intval(memory_get_peak_usage()).") for "
727  .$this->FullUrl()." from "
728  .$_SERVER["REMOTE_ADDR"];
729  $this->LogMessage(self::LOGLVL_INFO, $HighMemUsageMsg);
730  }
731  }
732 
733  # log slow page loads
734  if ($this->LogSlowPageLoads()
735  && ($this->GetElapsedExecutionTime()
736  >= ($this->SlowPageLoadThreshold())))
737  {
738  $SlowPageLoadMsg = "Slow page load ("
739  .intval($this->GetElapsedExecutionTime())."s) for "
740  .$this->FullUrl()." from "
741  .$_SERVER["REMOTE_ADDR"];
742  $this->LogMessage(self::LOGLVL_INFO, $SlowPageLoadMsg);
743  }
744 
745  # terminate and stay resident (TSR!) if indicated and HTML has been output
746  # (only TSR if HTML has been output because otherwise browsers will misbehave)
747  if ($ShouldTSR) { $this->LaunchTSR(); }
748  }
749 
755  function GetPageName()
756  {
757  return $this->PageName;
758  }
759 
765  function GetPageLocation()
766  {
767  # retrieve current URL
768  $Url = $this->GetScriptUrl();
769 
770  # remove the base path if present
771  $BasePath = $this->Settings["BasePath"];
772  if (stripos($Url, $BasePath) === 0)
773  {
774  $Url = substr($Url, strlen($BasePath));
775  }
776 
777  return $Url;
778  }
779 
785  function GetPageUrl()
786  {
787  return self::BaseUrl() . $this->GetPageLocation();
788  }
789 
798  function SetJumpToPage($Page, $IsLiteral = FALSE)
799  {
800  if (!is_null($Page)
801  && (!$IsLiteral)
802  && (strpos($Page, "?") === FALSE)
803  && ((strpos($Page, "=") !== FALSE)
804  || ((stripos($Page, ".php") === FALSE)
805  && (stripos($Page, ".htm") === FALSE)
806  && (strpos($Page, "/") === FALSE)))
807  && (stripos($Page, "http://") !== 0)
808  && (stripos($Page, "https://") !== 0))
809  {
810  $this->JumpToPage = self::BaseUrl() . "index.php?P=".$Page;
811  }
812  else
813  {
814  $this->JumpToPage = $Page;
815  }
816  }
817 
822  function JumpToPageIsSet()
823  {
824  return ($this->JumpToPage === NULL) ? FALSE : TRUE;
825  }
826 
836  function HtmlCharset($NewSetting = NULL)
837  {
838  if ($NewSetting !== NULL) { $this->HtmlCharset = $NewSetting; }
839  return $this->HtmlCharset;
840  }
841 
848  public function UseMinimizedJavascript($NewSetting = NULL)
849  {
850  if ($NewSetting !== NULL) { $this->UseMinimizedJavascript = $NewSetting; }
851  return $this->UseMinimizedJavascript;
852  }
853 
864  function UseBaseTag($NewValue = NULL)
865  {
866  if ($NewValue !== NULL) { $this->UseBaseTag = $NewValue ? TRUE : FALSE; }
867  return $this->UseBaseTag;
868  }
869 
876  function SuppressHTMLOutput($NewSetting = TRUE)
877  {
878  $this->SuppressHTML = $NewSetting;
879  }
880 
887  function ActiveUserInterface($UIName = NULL)
888  {
889  if ($UIName !== NULL)
890  {
891  $this->ActiveUI = preg_replace("/^SPTUI--/", "", $UIName);
892  }
893  return $this->ActiveUI;
894  }
895 
901  function GetUserInterfaces()
902  {
903  # possible UI directories
904  $InterfaceDirs = array(
905  "interface",
906  "local/interface");
907 
908  # start out with an empty list
909  $Interfaces = array();
910 
911  # for each possible UI directory
912  foreach ($InterfaceDirs as $InterfaceDir)
913  {
914  $Dir = dir($InterfaceDir);
915 
916  # for each file in current directory
917  while (($DirEntry = $Dir->read()) !== FALSE)
918  {
919  $InterfacePath = $InterfaceDir."/".$DirEntry;
920 
921  # skip anything that doesn't have a name in the required format
922  if (!preg_match('/^[a-zA-Z0-9]+$/', $DirEntry))
923  {
924  continue;
925  }
926 
927  # skip anything that isn't a directory
928  if (!is_dir($InterfacePath))
929  {
930  continue;
931  }
932 
933  # read the UI name (if available)
934  $UIName = @file_get_contents($InterfacePath."/NAME");
935 
936  # use the directory name if the UI name isn't available
937  if ($UIName === FALSE || !strlen($UIName))
938  {
939  $UIName = $DirEntry;
940  }
941 
942  $Interfaces[$InterfacePath] = $UIName;
943  }
944 
945  $Dir->close();
946  }
947 
948  # return list to caller
949  return $Interfaces;
950  }
951 
967  function AddPostProcessingCall($FunctionName,
968  &$Arg1 = self::NOVALUE, &$Arg2 = self::NOVALUE, &$Arg3 = self::NOVALUE,
969  &$Arg4 = self::NOVALUE, &$Arg5 = self::NOVALUE, &$Arg6 = self::NOVALUE,
970  &$Arg7 = self::NOVALUE, &$Arg8 = self::NOVALUE, &$Arg9 = self::NOVALUE)
971  {
972  $FuncIndex = count($this->PostProcessingFuncs);
973  $this->PostProcessingFuncs[$FuncIndex]["FunctionName"] = $FunctionName;
974  $this->PostProcessingFuncs[$FuncIndex]["Arguments"] = array();
975  $Index = 1;
976  while (isset(${"Arg".$Index}) && (${"Arg".$Index} !== self::NOVALUE))
977  {
978  $this->PostProcessingFuncs[$FuncIndex]["Arguments"][$Index]
979  =& ${"Arg".$Index};
980  $Index++;
981  }
982  }
983 
989  function AddEnvInclude($FileName)
990  {
991  $this->EnvIncludes[] = $FileName;
992  }
993 
1000  function GUIFile($FileName)
1001  {
1002  # determine if the file is an image or JavaScript file
1003  $FileIsImage = preg_match("/\.(gif|jpg|png)$/", $FileName);
1004  $FileIsJavascript = preg_match("/\.js$/", $FileName);
1005 
1006  # determine which location to search based on file suffix
1007  $DirList = $FileIsImage ? $this->ImageDirList : $this->IncludeDirList;
1008 
1009  # if directed to get a minimized JavaScript file
1010  if ($FileIsJavascript && $this->UseMinimizedJavascript)
1011  {
1012  # first try to find the minimized JavaScript file
1013  $MinimizedFileName = substr_replace($FileName, ".min", -3, 0);
1014  $FoundFileName = $this->FindFile($DirList, $MinimizedFileName);
1015 
1016  # search for the regular file if a minimized file wasn't found
1017  if (is_null($FoundFileName))
1018  {
1019  $FoundFileName = $this->FindFile($DirList, $FileName);
1020  }
1021  }
1022 
1023  # otherwise just search for the file
1024  else
1025  {
1026  $FoundFileName = $this->FindFile($DirList, $FileName);
1027  }
1028 
1029  # add non-image files to list of found files (used for required files loading)
1030  if (!$FileIsImage) { $this->FoundUIFiles[] = basename($FoundFileName); }
1031 
1032  # return file name to caller
1033  return $FoundFileName;
1034  }
1035 
1045  function PUIFile($FileName)
1046  {
1047  $FullFileName = $this->GUIFile($FileName);
1048  if ($FullFileName) { print($FullFileName); }
1049  }
1050 
1058  function RequireUIFile($FileName)
1059  {
1060  $this->AdditionalRequiredUIFiles[] = $FileName;
1061  }
1062 
1071  function LoadFunction($Callback)
1072  {
1073  # if specified function is not currently available
1074  if (!is_callable($Callback))
1075  {
1076  # if function info looks legal
1077  if (is_string($Callback) && strlen($Callback))
1078  {
1079  # start with function directory list
1080  $Locations = $this->FunctionDirList;
1081 
1082  # add object directories to list
1083  $Locations = array_merge(
1084  $Locations, array_keys(self::$ObjectDirectories));
1085 
1086  # look for function file
1087  $FunctionFileName = $this->FindFile($Locations, "F-".$Callback,
1088  array("php", "html"));
1089 
1090  # if function file was found
1091  if ($FunctionFileName)
1092  {
1093  # load function file
1094  include_once($FunctionFileName);
1095  }
1096  else
1097  {
1098  # log error indicating function load failed
1099  $this->LogError(self::LOGLVL_ERROR, "Unable to load function"
1100  ." for callback \"".$Callback."\".");
1101  }
1102  }
1103  else
1104  {
1105  # log error indicating specified function info was bad
1106  $this->LogError(self::LOGLVL_ERROR, "Unloadable callback value"
1107  ." (".$Callback.")"
1108  ." passed to AF::LoadFunction().");
1109  }
1110  }
1111 
1112  # report to caller whether function load succeeded
1113  return is_callable($Callback);
1114  }
1115 
1121  {
1122  return microtime(TRUE) - $this->ExecutionStartTime;
1123  }
1124 
1130  {
1131  return ini_get("max_execution_time") - $this->GetElapsedExecutionTime();
1132  }
1133 
1134  /*@)*/ /* Application Framework */
1135 
1136 
1137  # ---- Logging -----------------------------------------------------------
1138  /*@(*/
1140 
1151  function LogSlowPageLoads($NewValue = DB_NOVALUE)
1152  {
1153  return $this->UpdateSetting("LogSlowPageLoads", $NewValue);
1154  }
1155 
1163  function SlowPageLoadThreshold($NewValue = DB_NOVALUE)
1164  {
1165  return $this->UpdateSetting("SlowPageLoadThreshold", $NewValue);
1166  }
1167 
1178  function LogHighMemoryUsage($NewValue = DB_NOVALUE)
1179  {
1180  return $this->UpdateSetting("LogHighMemoryUsage", $NewValue);
1181  }
1182 
1191  function HighMemoryUsageThreshold($NewValue = DB_NOVALUE)
1192  {
1193  return $this->UpdateSetting("HighMemoryUsageThreshold", $NewValue);
1194  }
1195 
1209  function LogError($Level, $Msg)
1210  {
1211  # if error level is at or below current logging level
1212  if ($this->Settings["LoggingLevel"] >= $Level)
1213  {
1214  # attempt to log error message
1215  $Result = $this->LogMessage($Level, $Msg);
1216 
1217  # if logging attempt failed and level indicated significant error
1218  if (($Result === FALSE) && ($Level <= self::LOGLVL_ERROR))
1219  {
1220  # throw exception about inability to log error
1221  static $AlreadyThrewException = FALSE;
1222  if (!$AlreadyThrewException)
1223  {
1224  $AlreadyThrewException = TRUE;
1225  throw new Exception("Unable to log error (".$Level.": ".$Msg.").");
1226  }
1227  }
1228 
1229  # report to caller whether message was logged
1230  return $Result;
1231  }
1232  else
1233  {
1234  # report to caller that message was not logged
1235  return FALSE;
1236  }
1237  }
1238 
1250  function LogMessage($Level, $Msg)
1251  {
1252  # if message level is at or below current logging level
1253  if ($this->Settings["LoggingLevel"] >= $Level)
1254  {
1255  # attempt to open log file
1256  $FHndl = @fopen($this->LogFileName, "a");
1257 
1258  # if log file could not be open
1259  if ($FHndl === FALSE)
1260  {
1261  # report to caller that message was not logged
1262  return FALSE;
1263  }
1264  else
1265  {
1266  # format log entry
1267  $ErrorAbbrevs = array(
1268  self::LOGLVL_FATAL => "FTL",
1269  self::LOGLVL_ERROR => "ERR",
1270  self::LOGLVL_WARNING => "WRN",
1271  self::LOGLVL_INFO => "INF",
1272  self::LOGLVL_DEBUG => "DBG",
1273  self::LOGLVL_TRACE => "TRC",
1274  );
1275  $LogEntry = date("Y-m-d H:i:s")
1276  ." ".($this->RunningInBackground ? "B" : "F")
1277  ." ".$ErrorAbbrevs[$Level]
1278  ." ".$Msg;
1279 
1280  # write entry to log
1281  $Success = fwrite($FHndl, $LogEntry."\n");
1282 
1283  # close log file
1284  fclose($FHndl);
1285 
1286  # report to caller whether message was logged
1287  return ($Success === FALSE) ? FALSE : TRUE;
1288  }
1289  }
1290  else
1291  {
1292  # report to caller that message was not logged
1293  return FALSE;
1294  }
1295  }
1296 
1318  function LoggingLevel($NewValue = DB_NOVALUE)
1319  {
1320  # constrain new level (if supplied) to within legal bounds
1321  if ($NewValue !== DB_NOVALUE)
1322  {
1323  $NewValue = max(min($NewValue, 6), 1);
1324  }
1325 
1326  # set new logging level (if supplied) and return current level to caller
1327  return $this->UpdateSetting("LoggingLevel", $NewValue);
1328  }
1329 
1336  function LogFile($NewValue = NULL)
1337  {
1338  if ($NewValue !== NULL) { $this->LogFileName = $NewValue; }
1339  return $this->LogFileName;
1340  }
1341 
1346  const LOGLVL_TRACE = 6;
1351  const LOGLVL_DEBUG = 5;
1357  const LOGLVL_INFO = 4;
1362  const LOGLVL_WARNING = 3;
1368  const LOGLVL_ERROR = 2;
1373  const LOGLVL_FATAL = 1;
1374 
1375  /*@)*/ /* Logging */
1376 
1377 
1378  # ---- Event Handling ----------------------------------------------------
1379  /*@(*/
1381 
1391  const EVENTTYPE_CHAIN = 2;
1397  const EVENTTYPE_FIRST = 3;
1405  const EVENTTYPE_NAMED = 4;
1406 
1408  const ORDER_FIRST = 1;
1410  const ORDER_MIDDLE = 2;
1412  const ORDER_LAST = 3;
1413 
1422  function RegisterEvent($EventsOrEventName, $EventType = NULL)
1423  {
1424  # convert parameters to array if not already in that form
1425  $Events = is_array($EventsOrEventName) ? $EventsOrEventName
1426  : array($EventsOrEventName => $EventType);
1427 
1428  # for each event
1429  foreach ($Events as $Name => $Type)
1430  {
1431  # store event information
1432  $this->RegisteredEvents[$Name]["Type"] = $Type;
1433  $this->RegisteredEvents[$Name]["Hooks"] = array();
1434  }
1435  }
1436 
1443  function IsRegisteredEvent($EventName)
1444  {
1445  return array_key_exists($EventName, $this->RegisteredEvents)
1446  ? TRUE : FALSE;
1447  }
1448 
1455  function IsHookedEvent($EventName)
1456  {
1457  # the event isn't hooked to if it isn't even registered
1458  if (!$this->IsRegisteredEvent($EventName))
1459  {
1460  return FALSE;
1461  }
1462 
1463  # return TRUE if there is at least one callback hooked to the event
1464  return count($this->RegisteredEvents[$EventName]["Hooks"]) > 0;
1465  }
1466 
1480  function HookEvent($EventsOrEventName, $Callback = NULL, $Order = self::ORDER_MIDDLE)
1481  {
1482  # convert parameters to array if not already in that form
1483  $Events = is_array($EventsOrEventName) ? $EventsOrEventName
1484  : array($EventsOrEventName => $Callback);
1485 
1486  # for each event
1487  $Success = TRUE;
1488  foreach ($Events as $EventName => $EventCallback)
1489  {
1490  # if callback is valid
1491  if (is_callable($EventCallback))
1492  {
1493  # if this is a periodic event we process internally
1494  if (isset($this->PeriodicEvents[$EventName]))
1495  {
1496  # process event now
1497  $this->ProcessPeriodicEvent($EventName, $EventCallback);
1498  }
1499  # if specified event has been registered
1500  elseif (isset($this->RegisteredEvents[$EventName]))
1501  {
1502  # add callback for event
1503  $this->RegisteredEvents[$EventName]["Hooks"][]
1504  = array("Callback" => $EventCallback, "Order" => $Order);
1505 
1506  # sort callbacks by order
1507  if (count($this->RegisteredEvents[$EventName]["Hooks"]) > 1)
1508  {
1509  usort($this->RegisteredEvents[$EventName]["Hooks"],
1510  array("ApplicationFramework", "HookEvent_OrderCompare"));
1511  }
1512  }
1513  else
1514  {
1515  $Success = FALSE;
1516  }
1517  }
1518  else
1519  {
1520  $Success = FALSE;
1521  }
1522  }
1523 
1524  # report to caller whether all callbacks were hooked
1525  return $Success;
1526  }
1528  private static function HookEvent_OrderCompare($A, $B)
1529  {
1530  if ($A["Order"] == $B["Order"]) { return 0; }
1531  return ($A["Order"] < $B["Order"]) ? -1 : 1;
1532  }
1533 
1544  function SignalEvent($EventName, $Parameters = NULL)
1545  {
1546  $ReturnValue = NULL;
1547 
1548  # if event has been registered
1549  if (isset($this->RegisteredEvents[$EventName]))
1550  {
1551  # set up default return value (if not NULL)
1552  switch ($this->RegisteredEvents[$EventName]["Type"])
1553  {
1554  case self::EVENTTYPE_CHAIN:
1555  $ReturnValue = $Parameters;
1556  break;
1557 
1558  case self::EVENTTYPE_NAMED:
1559  $ReturnValue = array();
1560  break;
1561  }
1562 
1563  # for each callback for this event
1564  foreach ($this->RegisteredEvents[$EventName]["Hooks"] as $Hook)
1565  {
1566  # invoke callback
1567  $Callback = $Hook["Callback"];
1568  $Result = ($Parameters !== NULL)
1569  ? call_user_func_array($Callback, $Parameters)
1570  : call_user_func($Callback);
1571 
1572  # process return value based on event type
1573  switch ($this->RegisteredEvents[$EventName]["Type"])
1574  {
1575  case self::EVENTTYPE_CHAIN:
1576  if ($Result !== NULL)
1577  {
1578  foreach ($Parameters as $Index => $Value)
1579  {
1580  if (array_key_exists($Index, $Result))
1581  {
1582  $Parameters[$Index] = $Result[$Index];
1583  }
1584  }
1585  $ReturnValue = $Parameters;
1586  }
1587  break;
1588 
1589  case self::EVENTTYPE_FIRST:
1590  if ($Result !== NULL)
1591  {
1592  $ReturnValue = $Result;
1593  break 2;
1594  }
1595  break;
1596 
1597  case self::EVENTTYPE_NAMED:
1598  $CallbackName = is_array($Callback)
1599  ? (is_object($Callback[0])
1600  ? get_class($Callback[0])
1601  : $Callback[0])."::".$Callback[1]
1602  : $Callback;
1603  $ReturnValue[$CallbackName] = $Result;
1604  break;
1605 
1606  default:
1607  break;
1608  }
1609  }
1610  }
1611  else
1612  {
1613  $this->LogError(self::LOGLVL_WARNING,
1614  "Unregistered event signaled (".$EventName.").");
1615  }
1616 
1617  # return value if any to caller
1618  return $ReturnValue;
1619  }
1620 
1626  function IsStaticOnlyEvent($EventName)
1627  {
1628  return isset($this->PeriodicEvents[$EventName]) ? TRUE : FALSE;
1629  }
1630 
1641  function EventWillNextRunAt($EventName, $Callback)
1642  {
1643  # if event is not a periodic event report failure to caller
1644  if (!array_key_exists($EventName, $this->EventPeriods)) { return FALSE; }
1645 
1646  # retrieve last execution time for event if available
1647  $Signature = self::GetCallbackSignature($Callback);
1648  $LastRunTime = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
1649  ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
1650 
1651  # if event was not found report failure to caller
1652  if ($LastRunTime === NULL) { return FALSE; }
1653 
1654  # calculate next run time based on event period
1655  $NextRunTime = strtotime($LastRunTime) + $this->EventPeriods[$EventName];
1656 
1657  # report next run time to caller
1658  return $NextRunTime;
1659  }
1660 
1677  {
1678  # retrieve last execution times
1679  $this->DB->Query("SELECT * FROM PeriodicEvents");
1680  $LastRunTimes = $this->DB->FetchColumn("LastRunAt", "Signature");
1681 
1682  # for each known event
1683  $Events = array();
1684  foreach ($this->KnownPeriodicEvents as $Signature => $Info)
1685  {
1686  # if last run time for event is available
1687  if (array_key_exists($Signature, $LastRunTimes))
1688  {
1689  # calculate next run time for event
1690  $LastRun = strtotime($LastRunTimes[$Signature]);
1691  $NextRun = $LastRun + $this->EventPeriods[$Info["Period"]];
1692  if ($Info["Period"] == "EVENT_PERIODIC") { $LastRun = FALSE; }
1693  }
1694  else
1695  {
1696  # set info to indicate run times are not known
1697  $LastRun = FALSE;
1698  $NextRun = FALSE;
1699  }
1700 
1701  # add event info to list
1702  $Events[$Signature] = $Info;
1703  $Events[$Signature]["LastRun"] = $LastRun;
1704  $Events[$Signature]["NextRun"] = $NextRun;
1705  $Events[$Signature]["Parameters"] = NULL;
1706  }
1707 
1708  # return list of known events to caller
1709  return $Events;
1710  }
1711 
1712  /*@)*/ /* Event Handling */
1713 
1714 
1715  # ---- Task Management ---------------------------------------------------
1716  /*@(*/
1718 
1720  const PRIORITY_HIGH = 1;
1722  const PRIORITY_MEDIUM = 2;
1724  const PRIORITY_LOW = 3;
1727 
1740  function QueueTask($Callback, $Parameters = NULL,
1741  $Priority = self::PRIORITY_LOW, $Description = "")
1742  {
1743  # pack task info and write to database
1744  if ($Parameters === NULL) { $Parameters = array(); }
1745  $this->DB->Query("INSERT INTO TaskQueue"
1746  ." (Callback, Parameters, Priority, Description)"
1747  ." VALUES ('".addslashes(serialize($Callback))."', '"
1748  .addslashes(serialize($Parameters))."', ".intval($Priority).", '"
1749  .addslashes($Description)."')");
1750  }
1751 
1769  function QueueUniqueTask($Callback, $Parameters = NULL,
1770  $Priority = self::PRIORITY_LOW, $Description = "")
1771  {
1772  if ($this->TaskIsInQueue($Callback, $Parameters))
1773  {
1774  $QueryResult = $this->DB->Query("SELECT TaskId,Priority FROM TaskQueue"
1775  ." WHERE Callback = '".addslashes(serialize($Callback))."'"
1776  .($Parameters ? " AND Parameters = '"
1777  .addslashes(serialize($Parameters))."'" : ""));
1778  if ($QueryResult !== FALSE)
1779  {
1780  $Record = $this->DB->FetchRow();
1781  if ($Record["Priority"] > $Priority)
1782  {
1783  $this->DB->Query("UPDATE TaskQueue"
1784  ." SET Priority = ".intval($Priority)
1785  ." WHERE TaskId = ".intval($Record["TaskId"]));
1786  }
1787  }
1788  return FALSE;
1789  }
1790  else
1791  {
1792  $this->QueueTask($Callback, $Parameters, $Priority, $Description);
1793  return TRUE;
1794  }
1795  }
1796 
1806  function TaskIsInQueue($Callback, $Parameters = NULL)
1807  {
1808  $QueuedCount = $this->DB->Query(
1809  "SELECT COUNT(*) AS FoundCount FROM TaskQueue"
1810  ." WHERE Callback = '".addslashes(serialize($Callback))."'"
1811  .($Parameters ? " AND Parameters = '"
1812  .addslashes(serialize($Parameters))."'" : ""),
1813  "FoundCount");
1814  $RunningCount = $this->DB->Query(
1815  "SELECT COUNT(*) AS FoundCount FROM RunningTasks"
1816  ." WHERE Callback = '".addslashes(serialize($Callback))."'"
1817  .($Parameters ? " AND Parameters = '"
1818  .addslashes(serialize($Parameters))."'" : ""),
1819  "FoundCount");
1820  $FoundCount = $QueuedCount + $RunningCount;
1821  return ($FoundCount ? TRUE : FALSE);
1822  }
1823 
1829  function GetTaskQueueSize($Priority = NULL)
1830  {
1831  return $this->GetQueuedTaskCount(NULL, NULL, $Priority);
1832  }
1833 
1841  function GetQueuedTaskList($Count = 100, $Offset = 0)
1842  {
1843  return $this->GetTaskList("SELECT * FROM TaskQueue"
1844  ." ORDER BY Priority, TaskId ", $Count, $Offset);
1845  }
1846 
1860  function GetQueuedTaskCount($Callback = NULL,
1861  $Parameters = NULL, $Priority = NULL, $Description = NULL)
1862  {
1863  $Query = "SELECT COUNT(*) AS TaskCount FROM TaskQueue";
1864  $Sep = " WHERE";
1865  if ($Callback !== NULL)
1866  {
1867  $Query .= $Sep." Callback = '".addslashes(serialize($Callback))."'";
1868  $Sep = " AND";
1869  }
1870  if ($Parameters !== NULL)
1871  {
1872  $Query .= $Sep." Parameters = '".addslashes(serialize($Parameters))."'";
1873  $Sep = " AND";
1874  }
1875  if ($Priority !== NULL)
1876  {
1877  $Query .= $Sep." Priority = ".intval($Priority);
1878  $Sep = " AND";
1879  }
1880  if ($Description !== NULL)
1881  {
1882  $Query .= $Sep." Description = '".addslashes($Description)."'";
1883  }
1884  return $this->DB->Query($Query, "TaskCount");
1885  }
1886 
1894  function GetRunningTaskList($Count = 100, $Offset = 0)
1895  {
1896  return $this->GetTaskList("SELECT * FROM RunningTasks"
1897  ." WHERE StartedAt >= '".date("Y-m-d H:i:s",
1898  (time() - ini_get("max_execution_time")))."'"
1899  ." ORDER BY StartedAt", $Count, $Offset);
1900  }
1901 
1909  function GetOrphanedTaskList($Count = 100, $Offset = 0)
1910  {
1911  return $this->GetTaskList("SELECT * FROM RunningTasks"
1912  ." WHERE StartedAt < '".date("Y-m-d H:i:s",
1913  (time() - ini_get("max_execution_time")))."'"
1914  ." ORDER BY StartedAt", $Count, $Offset);
1915  }
1916 
1922  {
1923  return $this->DB->Query("SELECT COUNT(*) AS Count FROM RunningTasks"
1924  ." WHERE StartedAt < '".date("Y-m-d H:i:s",
1925  (time() - ini_get("max_execution_time")))."'",
1926  "Count");
1927  }
1928 
1934  function ReQueueOrphanedTask($TaskId, $NewPriority = NULL)
1935  {
1936  $this->DB->Query("LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
1937  $this->DB->Query("INSERT INTO TaskQueue"
1938  ." (Callback,Parameters,Priority,Description) "
1939  ."SELECT Callback, Parameters, Priority, Description"
1940  ." FROM RunningTasks WHERE TaskId = ".intval($TaskId));
1941  if ($NewPriority !== NULL)
1942  {
1943  $NewTaskId = $this->DB->LastInsertId();
1944  $this->DB->Query("UPDATE TaskQueue SET Priority = "
1945  .intval($NewPriority)
1946  ." WHERE TaskId = ".intval($NewTaskId));
1947  }
1948  $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
1949  $this->DB->Query("UNLOCK TABLES");
1950  }
1951 
1956  function DeleteTask($TaskId)
1957  {
1958  $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = ".intval($TaskId));
1959  $this->DB->Query("DELETE FROM RunningTasks WHERE TaskId = ".intval($TaskId));
1960  }
1961 
1969  function GetTask($TaskId)
1970  {
1971  # assume task will not be found
1972  $Task = NULL;
1973 
1974  # look for task in task queue
1975  $this->DB->Query("SELECT * FROM TaskQueue WHERE TaskId = ".intval($TaskId));
1976 
1977  # if task was not found in queue
1978  if (!$this->DB->NumRowsSelected())
1979  {
1980  # look for task in running task list
1981  $this->DB->Query("SELECT * FROM RunningTasks WHERE TaskId = "
1982  .intval($TaskId));
1983  }
1984 
1985  # if task was found
1986  if ($this->DB->NumRowsSelected())
1987  {
1988  # if task was periodic
1989  $Row = $this->DB->FetchRow();
1990  if ($Row["Callback"] ==
1991  serialize(array("ApplicationFramework", "PeriodicEventWrapper")))
1992  {
1993  # unpack periodic task callback
1994  $WrappedCallback = unserialize($Row["Parameters"]);
1995  $Task["Callback"] = $WrappedCallback[1];
1996  $Task["Parameters"] = $WrappedCallback[2];
1997  }
1998  else
1999  {
2000  # unpack task callback and parameters
2001  $Task["Callback"] = unserialize($Row["Callback"]);
2002  $Task["Parameters"] = unserialize($Row["Parameters"]);
2003  }
2004  }
2005 
2006  # return task to caller
2007  return $Task;
2008  }
2009 
2017  function TaskExecutionEnabled($NewValue = DB_NOVALUE)
2018  {
2019  return $this->UpdateSetting("TaskExecutionEnabled", $NewValue);
2020  }
2021 
2027  function MaxTasks($NewValue = DB_NOVALUE)
2028  {
2029  return $this->UpdateSetting("MaxTasksRunning", $NewValue);
2030  }
2031 
2039  function MaxExecutionTime($NewValue = NULL)
2040  {
2041  if (func_num_args() && !ini_get("safe_mode"))
2042  {
2043  if ($NewValue != $this->Settings["MaxExecTime"])
2044  {
2045  $this->Settings["MaxExecTime"] = max($NewValue, 5);
2046  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
2047  ." SET MaxExecTime = '"
2048  .intval($this->Settings["MaxExecTime"])."'");
2049  }
2050  ini_set("max_execution_time", $this->Settings["MaxExecTime"]);
2051  set_time_limit($this->Settings["MaxExecTime"]);
2052  }
2053  return ini_get("max_execution_time");
2054  }
2055 
2056  /*@)*/ /* Task Management */
2057 
2058 
2059  # ---- Clean URL Support -------------------------------------------------
2060  /*@(*/
2062 
2089  function AddCleanUrl($Pattern, $Page, $GetVars = NULL, $Template = NULL)
2090  {
2091  # save clean URL mapping parameters
2092  $this->CleanUrlMappings[] = array(
2093  "Pattern" => $Pattern,
2094  "Page" => $Page,
2095  "GetVars" => $GetVars,
2096  );
2097 
2098  # if replacement template specified
2099  if ($Template !== NULL)
2100  {
2101  # if GET parameters specified
2102  if (count($GetVars))
2103  {
2104  # retrieve all possible permutations of GET parameters
2105  $GetPerms = $this->ArrayPermutations(array_keys($GetVars));
2106 
2107  # for each permutation of GET parameters
2108  foreach ($GetPerms as $VarPermutation)
2109  {
2110  # construct search pattern for permutation
2111  $SearchPattern = "/href=([\"'])index\\.php\\?P=".$Page;
2112  $GetVarSegment = "";
2113  foreach ($VarPermutation as $GetVar)
2114  {
2115  if (preg_match("%\\\$[0-9]+%", $GetVars[$GetVar]))
2116  {
2117  $GetVarSegment .= "&amp;".$GetVar."=((?:(?!\\1)[^&])+)";
2118  }
2119  else
2120  {
2121  $GetVarSegment .= "&amp;".$GetVar."=".$GetVars[$GetVar];
2122  }
2123  }
2124  $SearchPattern .= $GetVarSegment."\\1/i";
2125 
2126  # if template is actually a callback
2127  if (is_callable($Template))
2128  {
2129  # add pattern to HTML output mod callbacks list
2130  $this->OutputModificationCallbacks[] = array(
2131  "Pattern" => $Pattern,
2132  "Page" => $Page,
2133  "SearchPattern" => $SearchPattern,
2134  "Callback" => $Template,
2135  );
2136  }
2137  else
2138  {
2139  # construct replacement string for permutation
2140  $Replacement = $Template;
2141  $Index = 2;
2142  foreach ($VarPermutation as $GetVar)
2143  {
2144  $Replacement = str_replace(
2145  "\$".$GetVar, "\$".$Index, $Replacement);
2146  $Index++;
2147  }
2148  $Replacement = "href=\"".$Replacement."\"";
2149 
2150  # add pattern to HTML output modifications list
2151  $this->OutputModificationPatterns[] = $SearchPattern;
2152  $this->OutputModificationReplacements[] = $Replacement;
2153  }
2154  }
2155  }
2156  else
2157  {
2158  # construct search pattern
2159  $SearchPattern = "/href=\"index\\.php\\?P=".$Page."\"/i";
2160 
2161  # if template is actually a callback
2162  if (is_callable($Template))
2163  {
2164  # add pattern to HTML output mod callbacks list
2165  $this->OutputModificationCallbacks[] = array(
2166  "Pattern" => $Pattern,
2167  "Page" => $Page,
2168  "SearchPattern" => $SearchPattern,
2169  "Callback" => $Template,
2170  );
2171  }
2172  else
2173  {
2174  # add simple pattern to HTML output modifications list
2175  $this->OutputModificationPatterns[] = $SearchPattern;
2176  $this->OutputModificationReplacements[] = "href=\"".$Template."\"";
2177  }
2178  }
2179  }
2180  }
2181 
2187  function CleanUrlIsMapped($Path)
2188  {
2189  foreach ($this->CleanUrlMappings as $Info)
2190  {
2191  if (preg_match($Info["Pattern"], $Path))
2192  {
2193  return TRUE;
2194  }
2195  }
2196  return FALSE;
2197  }
2198 
2205  function GetCleanUrlForPath($Path)
2206  {
2207  # the search patterns and callbacks require a specific format
2208  $Format = "href=\"".str_replace("&", "&amp;", $Path)."\"";
2209  $Search = $Format;
2210 
2211  # perform any regular expression replacements on the search string
2212  $Search = preg_replace(
2213  $this->OutputModificationPatterns,
2214  $this->OutputModificationReplacements,
2215  $Search);
2216 
2217  # only run the callbacks if a replacement hasn't already been performed
2218  if ($Search == $Format)
2219  {
2220  # perform any callback replacements on the search string
2221  foreach ($this->OutputModificationCallbacks as $Info)
2222  {
2223  # make the information available to the callback
2224  $this->OutputModificationCallbackInfo = $Info;
2225 
2226  # execute the callback
2227  $Search = preg_replace_callback(
2228  $Info["SearchPattern"],
2229  array($this, "OutputModificationCallbackShell"),
2230  $Search);
2231  }
2232  }
2233 
2234  # return the path untouched if no replacements were performed
2235  if ($Search == $Format)
2236  {
2237  return $Path;
2238  }
2239 
2240  # remove the bits added to the search string to get it recognized by
2241  # the replacement expressions and callbacks
2242  $Result = substr($Search, 6, -1);
2243 
2244  return $Result;
2245  }
2246 
2253  public function GetUncleanUrlForPath($Path)
2254  {
2255  # for each clean URL mapping
2256  foreach ($this->CleanUrlMappings as $Info)
2257  {
2258  # if current path matches the clean URL pattern
2259  if (preg_match($Info["Pattern"], $Path, $Matches))
2260  {
2261  # the GET parameters for the URL, starting with the page name
2262  $GetVars = array("P" => $Info["Page"]);
2263 
2264  # if additional $_GET variables specified for clean URL
2265  if ($Info["GetVars"] !== NULL)
2266  {
2267  # for each $_GET variable specified for clean URL
2268  foreach ($Info["GetVars"] as $VarName => $VarTemplate)
2269  {
2270  # start with template for variable value
2271  $Value = $VarTemplate;
2272 
2273  # for each subpattern matched in current URL
2274  foreach ($Matches as $Index => $Match)
2275  {
2276  # if not first (whole) match
2277  if ($Index > 0)
2278  {
2279  # make any substitutions in template
2280  $Value = str_replace("$".$Index, $Match, $Value);
2281  }
2282  }
2283 
2284  # add the GET variable
2285  $GetVars[$VarName] = $Value;
2286  }
2287  }
2288 
2289  # return the unclean URL
2290  return "index.php?" . http_build_query($GetVars);
2291  }
2292  }
2293 
2294  # return the path unchanged
2295  return $Path;
2296  }
2297 
2303  function GetCleanUrl()
2304  {
2305  return $this->GetCleanUrlForPath($this->GetUncleanUrl());
2306  }
2307 
2312  function GetUncleanUrl()
2313  {
2314  $GetVars = array("P" => $this->GetPageName()) + $_GET;
2315  return "index.php?" . http_build_query($GetVars);
2316  }
2317 
2318  /*@)*/ /* Clean URL Support */
2319 
2320 
2321  # ---- Server Environment ------------------------------------------------
2322  /*@(*/
2324 
2330  static function SessionLifetime($NewValue = NULL)
2331  {
2332  if ($NewValue !== NULL)
2333  {
2334  self::$SessionLifetime = $NewValue;
2335  }
2336  return self::$SessionLifetime;
2337  }
2338 
2344  static function HtaccessSupport()
2345  {
2346  # HTACCESS_SUPPORT is set in the .htaccess file
2347  return isset($_SERVER["HTACCESS_SUPPORT"]);
2348  }
2349 
2357  static function RootUrl()
2358  {
2359  # return override value if one is set
2360  if (self::$RootUrlOverride !== NULL)
2361  {
2362  return self::$RootUrlOverride;
2363  }
2364 
2365  # determine scheme name
2366  $Protocol = (isset($_SERVER["HTTPS"]) ? "https" : "http");
2367 
2368  # if HTTP_HOST is preferred or SERVER_NAME points to localhost
2369  # and HTTP_HOST is set
2370  if ((self::$PreferHttpHost || ($_SERVER["SERVER_NAME"] == "127.0.0.1"))
2371  && isset($_SERVER["HTTP_HOST"]))
2372  {
2373  # use HTTP_HOST for domain name
2374  $DomainName = $_SERVER["HTTP_HOST"];
2375  }
2376  else
2377  {
2378  # use SERVER_NAME for domain name
2379  $DomainName = $_SERVER["HTTP_HOST"];
2380  }
2381 
2382  # build URL root and return to caller
2383  return $Protocol."://".$DomainName;
2384  }
2385 
2400  static function RootUrlOverride($NewValue = self::NOVALUE)
2401  {
2402  if ($NewValue !== self::NOVALUE)
2403  {
2404  self::$RootUrlOverride = strlen(trim($NewValue)) ? $NewValue : NULL;
2405  }
2406  return self::$RootUrlOverride;
2407  }
2408 
2418  static function BaseUrl()
2419  {
2420  $BaseUrl = self::RootUrl().dirname($_SERVER["SCRIPT_NAME"]);
2421  if (substr($BaseUrl, -1) != "/") { $BaseUrl .= "/"; }
2422  return $BaseUrl;
2423  }
2424 
2432  static function FullUrl()
2433  {
2434  return self::RootUrl().$_SERVER["REQUEST_URI"];
2435  }
2436 
2447  static function PreferHttpHost($NewValue = NULL)
2448  {
2449  if ($NewValue !== NULL)
2450  {
2451  self::$PreferHttpHost = ($NewValue ? TRUE : FALSE);
2452  }
2453  return self::$PreferHttpHost;
2454  }
2455 
2460  static function BasePath()
2461  {
2462  $BasePath = dirname($_SERVER["SCRIPT_NAME"]);
2463 
2464  if (substr($BasePath, -1) != "/")
2465  {
2466  $BasePath .= "/";
2467  }
2468 
2469  return $BasePath;
2470  }
2471 
2477  static function GetScriptUrl()
2478  {
2479  if (array_key_exists("SCRIPT_URL", $_SERVER))
2480  {
2481  return $_SERVER["SCRIPT_URL"];
2482  }
2483  elseif (array_key_exists("REDIRECT_URL", $_SERVER))
2484  {
2485  return $_SERVER["REDIRECT_URL"];
2486  }
2487  elseif (array_key_exists("REQUEST_URI", $_SERVER))
2488  {
2489  $Pieces = parse_url($_SERVER["REQUEST_URI"]);
2490  return $Pieces["path"];
2491  }
2492  else
2493  {
2494  return NULL;
2495  }
2496  }
2497 
2506  static function WasUrlRewritten($ScriptName="index.php")
2507  {
2508  # needed to get the path of the URL minus the query and fragment pieces
2509  $Components = parse_url(self::GetScriptUrl());
2510 
2511  # if parsing was successful and a path is set
2512  if (is_array($Components) && isset($Components["path"]))
2513  {
2514  $BasePath = self::BasePath();
2515  $Path = $Components["path"];
2516 
2517  # the URL was rewritten if the path isn't the base path, i.e., the
2518  # home page, and the file in the URL isn't the script generating the
2519  # page
2520  if ($BasePath != $Path && basename($Path) != $ScriptName)
2521  {
2522  return TRUE;
2523  }
2524  }
2525 
2526  # the URL wasn't rewritten
2527  return FALSE;
2528  }
2529 
2535  static function GetFreeMemory()
2536  {
2537  return self::GetPhpMemoryLimit() - memory_get_usage();
2538  }
2539 
2545  static function GetPhpMemoryLimit()
2546  {
2547  $Str = strtoupper(ini_get("memory_limit"));
2548  if (substr($Str, -1) == "B") { $Str = substr($Str, 0, strlen($Str) - 1); }
2549  switch (substr($Str, -1))
2550  {
2551  case "K": $MemoryLimit = (int)$Str * 1024; break;
2552  case "M": $MemoryLimit = (int)$Str * 1048576; break;
2553  case "G": $MemoryLimit = (int)$Str * 1073741824; break;
2554  default: $MemoryLimit = (int)$Str; break;
2555  }
2556  return $MemoryLimit;
2557  }
2558 
2559  /*@)*/ /* Server Environment */
2560 
2561 
2562  # ---- Backward Compatibility --------------------------------------------
2563  /*@(*/
2565 
2570  function FindCommonTemplate($BaseName)
2571  {
2572  return $this->FindFile(
2573  $this->IncludeDirList, $BaseName, array("tpl", "html"));
2574  }
2575 
2576  /*@)*/ /* Backward Compatibility */
2577 
2578 
2579  # ---- PRIVATE INTERFACE -------------------------------------------------
2580 
2581  private $ActiveUI = "default";
2582  private $BrowserDetectFunc;
2583  private $CleanUrlMappings = array();
2584  private $CleanUrlRewritePerformed = FALSE;
2585  private $DB;
2586  private $DefaultPage = "Home";
2587  private $EnvIncludes = array();
2588  private $ExecutionStartTime;
2589  private $FoundUIFiles = array();
2590  private $AdditionalRequiredUIFiles = array();
2591  private $HtmlCharset = "UTF-8";
2592  private $JumpToPage = NULL;
2593  private $LogFileName = "local/logs/site.log";
2594  private $MaxRunningTasksToTrack = 250;
2595  private $OutputModificationPatterns = array();
2596  private $OutputModificationReplacements = array();
2597  private $OutputModificationCallbacks = array();
2598  private $OutputModificationCallbackInfo;
2599  private $PageName;
2600  private $PostProcessingFuncs = array();
2601  private $RunningInBackground = FALSE;
2602  private $RunningTask;
2603  private $SavedContext;
2604  private $Settings;
2605  private $SuppressHTML = FALSE;
2606  private $SaveTemplateLocationCache = FALSE;
2607  private $UnbufferedCallbacks = array();
2608  private $UseBaseTag = FALSE;
2609  private $UseMinimizedJavascript = FALSE;
2610 
2611  private static $AppName = "ScoutAF";
2612  private static $ObjectDirectories = array();
2613  private static $ObjectLocationCache;
2614  private static $ObjectLocationCacheInterval = 60;
2615  private static $ObjectLocationCacheExpiration;
2616  private static $PreferHttpHost = FALSE;
2617  private static $RootUrlOverride = NULL;
2618  private static $SaveObjectLocationCache = FALSE;
2619  private static $SessionLifetime = 1440;
2620 
2625  private $NoTSR = FALSE;
2626 
2627  private $KnownPeriodicEvents = array();
2628  private $PeriodicEvents = array(
2629  "EVENT_HOURLY" => self::EVENTTYPE_DEFAULT,
2630  "EVENT_DAILY" => self::EVENTTYPE_DEFAULT,
2631  "EVENT_WEEKLY" => self::EVENTTYPE_DEFAULT,
2632  "EVENT_MONTHLY" => self::EVENTTYPE_DEFAULT,
2633  "EVENT_PERIODIC" => self::EVENTTYPE_NAMED,
2634  );
2635  private $EventPeriods = array(
2636  "EVENT_HOURLY" => 3600,
2637  "EVENT_DAILY" => 86400,
2638  "EVENT_WEEKLY" => 604800,
2639  "EVENT_MONTHLY" => 2592000,
2640  "EVENT_PERIODIC" => 0,
2641  );
2642  private $UIEvents = array(
2643  "EVENT_PAGE_LOAD" => self::EVENTTYPE_DEFAULT,
2644  "EVENT_PHP_FILE_LOAD" => self::EVENTTYPE_CHAIN,
2645  "EVENT_PHP_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
2646  "EVENT_HTML_FILE_LOAD" => self::EVENTTYPE_CHAIN,
2647  "EVENT_HTML_FILE_LOAD_COMPLETE" => self::EVENTTYPE_DEFAULT,
2648  "EVENT_PAGE_OUTPUT_FILTER" => self::EVENTTYPE_CHAIN,
2649  );
2650 
2654  private function LoadSettings()
2655  {
2656  # read settings in from database
2657  $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
2658  $this->Settings = $this->DB->FetchRow();
2659 
2660  # if settings were not previously initialized
2661  if (!$this->Settings)
2662  {
2663  # initialize settings in database
2664  $this->DB->Query("INSERT INTO ApplicationFrameworkSettings"
2665  ." (LastTaskRunAt) VALUES ('2000-01-02 03:04:05')");
2666 
2667  # read new settings in from database
2668  $this->DB->Query("SELECT * FROM ApplicationFrameworkSettings");
2669  $this->Settings = $this->DB->FetchRow();
2670  }
2671 
2672  # if base path was not previously set or we appear to have moved
2673  if (!array_key_exists("BasePath", $this->Settings)
2674  || (!strlen($this->Settings["BasePath"]))
2675  || (!array_key_exists("BasePathCheck", $this->Settings))
2676  || (__FILE__ != $this->Settings["BasePathCheck"]))
2677  {
2678  # attempt to extract base path from Apache .htaccess file
2679  if (is_readable(".htaccess"))
2680  {
2681  $Lines = file(".htaccess");
2682  foreach ($Lines as $Line)
2683  {
2684  if (preg_match("/\\s*RewriteBase\\s+/", $Line))
2685  {
2686  $Pieces = preg_split(
2687  "/\\s+/", $Line, NULL, PREG_SPLIT_NO_EMPTY);
2688  $BasePath = $Pieces[1];
2689  }
2690  }
2691  }
2692 
2693  # if base path was found
2694  if (isset($BasePath))
2695  {
2696  # save base path locally
2697  $this->Settings["BasePath"] = $BasePath;
2698 
2699  # save base path to database
2700  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
2701  ." SET BasePath = '".addslashes($BasePath)."'"
2702  .", BasePathCheck = '".addslashes(__FILE__)."'");
2703  }
2704  }
2705 
2706  # if template location cache has been saved to database
2707  if (isset($this->Settings["TemplateLocationCache"]))
2708  {
2709  # unserialize cache values into array and use if valid
2710  $Cache = unserialize($this->Settings["TemplateLocationCache"]);
2711  $this->Settings["TemplateLocationCache"] =
2712  count($Cache) ? $Cache : array();
2713  }
2714  else
2715  {
2716  # start with empty cache
2717  $this->Settings["TemplateLocationCache"] = array();
2718  }
2719 
2720  # if object location cache has been saved to database
2721  if (isset($this->Settings["ObjectLocationCache"]))
2722  {
2723  # unserialize cache values into array and use if valid
2724  $Cache = unserialize($this->Settings["ObjectLocationCache"]);
2725  $this->Settings["ObjectLocationCache"] =
2726  count($Cache) ? $Cache : array();
2727 
2728  # store static versions for use when autoloading objects
2729  self::$ObjectLocationCache =
2730  $this->Settings["ObjectLocationCache"];
2731  self::$ObjectLocationCacheInterval =
2732  $this->Settings["ObjectLocationCacheInterval"];
2733  self::$ObjectLocationCacheExpiration =
2734  $this->Settings["ObjectLocationCacheExpiration"];
2735  }
2736  else
2737  {
2738  # start with empty cache
2739  $this->Settings["ObjectLocationCache"] = array();
2740  }
2741  }
2742 
2749  private function RewriteCleanUrls($PageName)
2750  {
2751  # if URL rewriting is supported by the server
2752  if ($this->HtaccessSupport())
2753  {
2754  # retrieve current URL and remove base path if present
2755  $Url = $this->GetPageLocation();
2756 
2757  # for each clean URL mapping
2758  foreach ($this->CleanUrlMappings as $Info)
2759  {
2760  # if current URL matches clean URL pattern
2761  if (preg_match($Info["Pattern"], $Url, $Matches))
2762  {
2763  # set new page
2764  $PageName = $Info["Page"];
2765 
2766  # if $_GET variables specified for clean URL
2767  if ($Info["GetVars"] !== NULL)
2768  {
2769  # for each $_GET variable specified for clean URL
2770  foreach ($Info["GetVars"] as $VarName => $VarTemplate)
2771  {
2772  # start with template for variable value
2773  $Value = $VarTemplate;
2774 
2775  # for each subpattern matched in current URL
2776  foreach ($Matches as $Index => $Match)
2777  {
2778  # if not first (whole) match
2779  if ($Index > 0)
2780  {
2781  # make any substitutions in template
2782  $Value = str_replace("$".$Index, $Match, $Value);
2783  }
2784  }
2785 
2786  # set $_GET variable
2787  $_GET[$VarName] = $Value;
2788  }
2789  }
2790 
2791  # set flag indicating clean URL mapped
2792  $this->CleanUrlRewritePerformed = TRUE;
2793 
2794  # stop looking for a mapping
2795  break;
2796  }
2797  }
2798  }
2799 
2800  # return (possibly) updated page name to caller
2801  return $PageName;
2802  }
2803 
2820  private function FindFile($DirectoryList, $BaseName,
2821  $PossibleSuffixes = NULL, $PossiblePrefixes = NULL)
2822  {
2823  # generate template cache index for this page
2824  $CacheIndex = md5(serialize($DirectoryList))
2825  .":".$this->ActiveUI.":".$BaseName;
2826 
2827  # if we have cached location and cache expiration time has not elapsed
2828  if (($this->Settings["TemplateLocationCacheInterval"] > 0)
2829  && count($this->Settings["TemplateLocationCache"])
2830  && array_key_exists($CacheIndex,
2831  $this->Settings["TemplateLocationCache"])
2832  && (time() < strtotime(
2833  $this->Settings["TemplateLocationCacheExpiration"])))
2834  {
2835  # use template location from cache
2836  $FoundFileName = $this->Settings[
2837  "TemplateLocationCache"][$CacheIndex];
2838  }
2839  else
2840  {
2841  # if suffixes specified and base name does not include suffix
2842  if (count($PossibleSuffixes)
2843  && !preg_match("/\.[a-zA-Z0-9]+$/", $BaseName))
2844  {
2845  # add versions of file names with suffixes to file name list
2846  $FileNames = array();
2847  foreach ($PossibleSuffixes as $Suffix)
2848  {
2849  $FileNames[] = $BaseName.".".$Suffix;
2850  }
2851  }
2852  else
2853  {
2854  # use base name as file name
2855  $FileNames = array($BaseName);
2856  }
2857 
2858  # if prefixes specified
2859  if (count($PossiblePrefixes))
2860  {
2861  # add versions of file names with prefixes to file name list
2862  $NewFileNames = array();
2863  foreach ($FileNames as $FileName)
2864  {
2865  foreach ($PossiblePrefixes as $Prefix)
2866  {
2867  $NewFileNames[] = $Prefix.$FileName;
2868  }
2869  }
2870  $FileNames = $NewFileNames;
2871  }
2872 
2873  # for each possible location
2874  $FoundFileName = NULL;
2875  foreach ($DirectoryList as $Dir)
2876  {
2877  # substitute active UI name into path
2878  $Dir = str_replace("%ACTIVEUI%", $this->ActiveUI, $Dir);
2879 
2880  # for each possible file name
2881  foreach ($FileNames as $File)
2882  {
2883  # if template is found at location
2884  if (file_exists($Dir.$File))
2885  {
2886  # save full template file name and stop looking
2887  $FoundFileName = $Dir.$File;
2888  break 2;
2889  }
2890  }
2891  }
2892 
2893  # save location in cache
2894  $this->Settings["TemplateLocationCache"][$CacheIndex]
2895  = $FoundFileName;
2896 
2897  # set flag indicating that cache should be saved
2898  $this->SaveTemplateLocationCache = TRUE;
2899  }
2900 
2901  # return full template file name to caller
2902  return $FoundFileName;
2903  }
2904 
2911  private function GetRequiredFilesNotYetLoaded($PageContentFile)
2912  {
2913  # start out assuming no files required
2914  $RequiredFiles = array();
2915 
2916  # if page content file supplied
2917  if ($PageContentFile)
2918  {
2919  # if file containing list of required files is available
2920  $Path = dirname($PageContentFile);
2921  $RequireListFile = $Path."/REQUIRES";
2922  if (file_exists($RequireListFile))
2923  {
2924  # read in list of required files
2925  $RequestedFiles = file($RequireListFile);
2926 
2927  # for each line in required file list
2928  foreach ($RequestedFiles as $Line)
2929  {
2930  # if line is not a comment
2931  $Line = trim($Line);
2932  if (!preg_match("/^#/", $Line))
2933  {
2934  # if file has not already been loaded
2935  if (!in_array($Line, $this->FoundUIFiles))
2936  {
2937  # add to list of required files
2938  $RequiredFiles[] = $Line;
2939  }
2940  }
2941  }
2942  }
2943  }
2944 
2945  # add in additional required files if any
2946  if (count($this->AdditionalRequiredUIFiles))
2947  {
2948  # make sure there are no duplicates
2949  $AdditionalRequiredUIFiles = array_unique(
2950  $this->AdditionalRequiredUIFiles);
2951 
2952  $RequiredFiles = array_merge(
2953  $RequiredFiles, $AdditionalRequiredUIFiles);
2954  }
2955 
2956  # return list of required files to caller
2957  return $RequiredFiles;
2958  }
2959 
2963  private function SetUpObjectAutoloading()
2964  {
2966  function __autoload($ClassName)
2967  {
2969  }
2970  }
2971 
2977  static function AutoloadObjects($ClassName)
2978  {
2979  # if caching is not turned off
2980  # and we have a cached location for class
2981  # and cache expiration has not elapsed
2982  # and file at cached location is readable
2983  if ((self::$ObjectLocationCacheInterval > 0)
2984  && count(self::$ObjectLocationCache)
2985  && array_key_exists($ClassName,
2986  self::$ObjectLocationCache)
2987  && (time() < strtotime(
2988  self::$ObjectLocationCacheExpiration))
2989  && is_readable(self::$ObjectLocationCache[$ClassName]))
2990  {
2991  # use object location from cache
2992  require_once(self::$ObjectLocationCache[$ClassName]);
2993  }
2994  else
2995  {
2996  # for each possible object file directory
2997  static $FileLists;
2998  foreach (self::$ObjectDirectories as $Location => $Info)
2999  {
3000  # if directory looks valid
3001  if (is_dir($Location))
3002  {
3003  # build class file name
3004  $NewClassName = ($Info["ClassPattern"] && $Info["ClassReplacement"])
3005  ? preg_replace($Info["ClassPattern"],
3006  $Info["ClassReplacement"], $ClassName)
3007  : $ClassName;
3008 
3009  # read in directory contents if not already retrieved
3010  if (!isset($FileLists[$Location]))
3011  {
3012  $FileLists[$Location] = self::ReadDirectoryTree(
3013  $Location, '/^.+\.php$/i');
3014  }
3015 
3016  # for each file in target directory
3017  $FileNames = $FileLists[$Location];
3018  $TargetName = strtolower($Info["Prefix"].$NewClassName.".php");
3019  foreach ($FileNames as $FileName)
3020  {
3021  # if file matches our target object file name
3022  if (strtolower($FileName) == $TargetName)
3023  {
3024  # include object file
3025  require_once($Location.$FileName);
3026 
3027  # save location to cache
3028  self::$ObjectLocationCache[$ClassName]
3029  = $Location.$FileName;
3030 
3031  # set flag indicating that cache should be saved
3032  self::$SaveObjectLocationCache = TRUE;
3033 
3034  # stop looking
3035  break 2;
3036  }
3037  }
3038  }
3039  }
3040  }
3041  }
3051  private static function ReadDirectoryTree($Directory, $Pattern)
3052  {
3053  $CurrentDir = getcwd();
3054  chdir($Directory);
3055  $DirIter = new RecursiveDirectoryIterator(".");
3056  $IterIter = new RecursiveIteratorIterator($DirIter);
3057  $RegexResults = new RegexIterator($IterIter, $Pattern,
3058  RecursiveRegexIterator::GET_MATCH);
3059  $FileList = array();
3060  foreach ($RegexResults as $Result)
3061  {
3062  $FileList[] = substr($Result[0], 2);
3063  }
3064  chdir($CurrentDir);
3065  return $FileList;
3066  }
3067 
3071  private function UndoMagicQuotes()
3072  {
3073  # if this PHP version has magic quotes support
3074  if (version_compare(PHP_VERSION, "5.4.0", "<"))
3075  {
3076  # turn off runtime magic quotes if on
3077  if (get_magic_quotes_runtime())
3078  {
3079  // @codingStandardsIgnoreStart
3080  set_magic_quotes_runtime(FALSE);
3081  // @codingStandardsIgnoreEnd
3082  }
3083 
3084  # if magic quotes GPC is on
3085  if (get_magic_quotes_gpc())
3086  {
3087  # strip added slashes from incoming variables
3088  $GPC = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
3089  array_walk_recursive($GPC,
3090  array($this, "UndoMagicQuotes_StripCallback"));
3091  }
3092  }
3093  }
3094  private function UndoMagicQuotes_StripCallback(&$Value)
3095  {
3096  $Value = stripslashes($Value);
3097  }
3098 
3103  private function LoadUIFunctions()
3104  {
3105  $Dirs = array(
3106  "local/interface/%ACTIVEUI%/include",
3107  "interface/%ACTIVEUI%/include",
3108  "local/interface/default/include",
3109  "interface/default/include",
3110  );
3111  foreach ($Dirs as $Dir)
3112  {
3113  $Dir = str_replace("%ACTIVEUI%", $this->ActiveUI, $Dir);
3114  if (is_dir($Dir))
3115  {
3116  $FileNames = scandir($Dir);
3117  foreach ($FileNames as $FileName)
3118  {
3119  if (preg_match("/^F-([A-Za-z0-9_]+)\.php/", $FileName, $Matches)
3120  || preg_match("/^F-([A-Za-z0-9_]+)\.html/", $FileName, $Matches))
3121  {
3122  if (!function_exists($Matches[1]))
3123  {
3124  include_once($Dir."/".$FileName);
3125  }
3126  }
3127  }
3128  }
3129  }
3130  }
3131 
3137  private function ProcessPeriodicEvent($EventName, $Callback)
3138  {
3139  # retrieve last execution time for event if available
3140  $Signature = self::GetCallbackSignature($Callback);
3141  $LastRun = $this->DB->Query("SELECT LastRunAt FROM PeriodicEvents"
3142  ." WHERE Signature = '".addslashes($Signature)."'", "LastRunAt");
3143 
3144  # determine whether enough time has passed for event to execute
3145  $ShouldExecute = (($LastRun === NULL)
3146  || (time() > (strtotime($LastRun) + $this->EventPeriods[$EventName])))
3147  ? TRUE : FALSE;
3148 
3149  # if event should run
3150  if ($ShouldExecute)
3151  {
3152  # add event to task queue
3153  $WrapperCallback = array("ApplicationFramework", "PeriodicEventWrapper");
3154  $WrapperParameters = array(
3155  $EventName, $Callback, array("LastRunAt" => $LastRun));
3156  $this->QueueUniqueTask($WrapperCallback, $WrapperParameters);
3157  }
3158 
3159  # add event to list of periodic events
3160  $this->KnownPeriodicEvents[$Signature] = array(
3161  "Period" => $EventName,
3162  "Callback" => $Callback,
3163  "Queued" => $ShouldExecute);
3164  }
3165 
3173  private static function PeriodicEventWrapper($EventName, $Callback, $Parameters)
3174  {
3175  static $DB;
3176  if (!isset($DB)) { $DB = new Database(); }
3177 
3178  # run event
3179  $ReturnVal = call_user_func_array($Callback, $Parameters);
3180 
3181  # if event is already in database
3182  $Signature = self::GetCallbackSignature($Callback);
3183  if ($DB->Query("SELECT COUNT(*) AS EventCount FROM PeriodicEvents"
3184  ." WHERE Signature = '".addslashes($Signature)."'", "EventCount"))
3185  {
3186  # update last run time for event
3187  $DB->Query("UPDATE PeriodicEvents SET LastRunAt = "
3188  .(($EventName == "EVENT_PERIODIC")
3189  ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
3190  : "NOW()")
3191  ." WHERE Signature = '".addslashes($Signature)."'");
3192  }
3193  else
3194  {
3195  # add last run time for event to database
3196  $DB->Query("INSERT INTO PeriodicEvents (Signature, LastRunAt) VALUES "
3197  ."('".addslashes($Signature)."', "
3198  .(($EventName == "EVENT_PERIODIC")
3199  ? "'".date("Y-m-d H:i:s", time() + ($ReturnVal * 60))."'"
3200  : "NOW()").")");
3201  }
3202  }
3203 
3209  private static function GetCallbackSignature($Callback)
3210  {
3211  return !is_array($Callback) ? $Callback
3212  : (is_object($Callback[0]) ? md5(serialize($Callback[0])) : $Callback[0])
3213  ."::".$Callback[1];
3214  }
3215 
3220  private function PrepForTSR()
3221  {
3222  # if HTML has been output and it's time to launch another task
3223  # (only TSR if HTML has been output because otherwise browsers
3224  # may misbehave after connection is closed)
3225  if (($this->JumpToPage || !$this->SuppressHTML)
3226  && (time() > (strtotime($this->Settings["LastTaskRunAt"])
3227  + (ini_get("max_execution_time")
3228  / $this->Settings["MaxTasksRunning"]) + 5))
3229  && $this->GetTaskQueueSize()
3230  && $this->Settings["TaskExecutionEnabled"])
3231  {
3232  # begin buffering output for TSR
3233  ob_start();
3234 
3235  # let caller know it is time to launch another task
3236  return TRUE;
3237  }
3238  else
3239  {
3240  # let caller know it is not time to launch another task
3241  return FALSE;
3242  }
3243  }
3244 
3249  private function LaunchTSR()
3250  {
3251  # set headers to close out connection to browser
3252  if (!$this->NoTSR)
3253  {
3254  ignore_user_abort(TRUE);
3255  header("Connection: close");
3256  header("Content-Length: ".ob_get_length());
3257  }
3258 
3259  # output buffered content
3260  while (ob_get_level()) { ob_end_flush(); }
3261  flush();
3262 
3263  # write out any outstanding data and end HTTP session
3264  session_write_close();
3265 
3266  # set flag indicating that we are now running in background
3267  $this->RunningInBackground = TRUE;
3268 
3269  # if there is still a task in the queue
3270  if ($this->GetTaskQueueSize())
3271  {
3272  # turn on output buffering to (hopefully) record any crash output
3273  ob_start();
3274 
3275  # lock tables and grab last task run time to double check
3276  $this->DB->Query("LOCK TABLES ApplicationFrameworkSettings WRITE");
3277  $this->LoadSettings();
3278 
3279  # if still time to launch another task
3280  if (time() > (strtotime($this->Settings["LastTaskRunAt"])
3281  + (ini_get("max_execution_time")
3282  / $this->Settings["MaxTasksRunning"]) + 5))
3283  {
3284  # update the "last run" time and release tables
3285  $this->DB->Query("UPDATE ApplicationFrameworkSettings"
3286  ." SET LastTaskRunAt = '".date("Y-m-d H:i:s")."'");
3287  $this->DB->Query("UNLOCK TABLES");
3288 
3289  # run tasks while there is a task in the queue and enough time left
3290  do
3291  {
3292  # run the next task
3293  $this->RunNextTask();
3294  }
3295  while ($this->GetTaskQueueSize()
3296  && ($this->GetSecondsBeforeTimeout() > 65));
3297  }
3298  else
3299  {
3300  # release tables
3301  $this->DB->Query("UNLOCK TABLES");
3302  }
3303  }
3304  }
3305 
3313  private function GetTaskList($DBQuery, $Count, $Offset)
3314  {
3315  $this->DB->Query($DBQuery." LIMIT ".intval($Offset).",".intval($Count));
3316  $Tasks = array();
3317  while ($Row = $this->DB->FetchRow())
3318  {
3319  $Tasks[$Row["TaskId"]] = $Row;
3320  if ($Row["Callback"] ==
3321  serialize(array("ApplicationFramework", "PeriodicEventWrapper")))
3322  {
3323  $WrappedCallback = unserialize($Row["Parameters"]);
3324  $Tasks[$Row["TaskId"]]["Callback"] = $WrappedCallback[1];
3325  $Tasks[$Row["TaskId"]]["Parameters"] = NULL;
3326  }
3327  else
3328  {
3329  $Tasks[$Row["TaskId"]]["Callback"] = unserialize($Row["Callback"]);
3330  $Tasks[$Row["TaskId"]]["Parameters"] = unserialize($Row["Parameters"]);
3331  }
3332  }
3333  return $Tasks;
3334  }
3335 
3339  private function RunNextTask()
3340  {
3341  # lock tables to prevent same task from being run by multiple sessions
3342  $this->DB->Query("LOCK TABLES TaskQueue WRITE, RunningTasks WRITE");
3343 
3344  # look for task at head of queue
3345  $this->DB->Query("SELECT * FROM TaskQueue ORDER BY Priority, TaskId LIMIT 1");
3346  $Task = $this->DB->FetchRow();
3347 
3348  # if there was a task available
3349  if ($Task)
3350  {
3351  # move task from queue to running tasks list
3352  $this->DB->Query("INSERT INTO RunningTasks "
3353  ."(TaskId,Callback,Parameters,Priority,Description) "
3354  ."SELECT * FROM TaskQueue WHERE TaskId = "
3355  .intval($Task["TaskId"]));
3356  $this->DB->Query("DELETE FROM TaskQueue WHERE TaskId = "
3357  .intval($Task["TaskId"]));
3358 
3359  # release table locks to again allow other sessions to run tasks
3360  $this->DB->Query("UNLOCK TABLES");
3361 
3362  # unpack stored task info
3363  $Callback = unserialize($Task["Callback"]);
3364  $Parameters = unserialize($Task["Parameters"]);
3365 
3366  # attempt to load task callback if not already available
3367  $this->LoadFunction($Callback);
3368 
3369  # run task
3370  $this->RunningTask = $Task;
3371  if ($Parameters)
3372  {
3373  call_user_func_array($Callback, $Parameters);
3374  }
3375  else
3376  {
3377  call_user_func($Callback);
3378  }
3379  unset($this->RunningTask);
3380 
3381  # remove task from running tasks list
3382  $this->DB->Query("DELETE FROM RunningTasks"
3383  ." WHERE TaskId = ".intval($Task["TaskId"]));
3384 
3385  # prune running tasks list if necessary
3386  $RunningTasksCount = $this->DB->Query(
3387  "SELECT COUNT(*) AS TaskCount FROM RunningTasks", "TaskCount");
3388  if ($RunningTasksCount > $this->MaxRunningTasksToTrack)
3389  {
3390  $this->DB->Query("DELETE FROM RunningTasks ORDER BY StartedAt"
3391  ." LIMIT ".($RunningTasksCount - $this->MaxRunningTasksToTrack));
3392  }
3393  }
3394  else
3395  {
3396  # release table locks to again allow other sessions to run tasks
3397  $this->DB->Query("UNLOCK TABLES");
3398  }
3399  }
3400 
3406  function OnCrash()
3407  {
3408  # attempt to remove any memory limits
3409  ini_set("memory_limit", -1);
3410 
3411  # if there is a background task currently running
3412  if (isset($this->RunningTask))
3413  {
3414  # add info about error that caused crash (if available)
3415  if (function_exists("error_get_last"))
3416  {
3417  $CrashInfo["LastError"] = error_get_last();
3418  }
3419 
3420  # add info about current output buffer contents (if available)
3421  if (ob_get_length() !== FALSE)
3422  {
3423  $CrashInfo["OutputBuffer"] = ob_get_contents();
3424  }
3425 
3426  # if backtrace info is available for the crash
3427  $Backtrace = debug_backtrace();
3428  if (count($Backtrace) > 1)
3429  {
3430  # discard the current context from the backtrace
3431  array_shift($Backtrace);
3432 
3433  # add the backtrace to the crash info
3434  $CrashInfo["Backtrace"] = $Backtrace;
3435  }
3436  # else if saved backtrace info is available
3437  elseif (isset($this->SavedContext))
3438  {
3439  # add the saved backtrace to the crash info
3440  $CrashInfo["Backtrace"] = $this->SavedContext;
3441  }
3442 
3443  # if we have crash info to recod
3444  if (isset($CrashInfo))
3445  {
3446  # save crash info for currently running task
3447  $DB = new Database();
3448  $DB->Query("UPDATE RunningTasks SET CrashInfo = '"
3449  .addslashes(serialize($CrashInfo))
3450  ."' WHERE TaskId = ".intval($this->RunningTask["TaskId"]));
3451  }
3452  }
3453 
3454  print("\n");
3455  return;
3456  }
3457 
3474  private function AddToDirList($DirList, $Dir, $SearchLast, $SkipSlashCheck)
3475  {
3476  # convert incoming directory to array of directories (if needed)
3477  $Dirs = is_array($Dir) ? $Dir : array($Dir);
3478 
3479  # reverse array so directories are searched in specified order
3480  $Dirs = array_reverse($Dirs);
3481 
3482  # for each directory
3483  foreach ($Dirs as $Location)
3484  {
3485  # make sure directory includes trailing slash
3486  if (!$SkipSlashCheck)
3487  {
3488  $Location = $Location
3489  .((substr($Location, -1) != "/") ? "/" : "");
3490  }
3491 
3492  # remove directory from list if already present
3493  if (in_array($Location, $DirList))
3494  {
3495  $DirList = array_diff(
3496  $DirList, array($Location));
3497  }
3498 
3499  # add directory to list of directories
3500  if ($SearchLast)
3501  {
3502  array_push($DirList, $Location);
3503  }
3504  else
3505  {
3506  array_unshift($DirList, $Location);
3507  }
3508  }
3509 
3510  # return updated directory list to caller
3511  return $DirList;
3512  }
3513 
3521  private function ArrayPermutations($Items, $Perms = array())
3522  {
3523  if (empty($Items))
3524  {
3525  $Result = array($Perms);
3526  }
3527  else
3528  {
3529  $Result = array();
3530  for ($Index = count($Items) - 1; $Index >= 0; --$Index)
3531  {
3532  $NewItems = $Items;
3533  $NewPerms = $Perms;
3534  list($Segment) = array_splice($NewItems, $Index, 1);
3535  array_unshift($NewPerms, $Segment);
3536  $Result = array_merge($Result,
3537  $this->ArrayPermutations($NewItems, $NewPerms));
3538  }
3539  }
3540  return $Result;
3541  }
3542 
3549  private function OutputModificationCallbackShell($Matches)
3550  {
3551  # call previously-stored external function
3552  return call_user_func($this->OutputModificationCallbackInfo["Callback"],
3553  $Matches,
3554  $this->OutputModificationCallbackInfo["Pattern"],
3555  $this->OutputModificationCallbackInfo["Page"],
3556  $this->OutputModificationCallbackInfo["SearchPattern"]);
3557  }
3558 
3565  function UpdateSetting($FieldName, $NewValue = DB_NOVALUE)
3566  {
3567  return $this->DB->UpdateValue("ApplicationFrameworkSettings",
3568  $FieldName, $NewValue, NULL, $this->Settings);
3569  }
3570 
3572  private $InterfaceDirList = array(
3573  "local/interface/%ACTIVEUI%/",
3574  "interface/%ACTIVEUI%/",
3575  "local/interface/default/",
3576  "interface/default/",
3577  );
3582  private $IncludeDirList = array(
3583  "local/interface/%ACTIVEUI%/include/",
3584  "interface/%ACTIVEUI%/include/",
3585  "local/interface/default/include/",
3586  "interface/default/include/",
3587  );
3589  private $ImageDirList = array(
3590  "local/interface/%ACTIVEUI%/images/",
3591  "interface/%ACTIVEUI%/images/",
3592  "local/interface/default/images/",
3593  "interface/default/images/",
3594  );
3596  private $FunctionDirList = array(
3597  "local/interface/%ACTIVEUI%/include/",
3598  "interface/%ACTIVEUI%/include/",
3599  "local/interface/default/include/",
3600  "interface/default/include/",
3601  "local/include/",
3602  "include/",
3603  );
3604 
3605  const NOVALUE = ".-+-.NO VALUE PASSED IN FOR ARGUMENT.-+-.";
3606 }
3607 
3608 
MaxTasks($NewValue=DB_NOVALUE)
Get/set maximum number of tasks to have running simultaneously.
const LOGLVL_ERROR
ERROR error logging level.
GetOrphanedTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
SuppressHTMLOutput($NewSetting=TRUE)
Suppress loading of HTML files.
AddInterfaceDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for user interface (HTML/TPL) files.
AddIncludeDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for user interface include (CSS, JavaScript, common PHP, common HTML, etc) files.
static GetScriptUrl()
Retrieve SCRIPT_URL server value, pulling it from elsewhere if that variable isn't set...
const LOGLVL_INFO
INFO error logging level.
AddUnbufferedCallback($Callback, $Parameters=array())
Add a callback that will be executed after buffered content has been output and that won't have its o...
QueueUniqueTask($Callback, $Parameters=NULL, $Priority=self::PRIORITY_LOW, $Description="")
Add task to queue if not already in queue or currently running.
const LOGLVL_FATAL
FATAL error logging level.
UseMinimizedJavascript($NewSetting=NULL)
Get/set whether or not to check for and use minimized JavaScript files when getting a JavaScript UI f...
AddPostProcessingCall($FunctionName, &$Arg1=self::NOVALUE, &$Arg2=self::NOVALUE, &$Arg3=self::NOVALUE, &$Arg4=self::NOVALUE, &$Arg5=self::NOVALUE, &$Arg6=self::NOVALUE, &$Arg7=self::NOVALUE, &$Arg8=self::NOVALUE, &$Arg9=self::NOVALUE)
Add function to be called after HTML has been loaded.
GetCleanUrlForPath($Path)
Get the clean URL mapped for a path.
const PRIORITY_LOW
Lower priority.
Abstraction for forum messages and resource comments.
Definition: Message.php:15
GetQueuedTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
LogFile($NewValue=NULL)
Get/set log file name.
GetCleanUrl()
Get the clean URL for the current page if one is available.
MaxExecutionTime($NewValue=NULL)
Get/set maximum PHP execution time.
static PreferHttpHost($NewValue=NULL)
Get/set whether to prefer $_SERVER["HTTP_HOST"] (if available) over $_SERVER["SERVER_NAME"] when dete...
RequireUIFile($FileName)
Add file to list of required UI files.
static FullUrl()
Get current full URL, before any clean URL remapping and with any query string (e.g.
Top-level framework for web applications.
static BaseUrl()
Get current base URL (the part before index.php) (e.g.
GetOrphanedTaskCount()
Retrieve current number of orphaned tasks.
SQL database abstraction object with smart query caching.
LogSlowPageLoads($NewValue=DB_NOVALUE)
Get/set whether logging of long page load times is enabled.
SlowPageLoadThreshold($NewValue=DB_NOVALUE)
Get/set how long a page load can take before it should be considered "slow" and may be logged...
GetTaskQueueSize($Priority=NULL)
Retrieve current number of tasks in queue.
const DB_NOVALUE
static RootUrlOverride($NewValue=self::NOVALUE)
Get/set root URL override.
GetQueuedTaskCount($Callback=NULL, $Parameters=NULL, $Priority=NULL, $Description=NULL)
Get number of queued tasks that match supplied values.
static AutoloadObjects($ClassName)
DeleteTask($TaskId)
Remove task from task queues.
const LOGLVL_DEBUG
DEBUG error logging leve.
const EVENTTYPE_NAMED
Named result event type.
const EVENTTYPE_FIRST
First response event type.
static WasUrlRewritten($ScriptName="index.php")
Determine if the URL was rewritten, i.e., the script is being accessed through a URL that isn't direc...
const EVENTTYPE_DEFAULT
Default event type.
GetPageUrl()
Get the full URL to the page.
IsStaticOnlyEvent($EventName)
Report whether specified event only allows static callbacks.
IsRegisteredEvent($EventName)
Check if event has been registered (is available to be signaled).
static AddObjectDirectory($Dir, $Prefix="", $ClassPattern=NULL, $ClassReplacement=NULL)
Add directory to be searched for object files when autoloading.
SignalEvent($EventName, $Parameters=NULL)
Signal that an event has occured.
static BasePath()
Get current base path (usually the part after the host name).
const LOGLVL_TRACE
TRACE error logging level.
const LOGLVL_WARNING
WARNING error logging level.
const PRIORITY_MEDIUM
Medium (default) priority.
LogError($Level, $Msg)
Write error message to log.
OnCrash()
Called automatically at program termination to ensure output is written out.
GetKnownPeriodicEvents()
Get list of known periodic events.
const EVENTTYPE_CHAIN
Result chaining event type.
GetSecondsBeforeTimeout()
Get remaining available (PHP) execution time.
TaskIsInQueue($Callback, $Parameters=NULL)
Check if task is already in queue or currently running.
AddFunctionDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for function ("F-") files.
LoggingLevel($NewValue=DB_NOVALUE)
Get/set logging level.
const ORDER_MIDDLE
Run hooked function after ORDER_FIRST and before ORDER_LAST events.
PHP
Definition: OAIClient.php:39
RegisterEvent($EventsOrEventName, $EventType=NULL)
Register one or more events that may be signaled.
SetJumpToPage($Page, $IsLiteral=FALSE)
Set URL of page to autoload after PHP page file is executed.
GetPageName()
Get name of page being loaded.
CleanUrlIsMapped($Path)
Report whether clean URL has already been mapped.
const PRIORITY_HIGH
Highest priority.
LoadFunction($Callback)
Attempt to load code for function or method if not currently available.
GetRunningTaskList($Count=100, $Offset=0)
Retrieve list of tasks currently in queue.
FindCommonTemplate($BaseName)
Preserved for backward compatibility for use with code written prior to October 2012.
EventWillNextRunAt($EventName, $Callback)
Get date/time a periodic event will next run.
static SessionLifetime($NewValue=NULL)
Get/set session timeout in seconds.
HookEvent($EventsOrEventName, $Callback=NULL, $Order=self::ORDER_MIDDLE)
Hook one or more functions to be called when the specified event is signaled.
IsHookedEvent($EventName)
Check if an event is registered and is hooked to.
static GetFreeMemory()
Get current amount of free memory.
AddImageDirectories($Dir, $SearchLast=FALSE, $SkipSlashCheck=FALSE)
Add additional directory(s) to be searched for image files.
HtmlCharset($NewSetting=NULL)
Get/set HTTP character encoding value.
const ORDER_FIRST
Run hooked function first (i.e.
GetUncleanUrlForPath($Path)
Get the unclean URL for mapped for a path.
TemplateLocationCacheExpirationInterval($NewInterval=DB_NOVALUE)
Get/set UI template location cache expiration period in minutes.
GetUncleanUrl()
Get the unclean URL for the current page.
RecordContextInCaseOfCrash($BacktraceOptions=0, $BacktraceLimit=0)
Record the current execution context in case of crash.
JumpToPageIsSet()
Report whether a page to autoload has been set.
const ORDER_LAST
Run hooked function last (i.e.
UseBaseTag($NewValue=NULL)
Get/set whether or not to use the "base" tag to ensure relative URL paths are correct.
static HtaccessSupport()
Determine if .htaccess files are enabled.
UpdateSetting($FieldName, $NewValue=DB_NOVALUE)
Convenience function for getting/setting our settings.
GetTask($TaskId)
Retrieve task info from queue (either running or queued tasks).
TaskExecutionEnabled($NewValue=DB_NOVALUE)
Get/set whether automatic task execution is enabled.
static GetPhpMemoryLimit()
Get PHP memory limit in bytes.
LoadPage($PageName)
Load page PHP and HTML/TPL files.
AddEnvInclude($FileName)
Add file to be included to set up environment.
ReQueueOrphanedTask($TaskId, $NewPriority=NULL)
Move orphaned task back into queue.
GUIFile($FileName)
Search UI directories for specified image or CSS file and return name of correct file.
QueueTask($Callback, $Parameters=NULL, $Priority=self::PRIORITY_LOW, $Description="")
Add task to queue.
GetUserInterfaces()
Get the list of available user interfaces.
PUIFile($FileName)
Search UI directories for specified image or CSS file and print name of correct file.
LogHighMemoryUsage($NewValue=DB_NOVALUE)
Get/set whether logging of high memory usage is enabled.
ActiveUserInterface($UIName=NULL)
Get/set name of current active user interface.
GetPageLocation()
Get the URL path to the page without the base path, if present.
ObjectLocationCacheExpirationInterval($NewInterval=DB_NOVALUE)
Get/set object file location cache expiration period in minutes.
GetElapsedExecutionTime()
Get time elapsed since constructor was called.
SetBrowserDetectionFunc($DetectionFunc)
Specify function to use to detect the web browser type.
static RootUrl()
Get portion of current URL through host name, with no trailing slash (e.g.
AddCleanUrl($Pattern, $Page, $GetVars=NULL, $Template=NULL)
Add clean URL mapping.
HighMemoryUsageThreshold($NewValue=DB_NOVALUE)
Get/set what percentage of max memory (set via the memory_limit PHP configuration directive) a page l...
const PRIORITY_BACKGROUND
Lowest priority.
LogMessage($Level, $Msg)
Write status message to log.