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()
25 // HTML/Page/Namespaces.php is required in _getNamespace()
28 * Base class for XHTML pages
30 * This class handles the details for creating a properly constructed XHTML page.
31 * Page caching, stylesheets, client side script, and Meta tags can be
32 * managed using this class.
34 * The body may be a string, object, or array of objects or strings. Objects with
35 * toHtml() and toString() methods are supported.
44 * // the default doctype is XHTML 1.0 Transitional
45 * // All doctypes and defaults are set in HTML/Page/Doctypes.php
46 * $p = new HTML_Page();
49 * $p->addBodyContent("<p>some text</p>");
55 * Complex XHTML example:
56 * ----------------------
58 * // The initializing code can also be in in the form of an HTML
59 * // attr="value" string.
60 * // Possible attributes are: charset, mime, lineend, tab, doctype, namespace, language and cache
62 * $p = new HTML_Page(array (
64 * // Sets the charset encoding
66 * 'charset' => 'utf-8',
68 * // Sets the line end character
69 * // unix (\n) is default
70 * 'lineend' => 'unix',
72 * // Sets the tab string for autoindent
73 * // tab (\t) is default
76 * // This is where you define the doctype
77 * 'doctype' => "XHTML 1.0 Strict",
79 * // Global page language setting
82 * // If cache is set to true, the browser may
83 * // cache the output. Else
89 * // Set the page title
90 * $p->setTitle("My page");
92 * // Add optional meta data
93 * $p->addMetaData("author", "My Name");
95 * // Put something into the body
96 * $p->addBodyContent = "<p>some text</p>";
98 * // If at some point you want to clear the page content
99 * // and output an error message, you can easily do that
100 * // See the source for {@link toHtml} and {@link _getDoctype}
101 * // for more details
103 * $p->setTitle("Error!");
104 * $p->setBodyContent("<p>Houston, we have a problem: $error</p>");
107 * } // end error handling
109 * // print to browser
113 * Simple XHTML declaration example:
115 * $p = new HTML_Page();
116 * // An XHTML compliant page (with title) is automatically generated
118 * // This overrides the XHTML 1.0 Transitional default
119 * $p->setDoctype('XHTML 1.0 Strict');
121 * // Put some content in here
122 * $p->addBodyContent("<p>some text</p>");
124 * // print to browser
135 * $p = new HTML_Page('doctype="HTML 4.01 Strict"');
136 * $p->addBodyContent = "<p>some text</p>";
140 * nuke doctype declaration:
141 * -------------------------
143 * $p = new HTML_Page('doctype="none"');
144 * $p->addBodyContent = "<p>some text</p>";
148 * @author Adam Daniel <adaniel1@eesus.jnj.com>
149 * @author Klaus Guenther <klaus@capitalfocus.org>
151 * @since PHP 4.0.3pl1
153 class HTML_Page extends HTML_Common {
156 * Contains the content of the <body> tag.
161 var $_body = array();
164 * Controls caching of the page
172 * Contains the character encoding string
177 var $_charset = 'utf-8';
180 * Contains the !DOCTYPE definition
185 var $_doctype = array('type'=>'xhtml','version'=>'1.0','variant'=>'transitional');
188 * Contains the page language setting
193 var $_language = 'en';
201 var $_metaTags = array( 'standard' => array ( 'Generator' => 'PEAR HTML_Page' ) );
209 var $_mime = 'text/html';
217 var $_namespace = '';
220 * Array of linked scripts
225 var $_scripts = array();
228 * Array of scripts placed in the header
233 var $_script = array();
236 * Array of linked scripts
241 var $_simple = false;
244 * Array of included style declarations
249 var $_style = array();
252 * Array of linked style sheets
257 var $_styleSheets = array();
269 * Possible attributes are:
271 * - "lineend" => "unix|win|mac" (Sets line ending style; defaults to unix.)
272 * - "tab" => string (Sets line ending style; defaults to \t.)
273 * - "cache" => "false|true"
274 * - "charset" => charset string (Sets charset encoding; defaults to utf-8)
275 * - "mime" => mime encoding string (Sets document mime type; defaults to text/html)
277 * - "doctype" => string (Sets XHTML doctype; defaults to XHTML 1.0 Transitional.)
278 * - "language" => two letter language designation. (Defines global document language; defaults to "en".)
279 * - "namespace" => string (Sets document namespace; defaults to the W3C defined namespace.)
281 * @param mixed $attributes Associative array of table tag attributes
282 * or HTML attributes name="value" pairs
285 function HTML_Page($attributes = array())
287 $commonVersion = 1.7;
288 if (HTML_Common::apiVersion() < $commonVersion) {
289 return PEAR::raiseError("HTML_Page version " . $this->apiVersion() . " requires " .
290 "HTML_Common version 1.2 or greater.", 0, PEAR_ERROR_TRIGGER);
294 $attributes = $this->_parseAttributes($attributes);
297 if (isset($attributes['lineend'])) {
298 $this->setLineEnd($attributes['lineend']);
301 if (isset($attributes['charset'])) {
302 $this->setCharset($attributes['charset']);
305 if (isset($attributes['doctype'])){
306 if ($attributes['doctype'] == 'none') {
307 $this->_simple = true;
308 } elseif ($attributes['doctype']) {
309 $this->setDoctype($attributes['doctype']);
313 if (isset($attributes['language'])) {
314 $this->setLang($attributes['language']);
317 if (isset($attributes['mime'])) {
318 $this->setMimeEncoding($attributes['mime']);
321 if (isset($attributes['namespace'])) {
322 $this->setNamespace($attributes['namespace']);
325 if (isset($attributes['tab'])) {
326 $this->setTab($attributes['tab']);
329 if (isset($attributes['cache'])) {
330 $this->setCache($attributes['cache']);
336 * Generates the HTML string for the <body< tag
341 function _generateBody()
345 $lnEnd = $this->_getLineEnd();
346 $tab = $this->_getTab();
348 // If body attributes exist, add them to the body tag.
349 // Depreciated because of CSS
350 $strAttr = $this->_getAttrString($this->_attributes);
353 $strHtml = "<body $strAttr>" . $lnEnd;
355 $strHtml = '<body>' . $lnEnd;
358 // Allow for mixed content in the body array
359 // Iterate through the array and process each element
360 foreach ($this->_body as $element) {
361 if (is_object($element)) {
362 if (is_subclass_of($element, "html_common")) {
363 $element->setTab($tab);
364 $element->setTabOffset(1);
365 $element->setLineEnd($lnEnd);
367 if (method_exists($element, "toHtml")) {
368 $strHtml .= $element->toHtml() . $lnEnd;
369 } elseif (method_exists($element, "toString")) {
370 $strHtml .= $element->toString() . $lnEnd;
372 } elseif (is_array($element)) {
373 foreach ($element as $level2) {
374 if (is_subclass_of($level2, "html_common")) {
375 $level2->setTabOffset(1);
376 $level2->setTab($tab);
377 $level2->setLineEnd($lnEnd);
379 if (is_object($level2)) {
380 if (method_exists($level2, "toHtml")) {
381 $strHtml .= $level2->toHtml() . $lnEnd;
382 } elseif (method_exists($level2, "toString")) {
383 $strHtml .= $level2->toString() . $lnEnd;
386 $strHtml .= $tab . $level2 . $lnEnd;
390 $strHtml .= $tab . $element . $lnEnd;
395 $strHtml .= '</body>' . $lnEnd;
399 } // end func _generateHead
402 * Generates the HTML string for the <head< tag
407 function _generateHead()
409 // close empty tags if XHTML
410 if ($this->_doctype['type'] == 'html'){
417 $lnEnd = $this->_getLineEnd();
418 $tab = $this->_getTab();
420 $strHtml = '<head>' . $lnEnd;
421 $strHtml .= $tab . '<title>' . $this->getTitle() . '</title>' . $lnEnd;
423 // Generate META tags
424 foreach ($this->_metaTags as $type => $tag) {
425 foreach ($tag as $name => $content) {
426 if ($type == 'http-equiv') {
427 $strHtml .= $tab . "<meta http-equiv=\"$name\" content=\"$content\"" . $tagEnd . $lnEnd;
428 } elseif ($type == 'standard') {
429 $strHtml .= $tab . "<meta name=\"$name\" content=\"$content\"" . $tagEnd . $lnEnd;
434 // Generate stylesheet links
435 foreach ($this->_styleSheets as $strStyleSheet) {
436 $strHtml .= $tab . "<link rel=\"stylesheet\" href=\"$strStyleSheet\" type=\"text/css\"" . $tagEnd . $lnEnd;
439 // Generate stylesheet declarations
440 foreach ($this->_style as $type => $content) {
441 $strHtml .= $tab . '<style type="' . $type . '">' . $lnEnd;
443 // This is for full XHTML supporte.
444 if ($this->_mime == 'text/html' ) {
445 $strHtml .= $tab . $tab . '<!--' . $lnEnd;
447 $strHtml .= $tab . $tab . '<![CDATA[' . $lnEnd;
450 if (is_object($content)) {
452 // first let's propagate line endings and tabs for other HTML_Common-based objects
453 if (is_subclass_of($content, "html_common")) {
454 $content->setTab($tab);
455 $content->setTabOffset(3);
456 $content->setLineEnd($lnEnd);
459 // now let's get a string from the object
460 if (method_exists($content, "toString")) {
461 $strHtml .= $content->toString() . $lnEnd;
463 return PEAR::raiseError('Error: Style content object does not support method toString().',
464 0,PEAR_ERROR_TRIGGER);
468 $strHtml .= $content . $lnEnd;
473 if ($this->_mime == 'text/html' ) {
474 $strHtml .= $tab . $tab . '-->' . $lnEnd;
476 $strHtml .= $tab . $tab . ']]>' . $lnEnd;
478 $strHtml .= $tab . '</style>' . $lnEnd;
481 // Generate script file links
482 foreach ($this->_scripts as $strSrc => $strType) {
483 $strHtml .= $tab . "<script type=\"$strType\" src=\"$strSrc\"></script>" . $lnEnd;
486 // Generate script declarations
487 foreach ($this->_script as $type => $content) {
488 $strHtml .= $tab . '<script type="' . $type . '">' . $lnEnd;
490 // This is for full XHTML support.
491 if ($this->_mime == 'text/html' ) {
492 $strHtml .= $tab . $tab . '<!--' . $lnEnd;
494 $strHtml .= $tab . $tab . '<![CDATA[' . $lnEnd;
497 if (is_object($content)) {
499 // first let's propagate line endings and tabs for other HTML_Common-based objects
500 if (is_subclass_of($content, "html_common")) {
501 $content->setTab($tab);
502 $content->setTabOffset(3);
503 $content->setLineEnd($lnEnd);
506 // now let's get a string from the object
507 if (method_exists($content, "toString")) {
508 $strHtml .= $content->toString() . $lnEnd;
510 return PEAR::raiseError('Error: Script content object does not support method toString().',
511 0,PEAR_ERROR_TRIGGER);
515 $strHtml .= $content . $lnEnd;
519 if ($this->_mime == 'text/html' ) {
520 $strHtml .= $tab . $tab . '-->' . $lnEnd;
522 $strHtml .= $tab . $tab . ']]>' . $lnEnd;
524 $strHtml .= $tab . '</script>' . $lnEnd;
528 $strHtml .= '</head>' . $lnEnd;
532 } // end func _generateHead
535 * Returns the doctype declaration
540 function _getDoctype()
542 require('HTML/Page/Doctypes.php');
544 if (isset($this->_doctype['type'])) {
545 $type = $this->_doctype['type'];
548 if (isset($this->_doctype['version'])) {
549 $version = $this->_doctype['version'];
552 if (isset($this->_doctype['variant'])) {
553 $variant = $this->_doctype['variant'];
558 if (isset($variant)) {
559 if (isset($doctype[$type][$version][$variant][0])) {
560 foreach ( $doctype[$type][$version][$variant] as $string) {
561 $strDoctype .= $string.$this->_getLineEnd();
564 } elseif (isset($version)) {
565 if (isset($doctype[$type][$version][0])) {
566 foreach ( $doctype[$type][$version] as $string) {
567 $strDoctype .= $string.$this->_getLineEnd();
570 if (isset($default[$type][$version][0])) {
571 $this->_doctype = $this->_parseDoctypeString($default[$type][$version][0]);
572 $strDoctype = $this->_getDoctype();
575 } elseif (isset($type)) {
576 if (isset($default[$type][0])){
577 $this->_doctype = $this->_parseDoctypeString($default[$type][0]);
578 $strDoctype = $this->_getDoctype();
581 $this->_doctype = $this->_parseDoctypeString($default['default'][0]);
582 $strDoctype = $this->_getDoctype();
588 return PEAR::raiseError('Error: "'.$this->getDoctypeString().'" is an unsupported or illegal document type.',
589 0,PEAR_ERROR_TRIGGER);
592 } // end func _getDoctype
595 * Retrieves the document namespace
600 function _getNamespace()
602 require('HTML/Page/Namespaces.php');
604 if (isset($this->_doctype['type'])) {
605 $type = $this->_doctype['type'];
608 if (isset($this->_doctype['version'])) {
609 $version = $this->_doctype['version'];
612 if (isset($this->_doctype['variant'])) {
613 $variant = $this->_doctype['variant'];
618 if (isset($variant)){
619 if (isset($namespace[$type][$version][$variant][0]) && is_string($namespace[$type][$version][$variant][0])) {
620 $strNamespace = $namespace[$type][$version][$variant][0];
621 } elseif (isset($namespace[$type][$version][0]) && is_string($namespace[$type][$version][0]) ) {
622 $strNamespace = $namespace[$type][$version][0];
623 } elseif (isset($namespace[$type][0]) && is_string($namespace[$type][0]) ) {
624 $strNamespace = $namespace[$type][0];
626 } elseif (isset($version)) {
627 if (isset($namespace[$type][$version][0]) && is_string($namespace[$type][$version][0]) ) {
628 $strNamespace = $namespace[$type][$version][0];
629 } elseif (isset($namespace[$type][0]) && is_string($namespace[$type][0]) ) {
630 $strNamespace = $namespace[$type][0];
633 if (isset($namespace[$type][0]) && is_string($namespace[$type][0]) ) {
634 $strNamespace = $namespace[$type][0];
640 return $strNamespace;
642 return PEAR::raiseError('Error: "'.$this->getDoctypeString().'" does not have a default namespace. Use setNamespace() to define your namespace.',
643 0,PEAR_ERROR_TRIGGER);
646 } // end func _getNamespace
649 * Parses a doctype declaration like "XHTML 1.0 Strict" to an array
651 * @param string $string The string to be parsed
655 function _parseDoctypeString($string)
657 $split = explode(' ',strtolower($string));
658 $elements = count($split);
660 if (isset($split[2])){
661 $array = array('type'=>$split[0],'version'=>$split[1],'variant'=>$split[2]);
662 } elseif (isset($split[1])){
663 $array = array('type'=>$split[0],'version'=>$split[1]);
665 $array = array('type'=>$split[0]);
669 } // end func _parseDoctypeString
672 * Sets the content of the <body> tag. If content already exists,
673 * the new content is appended.
674 * If you wish to overwrite whatever is in the body, use {@link setBody};
675 * {@link unsetBody} completely empties the body without inserting new content.
676 * It is possible to add objects, strings or an array of strings and/or objects
677 * Objects must have a toString method.
679 * @param mixed $content New <body> tag content (may be passed as a reference)
682 function addBodyContent($content)
684 $this->_body[] =& $content;
685 if (is_object($content)) {
686 if (method_exists($content, "toStyleSheet")) {
687 $this->addStyleSheet($content->toStyleSheet());
689 if (method_exists($content, "toScript")) {
690 $script = $content->toScript();
691 if (is_array($script)) {
692 $this->addScript($script[0], $script[1]);
694 $this->addScript($script);
697 } elseif (is_array($content)) {
698 foreach ($content as $element) {
699 if (is_object($content)) {
700 if (method_exists($element, "toStyleSheet")) {
701 $this->addStyleSheet($element->toStyleSheet());
703 if (method_exists($element, "toScript")) {
704 $script = $element->toScript();
705 if (is_array($script)) {
706 $this->addScript($script[0], $script[1]);
708 $this->addScript($script);
714 } // end addBodyContent
717 * Adds a linked script to the page
719 * @param string $url URL to the linked script
720 * @param string $type Type of script. Defaults to 'text/javascript'
723 function addScript($url, $type="text/javascript")
725 $this->_scripts[$url] = $type;
726 } // end func addScript
729 * Adds a script to the page.
730 * Content can be a string or an object with a toString method.
731 * Defaults to text/javascript.
734 * @param mixed $content Script (may be passed as a reference)
735 * @param string $type Scripting mime (defaults to 'text/javascript')
738 function addScriptDeclaration($content, $type = 'text/javascript')
740 $this->_script[strtolower($type)] =& $content;
741 } // end func addScriptDeclaration
744 * Adds a linked stylesheet to the page
746 * @param string $url URL to the linked style sheet
750 function addStyleSheet($url)
752 $this->_styleSheets[$url] = $url;
753 } // end func addStyleSheet
756 * Adds a stylesheet declaration to the page.
757 * Content can be a string or an object with a toString method.
758 * Defaults to text/css.
761 * @param mixed $content Style declarations (may be passed as a reference)
762 * @param string $type Type of stylesheet (defaults to 'text/css')
765 function addStyleDeclaration($content, $type = 'text/css')
767 $this->_style[strtolower($type)] =& $content;
768 } // end func addStyleDeclaration
771 * Returns the current API version
776 function apiVersion()
779 } // end func apiVersion
782 * Returns the document charset encoding.
787 function getCharset()
789 return $this->_charset;
793 * Returns the document type string
798 function getDoctypeString()
800 $strDoctype = strtoupper($this->_doctype['type']);
801 $strDoctype .= ' '.ucfirst(strtolower($this->_doctype['version']));
802 if ($this->_doctype['variant']) {
803 $strDoctype .= ' ' . ucfirst(strtolower($this->_doctype['variant']));
805 return trim($strDoctype);
806 } // end func getDoctypeString
809 * Returns the document language.
816 return $this->_language;
817 } // end func getLang
820 * Return the title of the page.
828 if ($this->_simple) {
831 return 'New '. $this->getDoctypeString() . ' Compliant Page';
834 return $this->_title;
836 } // end func getTitle
839 * Sets the content of the <body> tag. If content exists, it is overwritten.
840 * If you wish to use a "safe" version, use {@link addBodyContent}
841 * Objects must have a toString method.
843 * @param mixed $content New <body> tag content. May be an object. (may be passed as a reference)
846 function setBody($content)
849 $this->addBodyContent($content);
853 * Unsets the content of the <body> tag.
863 * Defines if the document should be cached by the browser. Defaults to false.
865 * @param string $cache Options are currently 'true' or 'false'. Defaults to 'false'.
868 function setCache($cache = 'false')
870 if ($cache == 'true'){
871 $this->_cache = true;
873 $this->_cache = false;
878 * Defines if the document should be cached by the browser. Defaults to false.
880 * @param string $cache Options are currently 'true' or 'false'. Defaults to 'false'.
884 function setCharset($type = 'utf-8')
886 $this->_charset = $type;
890 * Sets or alters the XHTML !DOCTYPE declaration. Can be set to "strict",
891 * "transitional" or "frameset". Defaults to "transitional". This must come
892 * _after_ declaring the character encoding with {@link setCharset} or directly
893 * when the class is initiated {@link HTML_Page}.
895 * @param string $type String containing a document type. Defaults to "XHTML 1.0 Transitional"
899 function setDoctype($type = "XHTML 1.0 Transitional")
901 $this->_doctype = $this->_parseDoctypeString($type);
902 } // end func setDoctype
905 * Sets the global document language declaration. Default is English.
908 * @param string $lang Two-letter language designation.
910 function setLang($lang = "en")
912 $this->_language = strtolower($lang);
916 * Sets or alters a meta tag.
918 * @param string $name Value of name or http-equiv tag
919 * @param string $content Value of the content tag
920 * @param bool $http_equiv META type "http-equiv" defaults to NULL
924 function setMetaData($name, $content, $http_equiv = false)
926 if ($http_equiv == true) {
927 $this->_metaTags['http-equiv'][$name] = $content;
929 $this->_metaTags['standard'][$name] = $content;
931 } // end func setMetaData
934 * Sets an http-equiv Content-Type meta tag
939 function setMetaContentType()
941 $this->setMetaData('Content-Type', $this->_mime . '; charset=' . $this->_charset , true );
942 } // end func setMetaContentType
945 * Easily sets or alters a refresh meta tag.
946 * If no $url is passed, "self" is presupposed, and the appropriate URL
947 * will be automatically generated.
949 * @param string $time Time till refresh (in seconds)
950 * @param string $url Absolute URL or "self"
951 * @param bool $https If $url = self, this allows for the https protocol defaults to NULL
955 function setMetaRefresh($time, $url = 'self', $https = false)
957 if ($url == 'self') {
959 $protocol = 'https://';
961 $protocol = 'http://';
963 $url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
965 $this->setMetaData("Refresh", "$time; url=$url", true);
966 } // end func setMetaRefresh
969 * Sets the document MIME encoding that is sent to the browser.
971 * @param string $type
975 function setMimeEncoding($type = 'text/html')
977 $this->_mime = strtolower($type);
978 } // end func setMimeEncoding
981 * Sets the document namespace
983 * @param string $namespace Optional. W3C namespaces are used by default.
987 function setNamespace($namespace = '')
989 if (isset($namespace)){
990 $this->_namespace = $namespace;
992 $this->_namespace = $this->_getNamespace();
994 } // end func setTitle
997 * Sets the title of the page
999 * @param string $title
1003 function setTitle($title)
1005 $this->_title = $title;
1006 } // end func setTitle
1009 * Generates and returns the complete page as a string.
1018 $lnEnd = $this->_getLineEnd();
1020 // get the doctype declaration
1021 $strDoctype = $this->_getDoctype();
1023 // This determines how the doctype is declared
1024 if ($this->_simple) {
1026 $strHtml = '<html>' . $lnEnd;
1028 } elseif ($this->_doctype['type'] == 'xhtml') {
1030 // get the namespace if not already set
1031 if (!$this->_namespace){
1032 $this->_namespace = $this->_getNamespace();
1035 $strHtml = '<?xml version="1.0" encoding="' . $this->_charset . '"?>' . $lnEnd;
1036 $strHtml .= $strDoctype . $lnEnd;
1037 $strHtml .= '<html xmlns="' . $this->_namespace . '" xml:lang="' . $this->_language . '">' . $lnEnd;
1041 $strHtml = $strDoctype . $lnEnd;
1042 $strHtml .= '<html>' . $lnEnd;
1046 $strHtml .= $this->_generateHead();
1047 $strHtml .= $this->_generateBody();
1048 $strHtml .= '</html>';
1050 } // end func toHtml
1053 * Outputs the HTML content to the screen.
1059 if(! $this->_cache) {
1060 header("Expires: Tue, 1 Jan 1980 12:00:00 GMT");
1061 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
1062 header("Cache-Control: no-cache");
1063 header("Pragma: no-cache");
1066 // set character encoding
1067 header('Content-Type: ' . $this->_mime . '; charset=' . $this->_charset);
1069 $strHtml = $this->toHTML();
1071 } // end func display