Section 3.2. Remote Scripting


3.2. Remote Scripting

Remote scripting is a technique in which you make a request to the server that directly maps to a function on the server. In most environments, this is usually referred to as a Remote Procedure Call (RPC). Remote scripting approaches differ from document-centric ones mainly in how tightly coupled the server side is to the JavaScript client side. The data formats used are of a more generic nature and are designed to move standard data types, such as arrays or strings, and are not application-specific, such as the schema used by our data XML files in the DOM or XSLT examples.

The general RPC pattern is shown in Figure 3-4 and is as follows:

  1. The JavaScript client code serializes the request data into a standard format.

  2. The serialized data is sent to the server.

  3. The data is userialized to a native data type.

  4. The data in native format is used to perform an action, usually calling a preregistered function or method.

  5. The results of the server action are serialized back to the standard format.

  6. The serialized data is returned to the JavaScript client.

  7. The JavaScript client unserializes the data to a native JavaScript type.

  8. An action is performed on the result.

Figure 3-4. The RPC pattern


Any approach that follows this basic pattern can be considered an RPC approach. This can be anything from a simple technique that passes plain strings back and forth to something as complex as an entire Simple Object Access Protocol (SOAP) stack.

RPC approaches fit into four main subgroups:

  • Approaches that use plain text or basic serialization such as URL encoding

  • Approaches that use standardized XML schemas, such as SOAP or XML-RPC

  • Approaches that use custom XML schemas

  • Approaches that send JavaScript or its subset, JavaScript Object Notation (JSON)

All these approaches are used in various AJAX implementations and, in many cases, are even combined; this is especially prevalent with approaches that generate JavaScript because few server-side languages have the ability to natively parse it.

3.2.1. Basic RPC

The simplest RPC approaches send plain text between the client and the server. What distinguishes them from document-centric approaches is that they usually call only a single page on the server, and the results come directly from what a function on the server returns. Like any remote scripting approach, there is a server component and a client component. The server component has a list of functions that can be called by the client (for security reasons, any RPC server should allow calls only to preregistered functions), and it manages dispatching client requests to a function and returning its results. The application flow is this: The JavaScript client makes a call using XMLHttpRequest to the server, sending the function to call and a payload. The server calls the requested functions and outputs the results, and the JavaScript client does something useful with the result. The process is shown in Listings 3-7 and 3-8; Listing 3-7 shows the server side written in PHP, and Listing 3-8 shows the client-side HTML and JavaScript.

Listing 3-7. rpc-basic-plain.php

1  <?php 2  // functions that can be called remotely 3  function rot13($string) { 4     return str_rot13($string); 5  } 6 7  function reverse($string) { 8     return strrev($string); 9  } 10 11 // list of functions that can be called 12  $functionList = array( 13           'rot13', 14           'reverse' 15    ); 16 17 18 // function to call 19 $funcToCall = $_REQUEST['function']; 20 21 22 // set the content type 23 header('Content-Type: text/plain'); 24 25 // check whether the function is registered 26 if (!in_array($funcToCall,$functionList)) { 27    die('Unable to call'.$funcToCall); 28 } 29 30 // Get the content from the client 31 $payload = ""; 32 if (isset($HTTP_RAW_POST_DATA)) { 33    $payload = $HTTP_RAW_POST_DATA; 34 } 35 36 // call a function and output its results 37 echo $funcToCall($payload); 38 ?>

The server-side component of this basic RPC arrangement is as simple as it could be; this works for small pages, but to build a full site, you would want to move to a more feature-rich solution, like one of the toolkits listed in Appendix A, "JavaScript AJAX Libraries." In Listing 3-7, lines 28 define two small string-processing functions; these could just as easily contain calls to a database or code that builds HTML. Lines 1115 provide an array of functions that can be called remotely; this provides security, locking remote access to a small set of functions that expect input from JavaScript. On line 23, we set the Content-type header. text/plain is used because we're merely sending back string data. Lines 2528 show the security check; it uses the $functionList array we built on lines 1215; it also uses $funcToCall, which we read on line 19. The function we're trying to call isn't in this array; we end the script execution using the die command. The script ends by reading in the POST data that was sent from the form and then calling the requested function with that data as its only parameter.

