0 Replies - 1160 Views - Last Post: 10 June 2010 - 10:19 AM

#1 Dormilich  Icon User is offline

  • 痛覚残留
  • member icon

Reputation: 4140
  • View blog
  • Posts: 13,091
  • Joined: 08-June 10

XHTML Header Script

Posted 10 June 2010 - 10:19 AM

Description: This script requires PHP 5. Create a class instance, change the settings as desired and echo the object out before any output. the HTML output will contain the opening <html> and tags as well as some MIME related tags. The object will write the correct (X)HTML headers for any supported HTML set (HTML 4.01/5, XHTML 1.0/1.1/5/…) along with sending the appropriate HTTP 1.1 headers. If the User Agent does not support XHTML (all versions of IE), it will fallback to HTML 4. You may also choose to deny serving the content, if the requesting UA doesn’t support HTML.
, ,  #
###########################################################

/**
 * Based on the Function by Dave Shea.
 *
 * due to IE's lack of support for "application/xhtml+xml" (XHTML 1.1
 * recommended MIME type) a browser switch (XHTML/HTML) is made. All
 * browsers accepting "application/xhtml+xml" will be served with the 
 * default output for all others the output will be buffered and
 * corrected towards HTML 4.01.
 *   XHTML Coders: empty elements that are not empty or don't have an
 * optional closing tag in HTML are thus likely to inflict problems.
 *
 * How to set the options
 * ======================
 * there are 5 options available:
 * - Document MIME type (HTML 4.01, HTML 5, XHTML 1.0, XHTML 1.1, XHTML)
 * - Document language
 * - Document encoding (character set)
 * - Script language MIME type
 * - Stylesheet MIME type (currently disabled)
 * - Denial to Serve (if the MIME type is not supported)
 *
 * of which the first 3 can be set using the class' constructor, but all
 * of them can be set later as they are public properties. Should you choose
 * to take the 'XHTML' MIME type, you need to set the DTD string beforehand.
 * This will be required, when you need to use 'mixed' XHTML types, such as
 * XHTML+SVG, XHTML+MathML, XHTML+RDFa, etc.
 *   You can choose to deny serving XHTML to User Agents (UA) that do not
 * support the set MIME type by setting $errorCode to a HTTP Client Error
 * Code (4xx). A denied document does not contain any data unless you provide
 * an error document, so UA don't display any message. However you can read
 * the HTTP Response Headers. You can also forcibly deny a document by calling
 * the deny() method.
 *   Note that XHTML 1.1 can only be served using "application/xhtml+xml".
 */
class Headers
{
	/**
	 * @const (string) DTD_XHTML_10      XHTML 1.0 Strict DTD
	 * @const (string) DTD_XHTML_11      XHTML 1.1 DTD
	 * @const (string) DTD_HTML_401      HTML 4.01 Strict DTD
	 * @const (string) DTD_HTML_5        HTML 5 DTD
	 * @const (string) XMLNS_XHTML       XHTML namespace
	 * @const (string) META_TYPE_CSS     meta element defining the default
	 *                                   style MIME type
	 * @const (string) META_TYPE_JS      meta element defining the default
	 *                                   script MIME type
	 * @const (string) MIME_TYPE_CSS     MIME type for CSS files
	 * @const (string) MIME_TYPE_JS      MIME type for Javascript files
	 * @const (string) MIME_TYPE_XML     MIME type for XML text files
	 * @const (string) MIME_TYPE_HTML    MIME type for HTML files
	 * @const (string) MIME_TYPE_XHTML   MIME type for XHTML files
	 * @var (array) HTTP_ERROR_MSG       HTTP Error Codes 4xx series (RFC 2616)
	 * @var (string) $mime               default page MIME type
	 * @var (string) $mime_js            Javascript MIME type (changeable)
	 * @var (string) $mime_css           CSS MIME type (changeable)
	 * @var (string) $type               default page type ((X)HTML),
	 *                                   may be edited by the user
	 * @var (string) $encoding           page encoding/charset, 
	 *                                   may be edited by the user
	 * @var (string) $language           default page language,
	 *                                   may be edited by the user
	 * @var (string) $dtd                used for a custom DTD
	 * @var (mixed) $errorCode           HTTP Error Code to send or false
	 *                                   for XHTML -> HTML conversion
	 */
	const DTD_XHTML_10    = '';
	const DTD_XHTML_11    = '';
	const DTD_HTML_401    = '';
	const DTD_HTML_5      = '';

	const XMLNS_XHTML     = 'xmlns="http://www.w3.org/1999/xhtml"';

	const META_TYPE_CSS   = '';
	const META_TYPE_JS    = '';

	const MIME_TYPE_JS    = 'text/javascript';
	const MIME_TYPE_CSS   = 'text/css';
	const MIME_TYPE_XML   = 'text/xml';
	const MIME_TYPE_HTML  = 'text/html';
	const MIME_TYPE_XHTML = 'application/xhtml+xml';

