diff InputParser.inc.php @ 134:b6b4a58c7625

Using .inc.php rather than just .inc for include files.
author Tom Fredrik Blenning <bfg@bfgconsult.no>
date Sun, 22 Jan 2023 19:22:00 +0100
parents InputParser.inc@ee5f98a0bc93
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/InputParser.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,368 @@
+<?php
+/// @cond
+$baseDir = dirname(__FILE__);
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->includeOnce('Page.inc.php', dirname(__FILE__));
+/// @endcond
+
+/**
+ * Functionality for translating an XML configuration document into a webpage
+ */
+class InputParser extends Page
+{
+  private $options;
+  private $master;
+
+  /**
+   * Constructs a new InputParser object
+   *
+   * @param $name name of file to read configuration from
+   *
+   * @param $masterCache CacheTimeCheck cache object to use as parent
+   * for this inputParsercache
+   */
+  function __construct($name, $masterCache) {
+    $this->master = new DOMDocument();
+    $cache = new CacheTimeCheck($name);
+    $cache->addParent($masterCache);
+    parent::setCache($cache);
+    $this->master->load($name);
+
+    $this->options = new Options($this->master);
+    $this->options->setCache($cache);
+    $base=basePath();
+    $base=$_SERVER['DOCUMENT_ROOT'];
+    $this->options->setBasePath($base);
+    $this->options->setCacheable(true);
+
+    $this->options->setUrlParams(array('name', 'lang'));
+
+    if(array_key_exists('lang', $_GET) && $_GET['lang']) {
+      $this->options->setLang($_GET['lang']);
+    }
+    else {
+      if (count($this->options->getAcceptedLanguages()) > 1)
+	$this->addVariant('Accept-Language');
+      $this->options->setLang($this->options->getDefaultLang());
+    }
+
+    if(array_key_exists('name', $_GET) && $_GET['name']) {
+      $this->options->setName($_GET['name']);
+    }
+    else {
+      $pathToAnalyze=$_SERVER['PATH_TRANSLATED'];
+      $prefix=$_SERVER['SCRIPT_FILENAME'];
+      if (substr($pathToAnalyze, 0, strlen($prefix)) == $prefix) {
+	$pathToAnalyze = substr($pathToAnalyze, strlen($prefix));
+      }
+      preg_match('/\/([^\/]*)$/', $pathToAnalyze, $groups);
+      if ($groups[1]) {
+	$this->options->setName($groups[1]);
+      }
+    }
+
+
+    $params = $this->master->getElementsByTagName("param");
+
+    foreach ($params as $param) {
+      if ($param->getAttribute("type") == "input") {
+	$doc = self::getInput($this->master, $param, $this->options);
+
+	$parent = $param->parentNode;
+	foreach ($doc->firstChild->childNodes as $child) {
+	  $clonedChild = $this->master->importNode($child, true);
+	  $parent->insertBefore($clonedChild, $param);
+	}
+	$parent->removeChild($param);
+      }
+    }
+    $this->master = self::getFiles($this->master, $this->options);
+    self::removeCommentsFromDOM($this->master);
+    $cache->setMaxAge(0);
+  }
+
+  /**
+   * Removes all comments from the DOM
+   *
+   * @param $dom A DOMDocument for processing
+   */
+  static function removeCommentsFromDOM($dom)
+  {
+    $xpath = new DOMXPath($dom);
+    foreach ($xpath->query('//comment()') as $comment) {
+      $comment->parentNode->removeChild($comment);
+    }
+  }
+
+  function cacheCheck()
+  {
+    return $this->options->getCacheable();
+  }
+
+  /**
+   * Generate an appropriate response for this page, eg. 302 NOT
+   * MODIFIED or the actual page
+   */
+  function generateContent()
+  {
+    //We may need to set and check the contenttype and replace
+    //saveHTML with saveXML
+    $retVal = new PageContent(options::replacePlaceholders($this->master->saveXML()));
+    $retVal->setHeader('Content-Language', $this->options->getLang());
+    return $retVal;
+  }
+
+  /**
+   * Extracts data from a @<param@> tag.
+   *
+   * @param $param the param tag
+   *
+   * @return if the type is array, return an array, otherwise return a
+   * scalar
+   */
+  static function getParam($param)
+  {
+    $param_type = $param->getAttribute("type");
+    $param_value;
+    if (! $param_type)
+      $param_type = "scalar";
+
+    if($param_type == "scalar") {
+      $param_subst = $param->getAttribute("subst");
+      $param_value = $param->getAttribute("value");
+      if ($param_subst) {
+	/*
+	  $param_value=preg_replace("/name/", $name, $param_subst);
+	  $param_value=preg_replace('/lang/', $lang, $param_value);
+	*/
+      }
+    }
+    elseif($param_type == "array") {
+      $params = $param->getElementsByTagName("param");
+      $param_value = array();
+      foreach ($param->childNodes as $param) {
+	if ($param->nodeType == XML_ELEMENT_NODE) {
+	  array_push($param_value, self::getParam($param));
+	}
+      }
+    }
+    else {
+      throw new UnexpectedValueException("Unknown parameter type '$param_type'");
+    }
+    return $param_value;
+  }
+
+  /**
+   * This is the last processing stage for generating a file.
+   *
+   * @param $doc the document to be worked upon
+   * @param $options an Options object for this file.
+   *
+   * @return This is the same as the input document, fully processed
+   */
+  static function getFiles($doc, $options) {
+    $lang = $options->getLang();
+    $conf = $options->getName();
+
+    $toRemove = array();
+
+    $topLevelTags = $doc->getElementsByTagName("toplevel");
+    foreach ($topLevelTags as $topLevel) {
+      $topLevel->parentNode->removeChild($topLevel);
+    }
+
+    $valueDict = array();
+    $fragments = array();
+    $setters = $doc->getElementsByTagName("set");
+    foreach ($setters as $setTag) {
+      $key = $setTag->getAttribute("id");
+      $type = $setTag->getAttribute("type");
+      if ($type == "fragment") {
+	$fragments[$key] = $setTag;
+      }
+      else {
+	$value = $setTag->getAttribute("value");
+	if ($key && $value) {
+	  $valueDict[$key] = $value;
+	}
+      }
+      //We need to iterate in the opposite direction when removing,
+      //so best shifting.
+      array_unshift($toRemove, $setTag);
+    }
+
+    $params = $doc->getElementsByTagName("param");
+    foreach ($params as $param) {
+      if ($param->getAttribute("type")=="input_config") {
+	$id = $param->getAttribute("id");
+	if (array_key_exists($id, $valueDict)) {
+	  $value = $valueDict[$id];
+	  $tmp = new DOMDocument();
+
+	  $tmp->loadXml("<xml>${value}</xml>");
+	  $parent = $param->parentNode;
+	  $parent->insertBefore(new DOMText($tmp->textContent), $param);
+	  //We need to iterate in the opposite direction when removing,
+	  //so best shifting.
+	  array_unshift($toRemove, $param);
+	}
+	elseif (array_key_exists($id, $fragments)) {
+	  $fragment = $fragments[$id];
+
+	  $cloneFragment = $fragment->cloneNode(true);
+	  $insNode = $param;
+	  for ($i = $cloneFragment->childNodes->length - 1; $i >= 0; $i--) {
+	    $child = $cloneFragment->childNodes->item($i);
+	    $child = $child->parentNode->removeChild($child);
+	    $insNode = $insNode->parentNode->insertBefore($child, $insNode);
+	  }
+
+	  //We need to iterate in the opposite direction when removing,
+	  //so best shifting.
+	  array_unshift($toRemove, $param);
+	}
+      }
+    }
+
+    foreach($toRemove as $param) {
+      $parent = $param->parentNode;
+      $parent->removeChild($param);
+    }
+
+    $body = getElementByTagName($doc,"html");
+    $files = $body->getElementsByTagName("file");
+
+    $toRemove = array();
+
+    foreach ($files as $file) {
+      $script = $file->getAttribute("script");
+      if ($script) {
+	$options->setCacheable(false);
+	$src="";
+	$cwd = getcwd();
+
+	$matches=array();
+	preg_match('/(.*\/)/', $script, $matches);
+	$dirname=$matches[0];
+	preg_match('/([^\/]*)$/', $script, $matches);
+	$filename=$matches[0];
+	chdir("${lang}/${dirname}");
+	$pipe=popen("php ${filename}","r");
+	$file_content = stream_get_contents($pipe);
+	chdir("${cwd}");
+      }
+      else {
+	$src = $file->getAttribute("src");
+	$fname = $options->getBasePath() . "/${lang}/${src}";
+	if (!file_exists($fname) ) {
+	  $fname = $options->getBasePath() . "/common/${src}";
+	}
+	$file_content = $options->getCache()->loadFile($fname);
+      }
+      if(floatval($file_content)<0) {
+	errorPage("Resource not found '${lang}/${src}'");
+      }
+
+      $filters = $file->getElementsByTagName("filter");
+      foreach($filters as $filter) {
+	$func = $filter->getAttribute("function");
+	$params = $filter->getElementsByTagName("param");
+	$callString = "\$file_content = ${func}(\$file_content, \$options";
+	$param_values = array();
+	$i = 0;
+	foreach ($filter->childNodes as $param) {
+	  if ($param->nodeType == XML_ELEMENT_NODE)
+	    {
+	      $param_value[$i] = self::getParam($param);
+	      $callString .= ",\$param_value[$i]";
+	      $i++;
+	    }
+	}
+	$callString .= ");";
+	eval($callString);
+      }
+      $ndoc = $doc->createDocumentFragment();
+
+      $parent = $file->parentNode;
+
+      $ndoc->appendXML($file_content);
+      $parent->insertBefore($ndoc, $file);
+
+      //We need to iterate in the opposite direction when removing,
+      //so best shifting.
+      array_unshift($toRemove, $file);
+    }
+    foreach($toRemove as $param) {
+      $parent = $param->parentNode;
+      $parent->removeChild($param);
+    }
+
+    return $doc;
+  }
+
+  /**
+   * Follows all include directives recursively for the specified
+   * $param an generates an xml file.
+   *
+   * This function may be used to generate a file which has all the
+   * necessary information to determine wether or not we may cache.
+   *
+   * @param $master The master document to be processed
+   * @param $param the input tag to resolve
+   * @param $options the options object for this file
+   */
+  function getInput($master, $param, $options)
+  {
+    $lang = $options->getLang();
+    $name = $param->getAttribute("id");
+    $conf = $options->getName();
+    if (!$conf)
+      $conf = $param->getAttribute("default");
+
+    $confFile = $options->getBasePath() . "/${lang}/${conf}.xml";
+    if (! file_exists($confFile) ) {
+      $confFile = $options->getBasePath() . "/common/${conf}.xml";
+    }
+
+    $options->getCache()->cache_time($confFile);
+    $doc = new DOMDocument();
+    $doc->load($confFile);
+
+    $toplevel = $doc->getElementsByTagName("toplevel");
+
+    if(! $toplevel->length) {
+      errorPage("Resource '${conf}' is not available", 500);
+    }
+
+    $includes = $doc->getElementsByTagName("include");
+    $recurse = 0;
+
+    while($includes->length > 0) {
+      if(++$recurse > MAX_RECURSE) {
+	errorPage('Recursion limit exceeded', 500);
+      }
+      foreach ($includes as $include) {
+	$src = $include->getAttribute("src");
+	$subdoc = new DOMDocument();
+	$subfile = $options->getBasePath() . "/${lang}/${src}";
+	if (! file_exists($subfile) ) {
+	  $subfile = $options->getBasePath() . "/common/${src}";
+	}
+	$subdoc->load("$subfile");
+	$options->getCache()->cache_time($subfile);
+	$parent = $include->parentNode;
+	$xml = getElementByTagName($subdoc,"xml");
+	foreach($xml->childNodes as $child) {
+	  $text = $subdoc->saveHTML($child);
+	  $clonedChild = $doc->importNode($child,true);
+	  $parent->insertBefore($clonedChild,$include);
+	}
+	$parent->removeChild($include);
+      }
+      $includes = $doc->getElementsByTagName("include");
+    }
+
+    return $doc;
+  }
+}
+?>