发新话题
打印

AJAX Chat Tutorial Part 4.1: IndexController MessageAction() and JSON Encoding

AJAX Chat Tutorial Part 4.1: IndexController MessageAction() and JSON Encoding

Sticking on the server side, we can make a stab at adding a new MessageAction method to our current Controller. This will expect one piece of information: a new chat message. XML has a number of illegal characters you may not use within XML content. We could use the CDATA style enclosures to tell parsers to ignore such characters but unfortunately SimpleXML is not quite up to handling CDATA. Instead we'll simply ensure the message is passed through htmlentities() to turn HTML special characters into their entity equivalents. This has much the same effect at the cost of a little added complexity.

Adding MessageAction()
Our IndexController will now evolve to:


class IndexController extends Zend_Controller_Action
{

     public function IndexAction()
     {
         /*
          * Get View from Registry
          */
         $view = Zend_Registry::getInstance()->get('view');

         /*
          * Set a default Screen Name
          * Once a user sets their own screen name
          * it will be stored in the user session
          */
         if(!isset($_SESSION['chat_screenname']))
         {
             $view->screenName = 'NewUser';
         }
         else
         {
             $view->screenName = $_SESSION['chat_screenname'];
         }

         /*
          * Use a Response object to collate
          * any output.
          */
         $this->getResponse()->setBody(
             $view->render('index.tpl.php')
         );
     }

     public function MessageAction()
     {
         /*
          * Check for Session value chat_lastrefresh (last refresh timestamp)
          * otherwise set it to time() - 120 (2 minutes earlier)
          * This lets new users see the prior 2 minutes of conversation.
          */
         if(!isset($_SESSION['chat_lastrefresh']))
         {
             $_SESSION['chat_lastrefresh'] = time() - 120;
         }

         /*
          * At this point the user has submitted a new chat message
          * without setting a screen name. Since the default is in
          * place, we'll simply add to the session for future use.
          */
         if(!isset($_SESSION['chat_screenname']))
         {
             $_SESSION['chat_screenname'] = 'NewUser';
         }
         
         /*
          * Grab the message text from the relevant superglobal variable
          */
         $message = isset($_GET['message']) ? $_GET['message'] : '';

         /*
          * The message value must not exceed 255 characters.
          * User data must always be filtered and validated.
          * We allow empty messages, and assume an empty message
          * is a simple request to refresh the chat panel without
          * adding a new user message.
          */
         if(strlen($message) > 255)
         {
             throw new Exception('Invalid message! Must be 255 characters or less.');
         }

         /*
          * Create the XML file if not existing! Directory must be writeable...
          */
         if(!file_exists('./data/chat.xml'))
         {
             $newXML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><chat></chat>';
             file_put_contents('./data/chat.xml', $newXML);
         }

         /*
          * Load the current XML store
          */
         $xml = simplexml_load_file('./data/chat.xml');

         /*
          * Only store the message if it's not empty!
          */
         if(!empty($message))
         {
             /*
              * First we entitise special characters so they do
              * not create future XML parsing errors. XML has several
              * invalid characters like <>&"' which can only be used
              * as entities, or when placed in a CDATA section.
              *
              * Although not fatal, SimpleXML will simply reverse
              * entities for both double and single quotes before
              * writing the XML to file. The XML will therefore
              * be illegal, but will not create any problem for
              * SimpleXML when parsing. Odd behaviour...;)
              */
             $entity_message = htmlspecialchars($message, ENT_QUOTES);

             /*
              * Add new <message> element to XML file
              */
             $newMessage = $xml->addChild('message');

             /*
              * Add our data to the new <message> element.
              * Requires SimpleXML improvements since PHP 5.1.3!
              */
             $newMessage->addChild('author', $_SESSION['chat_screenname']);
             $newMessage->addChild('timestamp', time());
             $newMessage->addChild('text', $entity_message);
         }

         /*
          * Add the file contents to our Response!
          * Set header to text/xml for browsers.
          */
         $this->getResponse()->setHeader('Content-Type', 'text/xml');
         $this->getResponse()->setBody(
             $xml->asXML()
         );
     }

}

Note that we've referred to the $_GET superglobal (i.e. data is from a request using the GET method) and are echoing rather than saving the XML. In order to keep track of the user's current screen name and the timestamp for when their chat pane was last refreshed we have added two values to the $_SESSION array: chat_screenname and chat_lastrefresh. Session data is stored between requests on the server - we started our session in the index.php bootstrap file by calling session_start().

To access the new method use a url similar to http://www.example.com/chat-tuto ... ge=somemessagetext. This will call the MessageAction() method of the IndexController class with the query string as attached accessible from the $_GET superglobal.

The result should be XML output containing the new message, with the user "NewUser" and the current UNIX timestamp. If you submit an empty message the current XML document will be returned without any changes. We're setting the text/xml Content-Type header so browsers will correctly render the XML (Firefox 1.5+ specifically requires this).

Assuming this works (should be!) we can make a small amendment to save the file rather than echo it. Change


/*
* Add the file contents to our Response!
* Set header to text/xml for browsers.
*/
$this->getResponse()->setHeader('Content-Type', 'text/xml');
$this->getResponse()->setBody(
     $xml->asXML()
);

to
php

/*
* Save the file!
*/
$xml->asXML('./data/chat.xml');

Any of our new requests via the GET method will now store the message (unless empty) to the XML file. Try it a few times and open the XML file to double check.


Told you so :). Also bear in mind, in a typical application we would preferably use POST requests. GET is just easier for debugging without mucking around with POST requests from a browser.


We're not done quite yet. XML is the format we're using for storage but for our chat application we will be using a second format to transfer the new message data back to a waiting AJAX handler function. This new format is JSON (Javascript Object Notation) and generating it from the XML data for new messages is covered in the next section.


JSON Encoding with Zend_Json
Transferring data to the browser on receipt of an XMLHttpRequest can be done using any number of formats from plain text to XML. AJAX (where the X stands for XML) was originally named as such because XML was the transport format of choice at the time. This does not necessarily make XML the most attractive format. Manipulating XML using the Javascript DOM is a complex task.

JSON (Javascript Object Notation) is a lightweight data interchange format which can be evaluated by Javascript using the eval() procedure. It's a simple subset of the object literal notation of Javascript and can represent data such as arrays (among other things). Unlike XML it's a lot simpler, requires no markup and minimal processing.

Because of its simplicity, JSON is quite easy to encode and decode in PHP and most other programming languages while XML requires a much heftier set of functionality. The only PHP methods we, in our chat application, need know are the static methods Zend_Json::encode() and Zend_Json::decode(). Given we're not sending lots of data to the server, we can narrow that down to Zend_Json::encode(). The Zend_Json class forms part of the Zend Framework.

Note: As of PHP 5.2.0, the new json_encode() and json_decode() functions are also available. I have not yet tested how these match up to "Zend_Json":[url]http://framework.zend.com/manual/en/zend.json.html's[/url] static methods. Initial testing shows these functions may require content to be encoded to utf8 (see utf8_encode() in the PHP manual) BEFORE calling the functions. "Zend_Json":http://framework.zend.com/manual/en/zend.json.html handles all content without any difficulty - the chat application will support multi-byte characters without any changes (assuming the particular charset is supported by your browser!).

TOP

发新话题