view Page.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 Page.inc@df158368051e
children
line wrap: on
line source

<?php
include_once 'ScriptIncludeCache.inc.php';

/// @cond
$baseDir = dirname(__FILE__);
$cache = ScriptIncludeCache::instance(__FILE__);
$cache->includeOnce('OnlineBufferValidator.inc.php', $baseDir);
$cache->includeOnce('Options.inc.php', $baseDir);
/// @endcond

/**
 * Container for headers and content, used for compounding these
 */
class PageContent
{
  /// The headers for the page
  public $headers = array();
  /// The content of the page
  public $content;

  /**
   * Constructs a PageContent object containing only content
   *
   * @param $content The content of this page
   */
  function __construct($content = "")
  {
    $this->content = $content;
  }

  /**
   * Sets a header
   *
   * @param $headername Name of the header
   * @param $value Value for the header
   */
  function setHeader($headername, $value)
  {
    $this->headers[$headername] = $value;
  }

  /**
   * Returns a string representation of this object, containing only
   * the content
   *
   * @return the content of the page
   */
  function __toString()
  {
    return $this->content;
  }
}

/**
 * Master class for generating a page
 */
abstract class Page
{
  private $cache;
  private $variants = array();

  function addVariant($value)
  {
    array_push($this->variants, $value);
  }

  function getVariants()
  {
    return $this->variants;
  }

  /**
   * Constructs a page
   *
   * @param $cache optionally sets a cache
   */
  function __construct($cache = null)
  {
    $this->setCache($cache);
  }

  /**
   * Set the cache
   *
   * @param $cache The cache object
   */
  protected function setCache($cache)
  {
    $this->cache = $cache;
  }

  /**
   * Get the cache
   *
   * @return The cache object
   */
  protected function getCache()
  {
    return $this->cache;
  }

  /**
   * Decide wether or not this page may be compressed.
   *
   * Normally this is a check for http headers, but some pages
   * eg. pictures may not want to be compressed, and may override this
   * function.
   *
   * @return bool if this page may be compressed
   */
  function mayCompress()
  {
    if (COMPRESSION_DISABLED)
      return false;
    //We want to add the variant even if we don't serve with compression.
    $this->addVariant('Accept-Encoding');
    if (!array_key_exists('HTTP_ACCEPT_ENCODING', $_SERVER))
      return false;
    return (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip'));
  }

  /**
   * Decide wether or not this page may be validated.
   *
   * Normally this is a check for the option novalidate, but this may
   * be overridden
   *
   * @return bool if this page may be validated
   */
  function mayValidate()
  {
    if (!VALIDATE)
      return false;
    if (array_key_exists('novalidate', $_GET))
      return !$_GET['novalidate'];
    if (!array_key_exists('HTTP_USER_AGENT', $_SERVER))
      return false;
    //UserAgent should be W3C_Validator/1.3
    return !startswith($_SERVER['HTTP_USER_AGENT'], 'W3C');
  }


  /**
   * Turns on compression for this page
   *
   * @note This may not be reversed
   */
  function startCompression()
  {
    ob_start("ob_gzhandler");
  }

  /**
   * Generates the actual content of the page
   *
   * @return the content buffer
   */
  abstract function generateContent();

  /**
   * Finishes all necessary processing to determine the cacheset of this page.
   *
   * @return bool if this page may be cached
   */
  abstract function cacheCheck();

  /**
   * Generates an appropriate response to the request.
   *
   * Eg. 304 NOT CHANGED, error message or the actual content
   */
  function genPage()
  {
    $cacheable = $this->cacheCheck();
    if ($cacheable == Cacheable::YES) {
      if (!CheckHttpModified($this->cache))
	return false;
    }
    $res = $this->generateContent();
    if ($cacheable == Cacheable::UNDETERMINED) {
      $cacheable = $this->cacheCheck();
      if ($cacheable == Cacheable::YES) {
	if (!CheckHttpModified($this->cache))
	  return false;
      }
    }

    if ($this->mayValidate()) {
      /*
      $request = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
      $validator = new OnlineUriValidator($request);
      */
      $validator = new OnlineBufferValidator($res);
      if (!$validator->check())
	throw new LogicException('The page could be generated, but contained errors');
    }
    if ($this->mayCompress()) {
      $this->startCompression();
    }
    $t = gettype($res);
    if ($t === "string") {
      $res = new PageContent($res);
    }
    elseif (get_class($res) !== "PageContent") {
      throw new InvalidArgumentException("generateContent returned an unexpected type");
    }
    if ($variants = $this->getVariants()) {
      $res->setHeader('Vary', join(",", $variants));
    }
    return $res;
  }

  /**
   * Displays the result from genPage.
   *
   * Printing headers and content.
   */
  function display()
  {
    $res = $this->genPage();
    if ($res) {
      foreach ($res->headers as $header => $value) {
	header("${header}: ${value}");
      }
      print $res;
    }
  }


}
?>