	protected static $HTTP_ERROR_MSG = array(
		400 => "Bad Request",
		401 => "Unauthorized",
		402 => "Payment Required",
		403 => "Forbidden",
		404 => "Not Found",
		405 => "Method Not Allowed",
		406 => "Not Acceptable",
		407 => "Proxy Authentication Required",
		408 => "Request Timeout",
		409 => "Conflict",
		410 => "Gone",
		411 => "Length Required",
		412 => "Precondition Failed",
		413 => "Request Entity Too Large",
		414 => "Request-URI Too Long",
		415 => "Unsupported Media Type",
		416 => "Requested Range Not Satisfiable",
		417 => "Expectation Failed"
	);

	public 
		$mime      = 'text/html',
		$mime_js   = 'text/javascript',
		$mime_css  = 'text/css',
		$type      = 'XHTML 1.0',
		$encoding  = 'UTF-8',
		$language  = 'de',
		$dtd       = '',
		$errorCode = false;
	
	/**
	 * determine the supported MIME type. set defaults.
	 *
	 * @param (string) $encoding   encoding/charset of the document [UTF-8]
	 * @param (string) $lang       used language [de]
	 * @param (string) $type       used DTD/HTML version [XHTML 1.1]
	 * @return (void)
	 */
	public function __construct(
		$encoding = 'UTF-8',
		$lang     = 'en',
		$type     = 'XHTML 1.0'
	)
	{
		$this->type     = $type;
		$this->encoding = $encoding;
		$this->language = $lang;
		# set defaults
		$this->mime_css = self::MIME_TYPE_CSS;
		$this->mime_js  = self::MIME_TYPE_JS;
		# determine possible MIME type for XHTML
		if (!$this->specialUA())
		{
			$this->changeMIME();
		}
	}
	
	/**
	 * setter method for the encoding/charset property.
	 *
	 * @return (Headers)           instance for chaining
	 */
	public function setEncoding(
		$charset = 'UTF-8'
	)
	{
		$this->encoding = $charset;
		return $this;
	}
	
	/**
	 * setter method for language property.
	 *
	 * @return (Headers)           instance for chaining
	 */
	public function setLanguage(
		$lang = 'de'
	)
	{
		$this->language = $lang;
		return $this;
	}
	
	/**
	 * setter method for Doctype identifyer.
	 *
	 * @return (Headers)           instance for chaining
	 */
	public function setDocType(
		$type = 'XHTML 1.0'
	)
	{
		$this->type = strtoupper($type);
		return $this;
	}

	/**
	 * Send a HTTP Status Code of the 4xx series (Client Errors) to the client.
	 *
	 * @param (int) $code          HTTP Error Code Number
	 * @throws (CodeException)     not a Client Error Code
	 */
	public static function deny(
		$code = 406,
		$page = false
	)
	{
		if (!isset(self::$HTTP_ERROR_MSG[$code]))
		{
			$emsg = "Only Client Error HTTP Status Codes supported.";
			throw new CodeException(15, 0, __METHOD__, $emsg);
		}
		header("HTTP/1.1 $code " . self::$HTTP_ERROR_MSG[$code]);
		header("Status: $code "  . self::$HTTP_ERROR_MSG[$code]);
		if (file_exists($page))
		{
			header("Location: $page");
		}
		else
		{
			header("Connection: Close");
		}
		exit;
	}
	
	/**
	 * send PHP headers and print the (X)HTML headers. if MIME type is set to
	 * HTML, start the output correction (XHTML -> HTML) via output buffer.
	 *
	 * @return (string)            HTML headers
	 */
	public function __toString()
	{
		# reset MIME type for HTML documents
		if (strpos($this->type, "HTML") === 0)
		{
			$this->mime = self::MIME_TYPE_HTML;
			# otherwise leave it at XHTML
		}
		# convenience variable
		$_isXHTML = is_int(strpos($this->type, 'XHTML'));
		
		# don't serve XHTML 1.1 to UAs that don't know "application/xhtml+xml"
		if ('XHTML 1.1' == $this->type and self::MIME_TYPE_XHTML != $this->mime)
		{
			self::deny(415);
		}
		# forcibly don't serve XHTML to UAs that don't know "application/xhtml+xml"
		if ($_isXHTML and self::MIME_TYPE_XHTML != $this->mime and is_int($this->errorCode))
		{
			self::deny($this->errorCode);
		}
		# remove empty xml tag notation ("/>" -> ">")
		if ($_isXHTML and self::MIME_TYPE_HTML == $this->mime)
		{
			ob_start(array($this, "fix_code"));
		}
		# send headers
		header("Content-Type: " . $this->mime . ";charset=" . $this->encoding);
		header("Vary: Accept");

		# choose page type from MIME type
		if (self::MIME_TYPE_HTML == $this->mime)
		{
			$type = 'HTML 4.01';
		}
		else
		{
			$type = $this->type;
		}
		return $this->getHeader($type);
	}
	
