2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 // +----------------------------------------------------------------------+
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997 - 2003 The PHP Group |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 3.0 of the PHP license, |
9 // | that is bundled with this package in the file LICENSE, and is |
10 // | available at through the world-wide-web at |
11 // | http://www.php.net/license/3_0.txt. |
12 // | If you did not receive a copy of the PHP license and are unable to |
13 // | obtain it through the world-wide-web, please send a note to |
14 // | license@php.net so we can mail you a copy immediately. |
15 // +----------------------------------------------------------------------+
16 // | Authors: Adam Daniel <adaniel1@eesus.jnj.com> |
17 // | Klaus Guenther <klaus@capitalfocus.org> |
18 // +----------------------------------------------------------------------+
22 require_once 'PEAR.php';
23 require_once 'HTML/Common.php';
24 // HTML/Page/Doctypes.php is required in _getDoctype()
27 * Base class for XHTML pages
29 * This class handles the details for creating a properly constructed XHTML page.
30 * Page caching, stylesheets, client side script, and Meta tags can be
31 * managed using this class.
33 * The body may be a string, object, or array of objects or strings. Objects with
34 * toHtml() and toString() methods are supported.
43 * // the default doctype is XHTML 1.0 Transitional
44 * // All doctypes and defaults are set in HTML/Page/Doctypes.php
45 * $p = new HTML_Page();
48 * $p->addBodyContent("<p>some text</p>");
54 * Complex XHTML example:
55 * ----------------------
57 * // The initializing code can also be in in the form of an HTML
58 * // attr="value" string.
59 * // Possible attributes are: charset, lineend, tab, doctype, language and cache
61 * $p = new HTML_Page(array (
63 * // Sets the charset encoding
65 * 'charset' => 'utf-8',
67 * // Sets the line end character
68 * // unix (\n) is default
69 * 'lineend' => 'unix',
71 * // Sets the tab string for autoindent
72 * // tab (\t) is default
75 * // This is where you define the doctype
76 * 'doctype' => "XHTML 1.0 Strict",
78 * // Global page language setting
81 * // If cache is set to true, the browser may
82 * // cache the output. Else
88 * // Set the page title
89 * $p->setTitle("My page");
91 * // Add optional meta data
92 * $p->addMetaData("author", "My Name");
94 * // Put something into the body
95 * $p->addBodyContent = "<p>some text</p>";
97 * // If at some point you want to clear the page content
98 * // and output an error message, you can easily do that
99 * // See the source for {@link toHtml} and {@link _getDoctype}
100 * // for more details
102 * $p->setTitle("Error!");
103 * $p->setBodyContent("<p>oops, we have an error: $error</p>");
106 * } // end error handling
108 * // print to browser
112 * Simple XHTML declaration example:
114 * $p = new HTML_Page();
115 * // An XHTML compliant page (with title) is automatically generated
117 * // This overrides the XHTML 1.0 Transitional default
118 * $p->setDoctype('XHTML 1.0 Strict');
120 * // Put some content in here
121 * $p->addBodyContent("<p>some text</p>");
123 * // print to browser
134 * $p = new HTML_Page('doctype="HTML 4.01 Strict"');
135 * $p->addBodyContent = "<p>some text</p>";
139 * nuke doctype declaration:
140 * -------------------------
142 * $p = new HTML_Page('doctype="none"');
143 * $p->addBodyContent = "<p>some text</p>";
147 * @author Adam Daniel <adaniel1@eesus.jnj.com>
148 * @author Klaus Guenther <klaus@capitalfocus.org>
150 * @since PHP 4.0.3pl1
152 class HTML_Page extends HTML_Common {
155 * Contains the content of the <body> tag.
160 var $_body = array();
163 * Controls caching of the page
171 * Contains the character encoding string
176 var $_charset = 'utf-8';
179 * Contains the !DOCTYPE definition
184 var $_doctype = array('type'=>'xhtml','version'=>'1.0','variant'=>'transitional');
187 * Contains the page language setting
192 var $_language = 'en';
200 var $_metaTags = array( 'standard' => array ( 'Generator' => 'PEAR HTML_Page' ) );
203 * Array of linked scripts
208 var $_scripts = array();
211 * Array of linked scripts
216 var $_simple = false;
219 * Array of included style declarations
224 var $_style = array();
227 * Array of linked style sheets
232 var $_styleSheets = array();
244 * Possible attributes are:
246 * - "lineend" => "unix|win|mac" (Sets line ending style; defaults to unix.)
247 * - "tab" => string (Sets line ending style; defaults to \t.)
248 * - "cache" => "false|true"
249 * - "charset" => charset string (Sets charset encoding; defaults to utf-8)
251 * - "doctype" => mixed (Sets XHTML doctype; defaults to XHTML 1.0 Transitional.)
252 * - "language" => two letter language designation. (Defines global document language; defaults to "en".)
254 * @param mixed $attributes Associative array of table tag attributes
255 * or HTML attributes name="value" pairs
258 function HTML_Page($attributes = "")
260 $commonVersion = 1.7;
261 if (HTML_Common::apiVersion() < $commonVersion) {
262 return PEAR::raiseError("HTML_Page version " . $this->apiVersion() . " requires " .
263 "HTML_Common version 1.2 or greater.", 0, PEAR_ERROR_TRIGGER);
267 $attributes = $this->_parseAttributes($attributes);
270 if (isset($attributes['lineend'])) {
271 $this->setLineEnd($attributes['lineend']);
274 if (isset($attributes['charset'])) {
275 $this->setCharset($attributes['charset']);
278 if (isset($attributes['doctype'])){
279 if ($attributes['doctype'] == 'none') {
280 $this->_simple = true;
281 } elseif ($attributes['doctype']) {
282 $this->setDoctype($attributes['doctype']);
286 if (isset($attributes['language'])) {
287 $this->setLang($attributes['language']);
290 if (isset($attributes['cache'])) {
291 $this->setCache($attributes['cache']);
297 * Generates the HTML string for the <body< tag
302 function _generateBody()
306 $lnEnd = $this->_getLineEnd();
307 $tab = $this->_getTab();
309 // If body attributes exist, add them to the body tag.
310 // Depreciated because of CSS
311 $strAttr = $this->_getAttrString($this->_attributes);
314 $strHtml = "<body $strAttr>" . $lnEnd;
316 $strHtml = '<body>' . $lnEnd;
319 // Allow for mixed content in the body array
320 // Iterate through the array and process each element
321 foreach ($this->_body as $element) {
322 if (is_object($element)) {
323 if (is_subclass_of($element, "html_common")) {
324 $element->setTab($tab);
325 $element->setTabOffset(1);
326 $element->setLineEnd($lnEnd);
328 if (method_exists($element, "toHtml")) {
329 $strHtml .= $element->toHtml() . $lnEnd;
330 } elseif (method_exists($element, "toString")) {
331 $strHtml .= $element->toString() . $lnEnd;
333 } elseif (is_array($element)) {
334 foreach ($element as $level2) {
335 if (is_subclass_of($level2, "html_common")) {
336 $level2->setTabOffset(1);
337 $level2->setTab($tab);
338 $level2->setLineEnd($lnEnd);
340 if (is_object($level2)) {
341 if (method_exists($level2, "toHtml")) {
342 $strHtml .= $level2->toHtml() . $lnEnd;
343 } elseif (method_exists($level2, "toString")) {
344 $strHtml .= $level2->toString() . $lnEnd;
347 $strHtml .= $tab . $level2 . $lnEnd;
351 $strHtml .= $tab . $element . $lnEnd;
356 $strHtml .= '</body>' . $lnEnd;
360 } // end func _generateHead
363 * Generates the HTML string for the <head< tag
368 function _generateHead()
372 $lnEnd = $this->_getLineEnd();
373 $tab = $this->_getTab();
375 $strHtml = '<head>' . $lnEnd;
376 $strHtml .= $tab . '<title>' . $this->getTitle() . '</title>' . $lnEnd;
378 // Generate META tags
379 foreach ($this->_metaTags as $type => $tag) {
380 foreach ($tag as $name => $content) {
381 if ($type == 'http-equiv') {
382 $strHtml .= $tab . "<meta http-equiv=\"$name\" content=\"$content\" />" . $lnEnd;
383 } elseif ($type == 'standard') {
384 $strHtml .= $tab . "<meta name=\"$name\" content=\"$content\" />" . $lnEnd;
389 // Generate stylesheet links
390 foreach ($this->_styleSheets as $strStyleSheet) {
391 $strHtml .= $tab . "<link rel=\"stylesheet\" href=\"$strStyleSheet\" type=\"text/css\" />" . $lnEnd;
394 // Generate stylesheet declarations
395 foreach ($this->_style as $type => $content) {
396 $strHtml .= $tab . '<style type="' . $type . '">' . $lnEnd;
397 $strHtml .= $tab . $tab . '<!--' . $lnEnd;
398 if (is_object($content)) {
400 // first let's propagate line endings and tabs for other HTML_Common-based objects
401 if (is_subclass_of($content, "html_common")) {
402 $content->setTab($tab);
403 $content->setTabOffset(3);
404 $content->setLineEnd($lnEnd);
407 // now let's get a string from the object
408 if (method_exists($content, "toString")) {
409 $strHtml .= $content->toString() . $lnEnd;
411 return PEAR::raiseError('Error: Body content object does not support method toString().',
412 0,PEAR_ERROR_TRIGGER);
416 $strHtml .= $content . $lnEnd;
418 $strHtml .= $tab . $spacer . "-->" . $lnEnd;
419 $strHtml .= $tab . "</style>" . $lnEnd;
422 // Generate script file links
423 foreach ($this->_scripts as $strSrc => $strType) {
424 $strHtml .= $tab . "<script type=\"$strType\" src=\"$strSrc\"></script>" . $lnEnd;
428 $strHtml .= '</head>' . $lnEnd;
432 } // end func _generateHead
435 * Returns the doctype declaration
440 function _getDoctype()
442 require('HTML/Page/Doctypes.php');
444 $type = $this->_doctype['type'];
445 $version = $this->_doctype['version'];
446 $variant = $this->_doctype['variant'];
450 if ($variant != '') {
451 if (isset($doctype[$type][$version][$variant][0])) {
452 foreach ( $doctype[$type][$version][$variant] as $string) {
453 $strDoctype .= $string.$this->_getLineEnd();
456 } elseif ($version != '') {
457 if (isset($doctype[$type][$version][0])) {
458 foreach ( $doctype[$type][$version] as $string) {
459 $strDoctype .= $string.$this->_getLineEnd();
462 if (isset($default[$type][$version][0])) {
463 $this->_doctype = $this->_parseDoctypeString($default[$type][$version][0]);
464 $strDoctype = $this->_getDoctype();
467 } elseif ($type != '') {
468 if (isset($default[$type][0])){
469 $this->_doctype = $this->_parseDoctypeString($default[$type][0]);
470 $strDoctype = $this->_getDoctype();
473 $this->_doctype = $this->_parseDoctypeString($default['default'][0]);
474 $strDoctype = $this->_getDoctype();
480 return PEAR::raiseError('Error: "'.$this->getDoctypeString().'" is an unsupported or illegal document type.',
481 0,PEAR_ERROR_TRIGGER);
484 } // end func _getDoctype
487 * Parses a doctype declaration like "XHTML 1.0 Strict" to an array
489 * @param string $string The string to be parsed
493 function _parseDoctypeString($string)
495 $split = explode(' ',strtolower($string));
496 $elements = count($split);
498 $array = array('type'=>$split[0],'version'=>$split[1],'variant'=>$split[2]);
501 } // end func _parseDoctypeString
504 * Sets the content of the <body> tag. If content already exists,
505 * the new content is appended.
506 * If you wish to overwrite whatever is in the body, use {@link setBody};
507 * {@link unsetBody} completely empties the body without inserting new content.
508 * It is possible to add objects, strings or an array of strings and/or objects
509 * Objects must have a toString method.
511 * @param mixed $content New <body> tag content (may be passed as a reference)
514 function addBodyContent($content)
516 $this->_body[] =& $content;
517 if (is_object($content)) {
518 if (method_exists($content, "toStyleSheet")) {
519 $this->addStyleSheet($content->toStyleSheet());
521 if (method_exists($content, "toScript")) {
522 $script = $content->toScript();
523 if (is_array($script)) {
524 $this->addScript($script[0], $script[1]);
526 $this->addScript($script);
529 } elseif (is_array($content)) {
530 foreach ($content as $element) {
531 if (is_object($content)) {
532 if (method_exists($element, "toStyleSheet")) {
533 $this->addStyleSheet($element->toStyleSheet());
535 if (method_exists($element, "toScript")) {
536 $script = $element->toScript();
537 if (is_array($script)) {
538 $this->addScript($script[0], $script[1]);
540 $this->addScript($script);
546 } // end addBodyContent
549 * Adds a linked script to the page
551 * @param string $url URL to the linked style sheet
552 * @param string $type Type of script. Defaults to 'text/javascript'
555 function addScript($url, $type="text/javascript")
557 $this->_scripts[$url] = $type;
558 } // end func addScript
561 * Adds a linked stylesheet to the page
563 * @param string $url URL to the linked style sheet
567 function addStyleSheet($url)
569 $this->_styleSheets[$url] = $url;
570 } // end func addStyleSheet
573 * Adds a stylesheet declaration to the page.
574 * Content can be a string or an object with a toString method.
575 * Defaults to text/css.
578 * @param mixed $content Style declarations (may be passed as a reference)
579 * @param string $type Type of stylesheet (defaults to 'text/css')
582 function addStyleDeclaration($content, $type = 'text/css')
584 $this->_style[$type] =& $content;
585 } // end func addStyleDeclaration
588 * Returns the current API version
593 function apiVersion()
596 } // end func apiVersion
599 * Returns the document charset encoding.
604 function getCharset()
606 return $this->_charset;
610 * Returns the document type string
615 function getDoctypeString()
617 $strDoctype = strtoupper($this->_doctype['type']);
618 $strDoctype .= ' '.ucfirst(strtolower($this->_doctype['version']));
619 if ($this->_doctype['variant']) {
620 $strDoctype .= ' ' . ucfirst(strtolower($this->_doctype['variant']));
622 return trim($strDoctype);
623 } // end func getDoctypeString
626 * Returns the document language.
633 return $this->_language;
634 } // end func getLang
637 * Return the title of the page.
645 if ($this->_simple) {
648 return 'New '. $this->getDoctypeString() . ' Compliant Page';
651 return $this->_title;
653 } // end func getTitle
656 * Sets the content of the <body> tag. If content exists, it is overwritten.
657 * If you wish to use a "safe" version, use {@link addBodyContent}
658 * Objects must have a toString method.
660 * @param mixed $content New <body> tag content. May be an object. (may be passed as a reference)
663 function setBody($content)
666 $this->addBodyContent($content);
670 * Unsets the content of the <body> tag.
680 * Defines if the document should be cached by the browser. Defaults to false.
682 * @param string $cache Options are currently 'true' or 'false'. Defaults to 'false'.
685 function setCache($cache = 'false')
687 if ($cache == 'true'){
688 $this->_cache = true;
690 $this->_cache = false;
695 * Defines if the document should be cached by the browser. Defaults to false.
697 * @param string $cache Options are currently 'true' or 'false'. Defaults to 'false'.
701 function setCharset($type = 'utf-8')
703 $this->_charset = $type;
707 * Sets or alters the XHTML !DOCTYPE declaration. Can be set to "strict",
708 * "transitional" or "frameset". Defaults to "transitional". This must come
709 * _after_ declaring the character encoding with {@link setCharset} or directly
710 * when the class is initiated {@link HTML_Page}.
712 * @param string $type String containing a document type. Defaults to "XHTML 1.0 Transitional"
716 function setDoctype($type = "XHTML 1.0 Transitional")
718 $this->_doctype = $this->_parseDoctypeString($type);
719 } // end func setDoctype
722 * Sets the global document language declaration. Default is English.
725 * @param string $lang Two-letter language designation.
727 function setLang($lang = "en")
729 $this->_language = strtolower($lang);
733 * Sets or alters a meta tag.
735 * @param string $name Value of name or http-equiv tag
736 * @param string $content Value of the content tag
737 * @param bool $http_equiv META type "http-equiv" defaults to NULL
741 function setMetaData($name, $content, $http_equiv = false)
743 if ($http_equiv == true) {
744 $this->_metaTags['http-equiv'][$name] = $content;
746 $this->_metaTags['standard'][$name] = $content;
748 } // end func setMetaData
751 * Easily sets or alters a refresh meta tag.
752 * If no $url is passed, "self" is presupposed, and the appropriate URL
753 * will be automatically generated.
755 * @param string $time Time till refresh (in seconds)
756 * @param string $url Absolute URL or "self"
757 * @param bool $https If $url = self, this allows for the https protocol defaults to NULL
761 function setMetaRefresh($time, $url = 'self', $https = false)
763 if ($url == 'self') {
765 $protocol = 'https://';
767 $protocol = 'http://';
769 $url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
771 $this->setMetaData("Refresh", "$time; url=$url", true);
772 } // end func setMetaRefresh
775 * Sets the title of the page
777 * @param string $title
781 function setTitle($title)
783 $this->_title = $title;
784 } // end func setTitle
787 * Generates and returns the complete page as a string.
796 $lnEnd = $this->_getLineEnd();
798 // get the doctype declaration
799 $strDoctype = $this->_getDoctype();
802 if ($this->_simple) {
803 $strHtml = '<html>' . $lnEnd;
804 } elseif ($this->_doctype['type'] == 'xhtml') {
805 $strHtml = '<?xml version="1.0" encoding="' . $this->_charset . '"?>' . $lnEnd;
806 $strHtml .= $strDoctype . $lnEnd;
807 $strHtml .= '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="'.$this->_language.'">';
809 $strHtml = $strDoctype . $lnEnd;
810 $strHtml .= '<html>' . $lnEnd;
812 $strHtml .= $this->_generateHead();
813 $strHtml .= $this->_generateBody();
814 $strHtml .= '</html>';
819 * Outputs the HTML content to the screen.
825 if(! $this->_cache) {
826 header("Expires: Tue, 1 Jan 1980 12:00:00 GMT");
827 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
828 header("Cache-Control: no-cache");
829 header("Pragma: no-cache");
832 // set character encoding
833 header("Content-Type: text/html; charset=" . $this->_charset);
835 $strHtml = $this->toHTML();
837 } // end func display