You are here:Home arrow Home arrow PHP arrow Serving XHTML in Zend Framework App
  • Narrow screen resolution
  • Wide screen resolution
  • Decrease font size
  • Default font size
  • Increase font size
  • default color
  • green color
  • blue color

Serving XHTML in Zend Framework App

Wednesday, 22 April 2009

Serving XHTML is often misunderstood by PHP developers. Frontend engineers simply include the XHTML doctype to their documents, without actually serving document as XHTML. This triggers majority of the browsers to treat such pages as “tag-soup”.

If you really care for faster XHTML rendering, you can get its benefit by using content negotiation, thanks to the new controller plugin we have for you.

You should note, however, that your pages should be XHTML compliant and validate against XHTML validators like our own XHTML validator. Otherwise, XHTML supporting browsers will only display errors instead of actual content! If you are sure that your frontend designer is a good XHTML writer, then you should be safe (still, it never hurts to validate first).

Some of the theory. Content negotiation allows to get different presentation of the same resource by setting preferred content type while requesting a document. Suppose you have a page at /some/url. If browser supports compressed pages, it can provide preferration for gzipped pages in HTTP request. The server then sends a gzipped version of that same pages to the browser (that is, in case you have gzip compression done by either PHP or Apache). Same applies to MIME types. A web page can be served with different MIME types (HTML or XHTML, and others) which are introduced as the Internet evolves its standards.

This front controller plugin's code mostly takes concepts from the excellent article by Keystone Websites, but implements in Zend Framework environment in an object-oriented way :)

<?php
class Smartycode_Controller_Plugin_XhtmlServe extends Zend_Controller_Plugin_Abstract
{
    private $_charset = 'iso-8859-1';
    private $_mime = 'text/html';
    private $_lang = 'en';
   
    public function __construct($charset = null,  $lang = null) {
       
        if ($charset) $this->_charset = $charset;
        if ($lang) $this->_lang = $lang;
    }
   
    public function dispatchLoopShutdown ()
    {
        if(stristr($this->getRequest()->getServer('HTTP_ACCEPT'), 'application/xhtml+xml'))
        {
            if (preg_match("/application\/xhtml\+xml;q=([01]|0\.\d{1,3}|1\.0)/i",
               $this->getRequest()->getServer('HTTP_ACCEPT'), $matches)) {
                $xhtml_q = $matches[1];
                if (preg_match("/text\/html;q=q=([01]|0\.\d{1,3}|1\.0)/i",
                  $this->getRequest()->getServer('HTTP_ACCEPT'), $matches)) {
                    $html_q = $matches[1];
                    if ((float) $xhtml_q >= (float) $html_q) {
                        $this->_mime = 'application/xhtml+xml';
                    }
                }
            } else {
                $this->_mime = 'application/xhtml+xml';
            }
        }
        if ($this->_mime == 'application/xhtml+xml') {
                       
            $this->getResponse()->setBody(
              '<?xml version="1.0" encoding="'.$this->_charset.'" ?>'.
              "\n".
              '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'.
                "\n".
                '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="'.$this->_lang.'" lang="'.$this->_lang.'">'.
                "\n".
                $this->getResponse()->getBody()
            );
        } else {
           
          $this->getResponse()->setBody(
                '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
              . "\n"
              . '<html lang="'.$this->_lang.'">'
              . "\n"
              . str_replace(' />', '>', $this->getResponse()->getBody())
          );
        }
       
        $this->getResponse()->setHeader('Content-Type', $this->_mime . ';charset=' . $this->_charset)->setHeader('Vary', 'Accept');
    }
}

What happens here, is that if browser shows that it can handle, and would like to get XHTML document, the server sends it with XHTML mime type. Otherwise, short closed tags are stripped from document thus converting it to an html document. HTML mime type (text/html) is then used.

To use this plugin, simply attach it to front controller like this:
 $frontController->registerPlugin(new Smartycode_Controller_Plugin_XhtmlServe('UTF-8', 'en'), 90); First parameter is the charset that will appear in document's content-type header, second is the language code that appears in document's html and/or xml tags.

To make this work for your application, you have to take additional steps

  • If your application uses our etag handler solution (conditional http get), make sure that the xhtml plugin is attached before that plugin. (simply adjust the numeric value).
  • Make sure to edit your layout file if using MVC to output ONLY html code that comes after html opening tag. For instance, in my layout file I have:   <?php
      /**
       * Default Layout
      */

      /*
      echo $this->doctype()
      ?>
      <html>
      */

      ?>
     
    (doctype and html opening tag are commented)
In conlusion: Browsers that indicate preferration for XHTML:
  • Google Chrome
  • Mozilla Firefox
  • Opera
  • Do you know any others? Please, comment here




Reddit!Del.icio.us!Facebook!Slashdot!Netscape!Technorati!StumbleUpon!
Last Updated ( Wednesday, 24 August 2011 )

Add comment


Security code
Refresh

< Prev   Next >