	/**
	 * read the q-value (MIME type weighting) from HTTP_ACCEPT and set
	 * the according MIME type. if support for XHTML is detected, change
	 * the MIME type from HTML (default) to XHTML.
	 *
	 * @return (void)
	 */
	protected function changeMIME()
	{
		$preg_xhtml = "@application/xhtml+xml;q=0(.[1-9]+)@i";
		$preg_html  = "@text/html;q=0(.[1-9]+)@i";
		
		# close connection for non-browsers (no Accept header)
		if (!isset($_SERVER["HTTP_ACCEPT"]))
		{
			self::deny(406);
		}
		// change
		# match without q-value
		if (stristr($_SERVER["HTTP_ACCEPT"], self::MIME_TYPE_XHTML)) 
		{
			# match with q-value
			if (preg_match($preg_xhtml, $_SERVER["HTTP_ACCEPT"], $matches)) 
			{
				# compare html/xhtml from q-value
				$xhtml_q = $matches[1];
				if (preg_match($preg_html, $_SERVER["HTTP_ACCEPT"], $matches)) 
				{
					$html_q = $matches[1];
					if ($xhtml_q >= $html_q) 
					{
						$this->mime = self::MIME_TYPE_XHTML;
					}
				}
			}
			else 
			{
				$this->mime = self::MIME_TYPE_XHTML;
			}
		}
	}
	
	/**
	 * correct the MIME type for some special User Agents that are not
	 * correctly recognized by self::changeMIME() or which contain bugs
	 * that would lead to errors.
	 *
	 * @return (bool)              HTTP_USER_AGENT available
	 */
	protected function specialUA()
	{
		if (!isset($_SERVER["HTTP_USER_AGENT"]))
		{
			return true;
		}
		# special check for the W3C_Validator
		if (stristr($_SERVER["HTTP_USER_AGENT"], "W3C_Validator")) 
		{
			$this->mime = self::MIME_TYPE_XHTML;
			return true;
		}
		return false;
	}
	
	/**
	 * build the headers (from XML Prolog to s) according to the
	 * set MIME type. 
	 *
	 * @param (string) $type       MIME type
	 * @return (string)            headers formatted for printing
	 */
	public function getHeader(
		$type = 'XHTML'
	)
	{
		# XML Prolog
		$xml = 'encoding . "" ?>n";
		$meta = '
	
		';
		$ie = '';
		
		switch ($type)
		{
			case 'XHTML 1.1':
				# DTD
				$dtd  = self::DTD_XHTML_11 . "n";
				# <html>
				$html = '<html ' 
				      . self::XMLNS_XHTML 
				      . ' xml:lang="' . $this->language . '">';
				break;

			case 'XHTML 1.0':
				# DTD
				$dtd  = self::DTD_XHTML_10 . "n";
				# <html>
				$html = '<html ' 
				      . self::XMLNS_XHTML 
				      . ' xml:lang="' . $this->language . '"'
				      . ' lang="' . $this->language . '">';
				break;

			// XHTML special types like XHTML+SVG, XHTML+MathML, etc.
			case 'XHTML': 
				# DTD
				$dtd = $this->dtd;
				# <html>
				$html = '<html ' 
				      . self::XMLNS_XHTML 
				      . ' xml:lang="' . $this->language . '">';
				break;
			
			case 'XHTML 5': // same as HTML5, only served as XHTML
				# DTD
				$dtd = '';
				# <html>
				$html = '<html ' 
				      . self::XMLNS_XHTML 
				      . ' xml:lang="' . $this->language . '">';
				break;
			
			case 'HTML 5':
				# no XML prolog
				$xml = '';
				# DTD
				$dtd = self::DTD_HTML_5 . "n";;
				# <html>
				$html = '<html lang="' . $this->language . '">';
				$ie = '
';
				break;
			
			case 'HTML 4.01':
			default:
				# no XML prolog
				$xml = '';
				# DTD
				$dtd  = self::DTD_HTML_401 . "n";
				# <html>
				$html = '<html lang="' . $this->language . '">';
				# 
				break;
		}
		
		$header = $xml . $dtd . $html . $meta 
		        . "ntt" . self::META_TYPE_CSS 
		        . "ntt" . sprintf(self::META_TYPE_JS,  $this->mime_js)
		        . $ie;
		return $header;
	}

	/**
	 * convert XHTML to HTML (basicly treating the empty elements)
	 *
	 * @param (string) $buffer     content from the output buffer
	 * @return (string)            HTML code to send to the browser
	 */
	public function fix_code($buffer) 
	{
		return (str_replace("/>", ">", $buffer));
	}
}	 



Is This A Good Question/Topic? 0
  • +

Page 1 of 1