The HTML page contains an input box and some links to perform some remote actions on the contents of the box. The actions run the two functions registered on the PHP page, reverse and rot13, against the content in the input box. The reverse function returns a string in reverse order, whereas rot13 replaces each character with the one that is 13 characters ahead of it in the alphabet. Now that we have a server to call, we need to build our client HTML and JavaScript page. We will reuse the same HttpClient class, adding in a helper function to allow us to make remote function calls. An HTML page that makes RPCs to the PHP script we built in Listing 3-7 is shown in Listing 3-8.

Listing 3-8. rpc-basic-plan.html

1  <html> 2  <head> 3  <title>Basic RPC Example, No data serialization</title> 4  <script type="text/javascript" src="/books/2/341/1/html/2/HttpClient.js"></script> 5  <script type="text/javascript"> 6  var serverUrl = 'rpc-basic-plain.php'; 7  function remoteCall(func,payload,callback) { 8     var client = new HttpClient(); 9     client.isAsync = true; 10    client.requestType = 'POST'; 11    client.callback = callback; 12    client.makeRequest(serverUrl+'?function='+escape(func), 13                payload,'text/plain'); 14  } 15 16 function reverseString() { 17    remoteCall('reverse',document.getElementById('string').value, 18          function(result) { 19                document.getElementById('string').value = result; 20           } 21    ); 22 } 23 24 function rot13String() { 25    remoteCall('rot13',document.getElementById('string').value, 26           function(result) { 27                 document.getElementById('string').value = result; 28           } 29    ); 30 } 31 </script> 32 </head> 33 <body> 34       <label for="string">Source String:</label> <input > 35 <ul> 36       <li><a href="javascript:reverseString()">Reverse String</a></li> 37       <li><a href="javascript:rot13String()">ROT 13 String</a></li> 38 </ul> 39 40 <div style="position: absolute; width:100px; height:20px; 41    top: 5px; right: 5px; display:none" 42    >Loading ...</div> 43 44 </body> 45 </html>

Like the earlier example pages, this one is broken into two main sections. The JavaScript code is at the top, followed by the HTML interface that calls it. Lines 714 contain the remoteCall function, which creates a new HttpClient. It sets up this new client to make an asynchronous POST request, and it uses the callback parameter as the callback function for the request. The function finishes by sending the request to the server. Each request is made to the same PHP page; we just change the query string, setting function to the PHP function we want to call. A new HttpClient instance is created for each request. If you didn't do this, you would need to add in extra logic to keep a new request from being made before an earlier one had finished, because each HttpClient instance can have only a single outstanding request.

Lines 1630 provide the two helper functions that initiate the remote function calls. The first is for reverse, and the second is for rot13. The two functions are nearly identical; they both call remoteCall, passing in the remote function to run, the value of the input box as the payload, and a callback to handle the results. The callback functions get the result from the PHP server and set the value of the input box to it.

The rest of the page is the basic UI. Line 34 contains the input box we're reading from to make remote calls; the input box also gets updated with the results of the calls. Lines 3637 contain links to run the JavaScript functions that make the remote calls. The script ends with a DIV (lines 4042), which is shown while we wait for the server to respond. Figure 3-5 illustrates example output showing a reversed string.

Figure 3-5. Using a basic RPC example to reverse a string


A simple RPC system is quick to build and has little overhead, but it lacks enough functionality that it's not usually used in larger projects. Normally, you want the ability to pass multiple arguments to the functions on the server side and an easy way to get back something besides a string on the client.

3.2.1.1. Mimicking a Form POST

Another option for performing AJAX-based RPC is to mimic a form POST. This entails URL encoding the data. URL encoding is the format used in the query string of a GET request; key value pairs are separated by an ampersand, and a basic example is shown here:

ajax=asynchronous+javascript+and+xml&hello=world


As you can see in this example, spaces are encoded as + characters. In addition, =, &, and other non-ASCII characters are escaped as hexadecimal entities. Knowing the actual details of the encoding isn't that important because JavaScript contains the encodeURIComponent() to handle the encoding of each key and value, and PHP will automatically handle the decoding for you. URL encoding can be used to perform basic RPC, allowing multiple variables to be passed, or to submit a form over AJAX. The form submission method is especially useful because you can easily fall back to normal form submission for users who don't have JavaScript enabled.

The example of this takes an HTML form and uses a drop-down element to decide how it's submitted. One mode does a submission to a slightly modified version of Listing 3-8; another submits the form using AJAX, and one mode does a normal form submission. The AJAX and normal form submission submit to the same page, showing you how you can detect an AJAX form submission. Because the AJAX code mimics a normal form submission, the server side can treat the data sent from either input method identically. You'll generally create different output from an AJAX form submission because you need to return only that content that needs to be updated (instead of generating an entire page). The fake form submission example is shown in Listing 3-9, starting with the HTML page and finishing with the two back ends.

Listing 3-9. Ajax-form.html

1  <html> 2  <head> 3  <title>Basic RPC Example, No data serialization</title> 4  <script type="text/javascript" src="/books/2/341/1/html/2/HttpClient.js"></script> 5  <script type="text/javascript"> 6  function handleForm(form) { 7     var serverUrl = ''; 8     switch(document.getElementById('formAction').value) { 9           case 'normal': 10                return true; 11                break; 12          case 'ajax': 13                serverUrl = 'Ajax-form.php'; 14                break; 15          case 'rpc': 16                serverUrl = 'Rpc-basic-urlencoded.php'; 17                break; 18     } 19 20     var client = new HttpClient(); 21     client.isAsync = true; 22     client.requestType = 'POST'; 23 24     // urlencode the payload 25     payload = "ajax=true"; 26     for(var i =0; i < form.elements.length; i++) { 27       if (form.elements[i].name) { 28             if (payload != "")  { 29                   payload += "&"; 30             } 31             payload +=  encodeURIComponent(form.elements[i].name) 32             + '=' + encodeURIComponent(form.elements[i].value); 33       } 34     } 35 36     client.callback = function(result) { 37           document.getElementById('target').innerHTML = result; 38     }; 39 40     client.makeRequest(serverUrl,payload, 41                 'application/x-www-form-urlencoded'); 42     return false; 43 } 44 45 </script> 46 </head> 47 <body> 48 <form action="Ajax-form.php" method="POST" 49          onsubmit="return handleForm(this)"> 50    <p><label>Source String:</label> 51    <input name="payload" ></p> 52 53    <p> 54    <label>Submit As:</label> 55    <select > 56          <option value='normal'>Normal Form</option> 57          <option value='ajax'>AJAX Form</option> 58          <option value='rpc'>RPC Form</option> 59    </select> 60    </p> 61 62    <p> 63    <select name='function'> 64          <option value="reverse">Reverse String</option> 64          <option value="rot13">ROT 13 String</option> 65    </select> 66    </p> 67 68    <p><input type="submit" value="Submit Form"></p> 69 </form> 70 71 <div ></div> 72 73 <div style="position: absolute; width:100px; height:20px; 74    top: 5px; right: 5px; display:none" 75    >Loading ...</div> 76 77 </body> 78 </html>

Listing 3-9 makes a form submission perform different actions; this basic setup leads us to a different layout in our JavaScript code than most of the RPC examples. Instead of having a number of smaller helper functions, we end up with a large form handler function that performs many of the same actions, no matter how we submitted the form. In its definition, this function handle Form, which starts on line 6 and continues to line 43, expects the form to be submitted as a parameter. Lines 718 decide how we're going to submit the form. To do this, we create a switch statement around the value of a select element. If we do a normal form submission, we return true, which allows normal form submission to take place. For AJAX or RPC form submission, we set the URL to submit content, too. The rest of the logic is the same because the data is formatted the samewhether we're treating it as a normal form POST or a URL-encoded RPC submission.

Lines 2023 set up an HttpClient instance to make an asynchronous POST submission; HttpClient is the XMLHttpRequest we built earlier and included on line 4. After that, we prepare a payload to send as the POST body. This is done on lines 2634. We loop over each element in the form, and if name is set on it, we add it to the form as the string name=value. Both name and value are escaped using encodeURIComponent, with each form element's value being separated from the next by an ampersand (&). Then a callback handler (lines 3638) is created to perform an action on the results of our remote calls; in this case, it just updates the contents of a DIV using innerHTML. The form handler finishes by making a remote request and returning false. When making a request (lines 4041), it's important to include the correct Content-type, because $_POST will be automatically populated in PHP only when the content-type is application/x-www-form-urlencoded. The final action of returning false is also important. Without it, the form would submit over our HttpClient and then as a normal form.

The rest of the file creates a basic user interface: an HTML form, an output target, and an element to show while we're waiting for the server to respond. Lines 4849 define the form, and the action attribute sets the page that will handle normal form submission requests. onsubmit ties our form-handling function to this form, and the value from this function is returned, allowing it to cancel the normal form submission. Line 51 creates the source string; this will be the payload sent to our RPC functions when doing an RPC submission. Lines 5559 define the select element that lets us select how the form will be submitted; the value of each option matches up with the switch statement in the handler function. Lines 6365 define a select element that lets us pick an RPC function to call on the string payload, and the values of these options match functions registered on the PHP RPC page. The page finishes with a submit button (line 68), a DIV with an ID of target that is used to show the output of our calls (line 71), and a status DIV (lines 7375).

The output from ajax-form.html can be sent to one of two pages: rpc-basic-urlencoded.php or ajax-form.php. Each page interacts with the data in the same way because our encoding works in the form submission handler, which makes each POST request look like a normal form submission. ajax-form.php is shown in Listing 3-10; it's a simple page that checks if this is an AJAX submission or a normal submission and then shows the value of $_POST using var_dump. The check for an AJAX submission is done by looking for the ajax element in the $_POST being set. Unless you set a marker like this, there is no way to tell that XMLHttpRequest was used instead of a normal form submission.

Listing 3-10. ajax-form.php

<pre> <?php if (isset($_POST['ajax'])) {       echo "AJAX Form submission\n"; } else {       echo "Standard Form submission\n"; } var_dump($_POST); ?> </pre>

3.2.1.2. URL-Encoded AJAX RPC

The RPC handler, rpc-basic-urlencoded.php, is the same code as rpc-basic-plain.php, except for a change to how the payload is read. Lines 3134 of rpc-basic-plain.php are replaced with the code shown next. This code sets $payload with the value of the payload index in $_POST:

 31  $payload = ""; 32  if (isset($_POST)) { 33      $payload = $_POST['payload']; 34  }


You can experiment with the example by loading Listing 3-9. Figure 3-6 shows what the listing's output should look like. Notice how easy it is to move between a normal form submission and an AJAX form submission. AJAX form submissions can be used with document-based approaches as well as with remote scripting approaches.

Figure 3-6. URL-encoded AJAX RPC


Adding some basic data encoding to our AJAX requests adds a lot of power and flexibility to AJAX-based RPC. It's a great way to send multiple parameters to the server, and it is a great fit for form type data. The same encoding technique can also be used with non-RPC-based approaches when you want to emulate a normal form submission.

3.2.2. SOAP and XML-RPC

SOAP and XML-RPC are standardized XML protocols for doing remote requests. Many people who have used them before will wonder why they are not used more often. There are a variety of reasons for this, but the mains ones are as follows:

  • The standards are complex and require large client libraries.

  • Their encodings are verbose, making for slower interaction.

  • The biggest benefits of talking to any Web service are negated by the fact that XMLHttpRequest's security model limits you to talking to the same server that sent the HTML page.

At present, there are no successful cross-browser implementations in JavaScript for either SOAP or XML-RPC. There don't seem to be technical issues stopping SOAP or XML-RPC from being implemented, except that any implementation will be much larger than the RPC options. SOAP or XML-RPC may become more popular in the future if browsers add native clients, but, so far, any implementations such as the SOAP client in Mozilla have been restricted to signed code or custom browsers built from the Mozilla base.

3.2.3. Custom XML

Some AJAX implementations use various custom XML schemas for transferring data. Although these formats suffer from some of the same data-bloat problems as SOAP or XML-RPC, they are generally much simpler and make a better fit because of this. One advantage of custom XML formats is that a format can be constructed to drive actions on the client side instead of just transferring data. Custom XML schemas based off the current schemas in your application's workflow might also be useful, but these generally fit a document-centric approach better because most of the schemas will be data-specific and don't fit into a generic RPC approach.

The XML example server pages, Rpc-xml.html and Rpc-xml.php, build off the RPC plain-encoded example. One advantage of using this approach over the basic RPC code is that it makes your client code more dynamic, because you can use a set of generic content-replacement functions that are put into action as needed from the server. You can use these generic functions instead of coding lots of custom callbacks. Depending on your needs, you may want to use a different XML schema in each direction, but for this example, we use the same one. It's a basic schema that tells which function to call and the parameters to pass to the function. An example of the schema is shown in Listing 3-11.

Listing 3-11. Example Call XML

 <call function="reverse">       <param>Test</param> </call>

On the JavaScript side, we can use the DOM to read the XML, turning it into a string that can be run through the eval function to make the actual call. This works well from a security standpoint because the browser security sandbox keeps any remote requests on the same server, so you can trust any new content just as much as you trusted the original page load. On the PHP side, you don't have that same level of trust, because a request can come from anywhere on the Internet. Instead, you'll want to compare the function to call against a white list and use a method like call_user_func instead of eval. XML processing is also slightly harder than in JavaScript, because versions older than 5.0 don't have DOM support by default. An easy way to support PHP 4 and 5 is to use a library from PEAR called XML_Serializer. XML_Serializer has the capability to take an XML file and turn it into a native PHP array. Most recent PHP installs come with the PEAR package manager, allowing you to install the library by running pear install XML_Serializer. (Detailed installation instructions are available at http://pear.php.net.) An example using XML to build an AJAX RPC system is shown in Listing 3-12.

Listing 3-12. Rpc-xml.html

1  <html> 2  <head> 3  <title>XML RPC Example</title> 4  <script type="text/javascript" src="/books/2/341/1/html/2/HttpClient.js"></script> 5  <script type="text/javascript"> 6  var serverUrl = 'Rpc-xml.php'; 7  function remoteCall(func,payload) { 8     var client = new HttpClient(); 9     client.isAsync = true; 10    client.requestType = 'POST'; 11    client.callback = function(result) { 12          var call = ''; 13          var callNode = client.xmlhttp.responseXML.firstChild; 14          call += callNode.getAttribute('function')+'('; 15          var params = callNode.getElementsByTagName('param'); 16 17           for(var i = 0; i < params.length; i++) { 18           call += "'"+escape(params[i].firstChild.nodeValue)+"',"; 19      } 20 21           call = call.substring(0,call.length-1)+')'; 22 23           eval(call); 24    } 25 26    payload = '<call function="'+escape(func)+'"><param>'+ 27                escape(payload)+'</param></call>'; 28 29    client.makeRequest(serverUrl,payload,'text/xml'); 30 } 31 32 function replace(id,value) { 33    document.getElementById(id).innerHTML = value; 34 } 35 36 function append(id,value) { 37    document.getElementById(id).innerHTML += value; 38 } 39 40 function remote(func) { 41    remoteCall(func,document.getElementById('string').value); 42 } 43 44 </script> 45 </head> 46 body> 47   <label for="string">Source String:</label> <input > 48 <ul> 49    <li><a href="javascript:remote('reverse')">Reverse String</a></li> 50    <li><a href="javascript:remote('rot13')">ROT 13 String</a></li> 50 </ul> 51 52 Output:<div ></div> 53 <div style="position: absolute; width:100px; height:20px; 54    top: 5px; right: 5px; display:none" 55    >Loading ...</div> 56 57 </body> 58 </html>

Listing 3-12 follows the normal pattern of a JavaScript section at the top and then a small UI to interact with it below. The JavaScript starts on line 4 by including the standard XMLHttpRequest wrapper. After that, we define the remoteCall method. This method is based on remoteCall in rpc-basic-plain.html; the biggest different is that instead of a callback being passed in, it is built from the XML. Lines 810 create an HttpClient instance and set it up for an asynchronous POST request. Lines 1124 build the callback handler that handles this result; this code uses the DOM representation of the resulting XML file to perform an action.

The basic process is to build a string and evaluate it. This process starts on line 13 by grabbing the root node of the XML document (the call tag) and putting it into callNode. Next, we append the function name and an "(" to the call string. After that, we use getElementsByTagName() (line 15) to get an array of all the param tags. We loop through these tags, appending each one inside single quotes to the string (lines 1719). Each value is also run through the escape function in case it contains a single quote or another character that would cause our eval() to fail. At the end of this process, we will get a string like "functionName('param1', 'param2',". To finish up the process of building our call string, we remove the extra "," from the end and append the closing ")" on line 21. Finally, the string is run through eval() on line 23, calling the function.

Lines 2627 prepare the XML payload to be sent to the server. Because this is just a simple example with one parameter, it is just a matter of escaping the input and putting it together with the XML tags using string concatenation. Line 26 finishes remoteCall() by making the actual server request.

Next, the file contains a couple of generic callback functions and a helper function for making the remote calls. Lines 3234 contain a generic function that replaces the content of an HTML element using innerHTML. This method takes two parameters: the ID of the element and the content to use for innerHTML. Lines 3638 contain an append() function that follows the same pattern as the replace() function on lines 3234; the only difference is that append() appends to innerHTML. The remote() function on lines 4042 grabs the input value from the string input box and calls remoteCall() with it, passing it the function in the func variable.

The rest of the file (lines 4657) contains the basic HTML UI. Line 47 contains our source input box, and lines 4850 contain a list of action links that call the JavaScript remote() function. Line 52 contains our target DIV, which can be used by the append and replace functions. The file is finished by a status DIV on lines 5355. The back end for this page is shown in Listing 3-13.

Listing 3-13. Rpc-xml.php

1  <?php 2  require_once 'XML/Unserializer.php'; 3 4  // functions that can be called remotely 5  function rot13($string) { 6     $xml = ' 7     <call function="replace"> 8           <param>target</param> 9           <param>'.str_rot13($string).'</param> 10    </call> 11    '; 12    return $xml; 13 } 14 15 function reverse($string) { 16    $xml = ' 17    <call function="append"> 18      <param>target</param> 19      <param>'.strrev($string).'</param> 20    </call> 21    '; 22    return $xml; 23 } 24 25 26 // list of functions that can be called 27 $functionList = array( 28      'rot13', 29      'reverse' 30      ); 31 32 33 // set the content type 34 header('Content-Type: text/xml'); 35 36 37 // Get the content from the client 38 if (isset($HTTP_RAW_POST_DATA)) { 39    $xml = $HTTP_RAW_POST_DATA; 40 41    $unserializer = new XML_Unserializer( 42    array('returnResult'=>true,'parseAttributes'=>true)); 43    $data = $unserializer->unserialize($xml); 44 45    // function to call 46    $funcToCall = $data['function']; 47 48    // params to function 49    $params = array(); 50    if (isset($data['param'])) { 51          if (is_array($data['param'])) { 52                $params = $data['param']; 53          } 54          else { 55                $params = array($data['param']); 56         } 57    } 58 } 59 60 // check whether the function is registered 61 if (!in_array($funcToCall,$functionList)) { 62    die('Unable to call'.$funcToCall); 63 } 64 65 // call a function and output its results 66 echo call_user_func_array($funcToCall,$params); 67 ?>

The first section of the file contains the same small function wrappers around basic PHP string handling functions as the plain example. The difference is that returned small chunks of XML define what to do with the results instead of just sending the results back. The rot13 function on lines 513 returns XML to replace the content of the element with an ID of target with a rot13 of the input string. The reverse() function on lines 1523 returns XML to append to the contents of the target element with the reverse output of the input string. These two functions are then added to our function white list on lines 2730.

The latter half of the file (lines 3366) takes an incoming request and prepares the results. Line 34 sets the content-type; if it's not set to text/xml, then responseXML will never be populated on the client without this header being set. Lines 3758 parse the XML, getting the function to call and building an array of parameters to call it with. Lines 4142 create a new XML_Unserializer instance. Options are set to parse XML attributes (line 42) and to return the parsed data form unserialized instead of using an extra method call to get it. Line 43 parses the actual XML and sets its output array to the $data variable. Line 46 uses this array to get the function we're calling, and then lines 4957 grab the array of parameters. We first check whether the param index is set (line 50), allowing us to call functions without input. If it is set, we check whether it's an array (line 51). If it is, we just set that to $params; if it's not, we wrap it in an array as we set it to $params. This is done because XML_Serializer makes $data['param'] an array when multiple param tags exist, but if just one exists, XML_Serializer makes $data['param'] index string. Lines 6063 do a basic security check, canceling script execution if the function isn't in our white list of AJAX-callable functions. Finally on line 66, we use call_user_func_array to call the function and echo its results to the client. This example is shown in Figure 3-7.

Figure 3-7. XML-based AJAX RPC


Using XML to move the data in our AJAX RPC system, we can build a system that can transfer any type of data. Using the concepts of standard XML-based RPC systems, we can encode any type of data and get the flexibility needed to build complex applications. If we optimize the schema for the server-side language that is being used, we can also limit the overhead created by the XML tags needed to describe the data. XML is used in many AJAX RPC libraries and provides everything you need to make a complete RPC solution.

3.2.4. JavaScript and JSON

Generating JavaScript and sending it to the client where it is run through eval() is a popular way to move data in object implementations. This process is popular because JavaScript has compact notations available for data types, such as arrays, and it allows for very flexible operations. Some AJAX frameworks use this ability to generate new client code from the server as needed, allowing the framework to provide a more centralized view of the development instead of the normal client/server dichotomy.

A mixed XML/JavaScript example could easily be built for the RPC XML in Listings 3-12 and 3-13. On the client side, you would send XML to the server, as is the case with the current code, but for the results, the server would return JavaScript and the client would eval it directly. To accomplish this, we need to make some small edits to Listings 3-12 and 3-13. For Listing 3-12, we need to replace lines 1224 with a simple three-line callback that will run eval on the results from the server. This new callback is shown in Listing 3-14. The server URL on line 6 is updated to point to a new PHP script, which will be an edited version of Listing 3-13. This update is shown here:

var serverUrl = 'rpc-xml-javascript.php';


On the server side, lines 423 of Listing 3-13 are removed and replaced with the eight lines shown in Listing 3-15. The content type on line 34 of Listing 3-13 was also changed to text/plain.

Listing 3-14. Changes to Make Rpc-xml-javascript.html

12 client.callback = function(result) { 13      eval(result); 14 }

Listing 3-15. More changes to Make Rpc-xml-javascript.html

4  // functions that can be called remotely 5  function rot13($string) { 6       return "replace('target','".addslashes(str_rot13($string))."')"; 7  } 8 9  function reverse($string) { 10      return "append('target','".addslashes(strrev($string))."')"; 11 }

These small changes give you a simpler code base with which to work, and their use entails sending a much smaller amount of data back to the client. It also allows for simpler, more flexible coding because you can send back any JavaScript instead of only what your XML schema allows.

Taking this approach one step further and sending JavaScript in both directions would be nice because of the data savings, but that's a much harder task to do for a couple reasons. Most server languages don't contain a JavaScript interpreter, so they can't evaluate JavaScript code, but even if they did, you wouldn't want to allow arbitrary client-created code to run on your server. Running code from the client on the server would be a huge security problem. The solution to both of these problems is a subset of JavaScript called JSON, which is the literal syntax for JavaScript objects. It can be run by eval() on the JavaScript side. This allows for any JavaScript data types to be transferred, and it's much faster than an XML-based solution because the compact encoding allows for a much smaller amount of data to be transferred. On the server side, JSON is simple enough for a small parser to be built to serialize native data types into JSON and to create native data types from JSON.

While JSON is powerful and supports all JavaScript data types, it also has the drawback of being a more complex solution. A library is needed on the server to handle moving to and from the JSON strings, and a library is needed on the JavaScript side to create JSON stringsalthough eval() can be used to turn JSON into JavaScript objects. This makes a JSON example more complex than the examples shown in the other sections, which means if you want to use JSON, you'll need to find some external libraries to do the actual parsing. Appendix A contains a list of libraries that provide JSON processing. In Chapter 9, "Libraries Used in Part II: HTML_AJAX," a complete JSON RPC library for PHP HTML_AJAX is shown.




Understanding AJAX(c) Using JavaScript to Create Rich Internet Applications
Understanding AJAX: Using JavaScript to Create Rich Internet Applications
ISBN: 0132216353
EAN: 2147483647
Year: N/A
Pages: 154

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net