[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/lib/pear/HTML/AJAX/ -> Server.php (source)

   1  <?php
   2  /**
   3   * OO AJAX Implementation for PHP
   4   *
   5   * SVN Rev: $Id: Server.php,v 1.1.2.1 2008/10/03 07:09:51 nicolasconnault Exp $
   6   *
   7   * @category   HTML
   8   * @package    AJAX
   9   * @author     Joshua Eichorn <josh@bluga.net>
  10   * @copyright  2005 Joshua Eichorn
  11   * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
  12   * @version    Release: @package_version@
  13   */
  14  
  15  /**
  16   * Require the main AJAX library
  17   */
  18  require_once 'HTML/AJAX.php';
  19  
  20  /**
  21   * Class for creating an external AJAX server
  22   *
  23   * Can be used in 2 different modes, registerClass mode where you create an instance of the server and add the classes that will be registered
  24   * and then run handle request
  25   *
  26   * Or you can extend it and add init{className} methods for each class you want to export
  27   *
  28   * Client js generation is exposed through 2 _GET params client and stub
  29   *  Setting the _GET param client to `all` will give you all the js classes needed
  30   *  Setting the _GET param stub to `all` will give you stubs of all registered classes, you can also set it too just 1 class
  31   *
  32   * @category   HTML
  33   * @package    AJAX
  34   * @author     Joshua Eichorn <josh@bluga.net>
  35   * @copyright  2005 Joshua Eichorn
  36   * @license    http://www.opensource.org/licenses/lgpl-license.php  LGPL
  37   * @version    Release: @package_version@
  38   * @link       http://pear.php.net/package/PackageName
  39   */
  40  class HTML_AJAX_Server 
  41  {
  42  
  43      /**
  44       * Client options array if set to true the code looks at _GET
  45       * @var bool|array
  46       */
  47      var $options = true;
  48  
  49      /**
  50       * HTML_AJAX instance
  51       * @var HTML_AJAX
  52       */
  53      var $ajax;
  54  
  55      /**
  56       * Set to true if your extending the server to add init{className methods}
  57       * @var boolean
  58       * @access  public
  59       */
  60      var $initMethods = false;
  61  
  62      /**
  63       * Location on filesystem of client javascript library
  64       * @var false|string if false the default pear data dir location is used
  65       */
  66      var $clientJsLocation = false;
  67  
  68      /** 
  69       * An array of options that tell the server howto Cache output
  70       *
  71       * The rules are functions that make etag hash used to see if the client needs to download updated content
  72       * If you extend this class you can make your own rule function the naming convention is _cacheRule{RuleName}
  73       *
  74       * <code>
  75       * array(
  76       *  'httpCacheClient' => true,   // send 304 headers for responses to ?client=* requests
  77       *  'ClientCacheRule' => 'File', // create a hash from file names and modified times, options: file|content
  78       *  'ClientCacheExpects'=> 'files', // what type of content to send to the hash function, options: files|classes|content
  79       *  'httpCacheStub'   => true,   // send 304 headers for responses to ?stub=* requests
  80       *  'StubCacheRule'   => 'Api',  // create a hash from the exposed api, options: api|content
  81       *  'StubCacheExpects'=> 'classes', // what type of content to send to the hash function, options: files|classes|content
  82       * )
  83       * </code>
  84       *
  85       * @var array
  86       * @access  public
  87       */
  88      var $cacheOptions = array(
  89          'httpCacheClient'       => true, 
  90          'ClientCacheRule'       => 'file',
  91          'ClientCacheExpects'    => 'files',
  92          'httpCacheStub'         => true, 
  93          'StubCacheRule'         => 'api', 
  94          'StubCacheExpects'      => 'classes', 
  95          );
  96  
  97      /**
  98       * Compression Options
  99       *
 100       * <code>
 101       * array(
 102       *  'enabled'   => false,   // enable compression
 103       *  'type'      => 'gzip'   // the type of compression to do, options: gzip
 104       * )
 105       * </code>
 106       *
 107       * @var array
 108       * @access public
 109       */
 110      var $compression = array(
 111          'enabled'       => false,
 112          'type'          => 'gzip'
 113      );
 114  
 115      /**
 116       * Javascript library names and there path 
 117       *
 118       * the return of $this->clientJsLocation(), is prepended before running readfile on them
 119       *
 120       * @access  public
 121       * @var array
 122       */
 123      var $javascriptLibraries = array(
 124          'all'           =>  'HTML_AJAX.js',
 125          'html_ajax'     =>  'HTML_AJAX.js',
 126          'html_ajax_lite'=>  'HTML_AJAX_lite.js',
 127          'json'          =>  'serializer/JSON.js',
 128          'request'       =>  'Request.js',
 129          'main'          =>  array('Compat.js','Main.js','clientPool.js'),
 130          'httpclient'    =>  'HttpClient.js',
 131          'dispatcher'    =>  'Dispatcher.js',
 132          'util'          =>  'util.js',
 133          'loading'       =>  'Loading.js',
 134          'phpserializer' =>  'serializer/phpSerializer.js',
 135          'urlserializer' =>  'serializer/UrlSerializer.js',
 136          'haserializer'  =>  'serializer/haSerializer.js',
 137          'clientpool'    =>  'clientPool.js',
 138          'iframe'        =>  'IframeXHR.js',
 139          'alias'         =>  'Alias.js',
 140          'queues'        =>  'Queue.js',
 141          'behavior'      =>  array('behavior/behavior.js','behavior/cssQuery-p.js'),
 142  
 143          // rules to help you use a minimal library set
 144          'standard'      =>  array('Compat.js','clientPool.js','util.js','Main.js','HttpClient.js','Request.js','serializer/JSON.js',
 145                                      'Loading.js','serializer/UrlSerializer.js','Alias.js','behavior/behavior.js','behavior/cssQuery-p.js'),
 146          'jsonrpc'       =>  array('Compat.js','util.js','Main.js','clientPool.js','HttpClient.js','Request.js','serializer/JSON.js'),
 147          'proxyobjects'  =>  array('Compat.js','util.js','Main.js','clientPool.js','Request.js','serializer/JSON.js','Dispatcher.js'),
 148  
 149          // BC rules
 150          'priorityqueue' =>  'Queue.js',
 151          'orderedqueue'  =>  'Queue.js',
 152      );
 153  
 154      /**
 155       * Custom paths to use for javascript libraries, if not set {@link clientJsLocation} is used to find the system path
 156       *
 157       * @access public
 158       * @var array
 159       * @see registerJsLibrary
 160       */
 161      var $javascriptLibraryPaths = array();
 162  
 163      /**
 164       * Array of className => init methods to call, generated from constructor from initClassName methods
 165       *
 166       * @access protected
 167       */
 168      var $_initLookup = array();
 169      
 170  
 171      /**
 172       * Constructor creates the HTML_AJAX instance
 173       *
 174       * @param string $serverUrl (Optional) the url the client should be making a request too
 175       */
 176      function HTML_AJAX_Server($serverUrl = false) 
 177      {
 178          $this->ajax = new HTML_AJAX();
 179  
 180          // parameters for HTML::AJAX
 181          $parameters = array('stub', 'client');
 182  
 183          // keep in the query string all the parameters that don't belong to AJAX
 184          // we remove all string like "parameter=something&". Final '&' can also
 185          // be '&amp;' (to be sure) and is optional. '=something' is optional too.
 186          $querystring = '';
 187          if (isset($_SERVER['QUERY_STRING'])) {
 188              $querystring = preg_replace('/(' . join('|', $parameters) . ')(?:=[^&]*(?:&(?:amp;)?|$))?/', '', $this->ajax->_getServer('QUERY_STRING'));
 189          }
 190  
 191          // call the server with this query string
 192          if ($serverUrl === false) {
 193              $serverUrl = htmlentities($this->ajax->_getServer('PHP_SELF'));
 194          }
 195  
 196          if (substr($serverUrl,-1) != '?') {
 197              $serverUrl .= '?';
 198          }
 199          $this->ajax->serverUrl =  $serverUrl . $querystring;
 200          
 201          $methods = get_class_methods($this);
 202          foreach($methods as $method) {
 203              if (preg_match('/^init([a-zA-Z0-9_]+)$/',$method,$match)) {
 204                  $this->_initLookup[strtolower($match[1])] = $method;
 205              }
 206          }
 207      }
 208  
 209      /**
 210       * Handle a client request, either generating a client or having HTML_AJAX handle the request
 211       *
 212       * @return boolean true if request was handled, false otherwise
 213       */
 214      function handleRequest() 
 215      {
 216          if ($this->options == true) {
 217              $this->_loadOptions();
 218          }
 219          //basically a hook for iframe but allows processing of data earlier
 220          $this->ajax->populatePayload();
 221          if (!isset($_GET['c']) && (count($this->options['client']) > 0 || count($this->options['stub']) > 0) ) {
 222              $this->generateClient();
 223              return true;
 224          } else {
 225              if (!empty($_GET['c'])) {
 226                  $this->_init($this->_cleanIdentifier($this->ajax->_getVar('c')));
 227              }
 228              return $this->ajax->handleRequest();
 229          }
 230      }
 231  
 232      /**
 233       * Register method passthrough to HTML_AJAX
 234       *
 235       * @see HTML_AJAX::registerClass for docs
 236       */
 237      function registerClass(&$instance, $exportedName = false, $exportedMethods = false) 
 238      {
 239          $this->ajax->registerClass($instance,$exportedName,$exportedMethods);
 240      }
 241  
 242      /**
 243       * Change default serialization - important for exporting classes
 244       *
 245       * I wanted this for the xml serializer :)
 246       */
 247      function setSerializer($type) 
 248      {
 249          $this->ajax->serializer = $type;
 250          $this->ajax->unserializer = $type;
 251      }
 252  
 253      /**
 254       * Register a new js client library
 255       *
 256       * @param string          $libraryName name you'll reference the library as
 257       * @param string|array    $fileName   actual filename with no path, for example customLib.js
 258       * @param string|false    $path   Optional, if not set the result from jsClientLocation is used
 259       */
 260      function registerJSLibrary($libraryName,$fileName,$path = false) {
 261          $libraryName = strtolower($libraryName);
 262          $this->javascriptLibraries[$libraryName] = $fileName;
 263  
 264          if ($path !== false) {
 265              $this->javascriptLibraryPaths[$libraryName] = $path;
 266          }
 267      }
 268  
 269      /**
 270       * Register init methods from an external class
 271       *
 272       * @param object    $instance an external class with initClassName methods
 273       */
 274      function registerInitObject(&$instance) {
 275          $instance->server =& $this;
 276          $methods = get_class_methods($instance);
 277          foreach($methods as $method) {
 278              if (preg_match('/^init([a-zA-Z0-9_]+)$/',$method,$match)) {
 279                  $this->_initLookup[strtolower($match[1])] = array(&$instance,$method);
 280              }
 281          }
 282      }
 283  
 284      /**
 285       * Register a callback to be exported to the client
 286       *
 287       * This function uses the PHP callback pseudo-type
 288       *
 289       */
 290      function registerPhpCallback($callback)
 291      {
 292          if (!is_callable($callback)) {
 293              // invalid callback
 294              return false;
 295          }
 296          
 297          if (is_array($callback) && is_object($callback[0])) {
 298              // object method
 299              $this->registerClass($callback[0], strtolower(get_class($callback[0])), array($callback[1]));
 300              return true;
 301          }
 302          
 303          // static callback
 304          $this->ajax->registerPhpCallback($callback);
 305      }
 306  
 307      /**
 308       * Generate client js
 309       *
 310       * @todo    this is going to need tests to cover all the options
 311       */
 312      function generateClient() 
 313      {
 314          $headers = array();
 315  
 316          ob_start();
 317  
 318          // create a list list of js files were going to need to output
 319          // index is the full file and so is the value, this keeps duplicates out of $fileList
 320          $fileList = array();
 321  
 322          if(!is_array($this->options['client'])) {
 323              $this->options['client'] = array();
 324          }
 325          foreach($this->options['client'] as $library) {
 326              if (isset($this->javascriptLibraries[$library])) {
 327                  $lib = (array)$this->javascriptLibraries[$library];
 328                  foreach($lib as $file) {
 329                      if (isset($this->javascriptLibraryPaths[$library])) {
 330                          $fileList[$this->javascriptLibraryPaths[$library].$file] = $this->javascriptLibraryPaths[$library].$file;
 331                      }
 332                      else {
 333                          $fileList[$this->clientJsLocation().$file] = $this->clientJsLocation().$file;
 334                      }
 335                  }
 336              }
 337          }
 338  
 339          // do needed class init if were running an init server
 340          if(!is_array($this->options['stub'])) {
 341              $this->options['stub'] = array();
 342          }
 343          $classList = $this->options['stub'];
 344          if ($this->initMethods) {
 345              if (isset($this->options['stub'][0]) && $this->options['stub'][0] === 'all') {
 346                      $this->_initAll();
 347              } else {
 348                  foreach($this->options['stub'] as $stub) {
 349                      $this->_init($stub);
 350                  }
 351              }
 352          }
 353          if (isset($this->options['stub'][0]) && $this->options['stub'][0] === 'all') {
 354              $classList = array_keys($this->ajax->_exportedInstances);
 355          }
 356  
 357          // if were doing stub and client we have to wait for both ETags before we can compare with the client
 358          $combinedOutput = false;
 359          if ($classList != false && count($classList) > 0 && count($fileList) > 0) {
 360              $combinedOutput = true;
 361          }
 362  
 363  
 364          if ($classList != false && count($classList) > 0) {
 365  
 366              // were setup enough to make a stubETag if the input it wants is a class list
 367              if ($this->cacheOptions['httpCacheStub'] && 
 368                  $this->cacheOptions['StubCacheExpects'] == 'classes') 
 369              {
 370                  $stubETag = $this->_callCacheRule('Stub',$classList);
 371              }
 372  
 373              // if were not in combined output compare etags, if method returns true were done
 374              if (!$combinedOutput && isset($stubETag)) {
 375                  if ($this->_compareEtags($stubETag)) {
 376                      ob_end_clean();
 377                      return;
 378                  }
 379              }
 380  
 381              // output the stubs for all the classes in our list
 382              foreach($classList as $class) {
 383                      echo $this->ajax->generateClassStub($class);
 384              }
 385  
 386              // if were cacheing and the rule expects content make a tag and check it, if the check is true were done
 387              if ($this->cacheOptions['httpCacheStub'] && 
 388                  $this->cacheOptions['StubCacheExpects'] == 'content') 
 389              {
 390                  $stubETag = $this->_callCacheRule('Stub',ob_get_contents());
 391              }
 392  
 393              // if were not in combined output compare etags, if method returns true were done
 394              if (!$combinedOutput && isset($stubETag)) {
 395                  if ($this->_compareEtags($stubETag)) {
 396                      ob_end_clean();
 397                      return;
 398                  }
 399              }
 400          }
 401  
 402          if (count($fileList) > 0) {
 403              // if were caching and need a file list build our jsETag
 404              if ($this->cacheOptions['httpCacheClient'] && 
 405                  $this->cacheOptions['ClientCacheExpects'] === 'files') 
 406              {
 407                  $jsETag = $this->_callCacheRule('Client',$fileList);
 408  
 409              }
 410  
 411              // if were not in combined output compare etags, if method returns true were done
 412              if (!$combinedOutput && isset($jsETag)) {
 413                  if ($this->_compareEtags($jsETag)) {
 414                      ob_end_clean();
 415                      return;
 416                  }
 417              }
 418  
 419              // output the needed client js files
 420              foreach($fileList as $file) {
 421                  $this->_readFile($file);
 422              }
 423  
 424              // if were caching and need content build the etag
 425              if ($this->cacheOptions['httpCacheClient'] && 
 426                  $this->cacheOptions['ClientCacheExpects'] === 'content') 
 427              {
 428                  $jsETag = $this->_callCacheRule('Client',ob_get_contents());
 429              }
 430  
 431              // if were not in combined output compare etags, if method returns true were done
 432              if (!$combinedOutput && isset($jsETag)) {
 433                  if ($this->_compareEtags($jsETag)) {
 434                      ob_end_clean();
 435                      return;
 436                  }
 437              }
 438              // were in combined output, merge the 2 ETags and compare
 439              else if (isset($jsETag) && isset($stubETag)) {
 440                  if ($this->_compareEtags(md5($stubETag.$jsETag))) {
 441                      ob_end_clean();
 442                      return;
 443                  }
 444              }
 445          }
 446  
 447  
 448          // were outputting content, add our length header and send the output
 449          $length = ob_get_length();
 450          $output = ob_get_contents();
 451          ob_end_clean();
 452  
 453          if ($this->ajax->packJavaScript) {
 454              $output = $this->ajax->packJavaScript($output);
 455              $length = strlen($output);
 456          }
 457  
 458          if ($this->compression['enabled'] && $this->compression['type'] == 'gzip' && strpos($_SERVER["HTTP_ACCEPT_ENCODING"], "gzip") !== false) {
 459              $output = gzencode($output,9);
 460              $length = strlen($output);
 461              $headers['Content-Encoding'] = 'gzip';
 462          }
 463  
 464          if ($length > 0 && $this->ajax->_sendContentLength()) { 
 465              $headers['Content-Length'] = $length;
 466          }
 467          $headers['Content-Type'] = 'text/javascript; charset=utf-8';
 468          $this->ajax->_sendHeaders($headers);
 469          echo($output);
 470      }
 471  
 472      /**
 473       * Run readfile on input with basic error checking
 474       *
 475       * @param   string  $file   file to read
 476       * @access  private
 477       * @todo    is addslashes enough encoding for js?
 478       */
 479      function _readFile($file) 
 480      {
 481          if (file_exists($file)) {
 482              readfile($file);
 483          } else {
 484              $file = addslashes($file);
 485              echo "alert('Unable to find javascript file: $file');";
 486          }
 487      }
 488  
 489      /**
 490       * Get the location of the client js
 491       * To override the default pear datadir location set $this->clientJsLocation
 492       *
 493       * @return  string
 494       */
 495      function clientJsLocation() 
 496      {
 497          if (!$this->clientJsLocation) {
 498              $path = '@data-dir@'.DIRECTORY_SEPARATOR.'HTML_AJAX'.DIRECTORY_SEPARATOR.'js'.DIRECTORY_SEPARATOR;
 499              if(strpos($path, '@'.'data-dir@') === 0)
 500              {
 501                  $path = realpath(dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'js').DIRECTORY_SEPARATOR;
 502              }
 503              return $path;
 504          } else {
 505              return $this->clientJsLocation;
 506          }
 507      }
 508  
 509      /**
 510       * Set the location of the client js
 511       *
 512       * @access  public
 513       * @param   string  $location   Location
 514       * @return  void
 515       */
 516      function setClientJsLocation($location) 
 517      {
 518          $this->clientJsLocation = $location;
 519      }
 520  
 521      /**
 522       * Set the path to a Javascript libraries
 523       *
 524       * @access  public
 525       * @param   string  $library    Library name
 526       * @param   string  $path       Path
 527       * @return  void
 528       */
 529      function setJavascriptLibraryPath($library, $path) 
 530      {
 531          $this->javascriptLibraryPaths[$library] = $path;
 532      }
 533  
 534      /**
 535       * Set the path to more than one Javascript libraries at once
 536       *
 537       * @access  public
 538       * @param   array   $paths  Paths
 539       * @return  void
 540       */
 541      function setJavascriptLibraryPaths($paths) 
 542      {
 543          if (is_array($paths)) {
 544              $this->javascriptLibraryPaths = array_merge($this->javascriptLibraryPaths, $paths);
 545          }
 546      }
 547  
 548      /**
 549       * Load options from _GET
 550       *
 551       * @access private
 552       */
 553      function _loadOptions() 
 554      {
 555          $this->options = array('client'=>array(),'stub'=>array());
 556          if (isset($_GET['client'])) {
 557              $clients = explode(',',$this->ajax->_getVar('client'));
 558              $client = array();
 559              foreach($clients as $val) {
 560                  $cleanVal = $this->_cleanIdentifier($val);
 561                  if (!empty($cleanVal)) {
 562                      $client[] = strtolower($cleanVal);
 563                  }
 564              }
 565  
 566              if (count($client) > 0) {
 567                  $this->options['client'] = $client;
 568              }
 569          }
 570          if (isset($_GET['stub'])) {
 571              $stubs = explode(',',$this->ajax->_getVar('stub'));
 572              $stub = array();
 573              foreach($stubs as $val) {
 574                  $cleanVal = $this->_cleanIdentifier($val);
 575                  if (!empty($cleanVal)) {
 576                      $stub[] = strtolower($cleanVal);
 577                  }
 578              }
 579  
 580              if (count($stub) > 0) {
 581                  $this->options['stub'] = $stub;
 582              }
 583          }
 584      }
 585  
 586      /**
 587       * Clean an identifier like a class name making it safe to use
 588       *
 589       * @param   string  $input
 590       * @return  string
 591       * @access  private
 592       */
 593      function _cleanIdentifier($input) {
 594              return trim(preg_replace('/[^A-Za-z_0-9]/','',$input));
 595      }
 596  
 597      /**
 598       * Run every init method on the class
 599       *
 600       * @access private
 601       */
 602      function _initAll() 
 603      {
 604          if ($this->initMethods) {
 605              foreach($this->_initLookup as $class => $method) {
 606                  $this->_init($class);
 607              }
 608          }
 609      }
 610  
 611      /**
 612       * Init one class
 613       *
 614       * @param   string  $className
 615       * @access private
 616       */
 617      function _init($className) 
 618      {
 619          $className = strtolower($className);
 620          if ($this->initMethods) {
 621              if (isset($this->_initLookup[$className])) {
 622                  $method =& $this->_initLookup[$className];
 623                  if (is_array($method)) {
 624                      call_user_func($method);
 625                  }
 626                  else {
 627                      $this->$method();
 628                  }
 629              } else {
 630                  trigger_error("Could find an init method for class: " . $className);
 631              }
 632          }
 633      }
 634  
 635      /**
 636       * Generate a hash from a list of files
 637       *
 638       * @param   array   $files  file list
 639       * @return  string  a hash that can be used as an etag
 640       * @access  private
 641       */
 642      function _cacheRuleFile($files) {
 643          $signature = "";
 644          foreach($files as $file) {
 645              if (file_exists($file)) {
 646                  $signature .= $file.filemtime($file);
 647              }
 648          }
 649          return md5($signature);
 650      }
 651  
 652      /**
 653       * Generate a hash from the api of registered classes
 654       *
 655       * @param   array   $classes class list
 656       * @return  string  a hash that can be used as an etag
 657       * @access  private
 658       */
 659      function _cacheRuleApi($classes) {
 660          $signature = "";
 661          foreach($classes as $class) {
 662              if (isset($this->ajax->_exportedInstances[$class])) {
 663                  $signature .= $class.implode(',',$this->ajax->_exportedInstances[$class]['exportedMethods']);
 664              }
 665          }
 666          return md5($signature);
 667      }
 668  
 669      /**
 670       * Generate a hash from the raw content
 671       *
 672       * @param   array   $content
 673       * @return  string  a hash that can be used as an etag
 674       * @access  private
 675       */
 676      function _cacheRuleContent($content) {
 677          return md5($content);
 678      }
 679  
 680      /**
 681       * Send cache control headers
 682       * @access  private
 683       */
 684      function _sendCacheHeaders($etag,$notModified) {
 685          header('Cache-Control: must-revalidate');
 686          header('ETag: '.$etag);
 687          if ($notModified) {
 688              header('HTTP/1.0 304 Not Modified',false,304);
 689          }
 690      }
 691  
 692      /**
 693       * Compare eTags
 694       *
 695       * @param   string  $serverETag server eTag
 696       * @return  boolean
 697       * @access  private
 698       */
 699      function _compareEtags($serverETag) {
 700          if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
 701              if (strcmp($this->ajax->_getServer('HTTP_IF_NONE_MATCH'),$serverETag) == 0) {
 702                  $this->_sendCacheHeaders($serverETag,true);
 703                  return true;
 704              }
 705          }
 706          $this->_sendCacheHeaders($serverETag,false);
 707          return false;
 708      }
 709  
 710      /**
 711       * Call a cache rule and return its retusn
 712       *
 713       * @param   string  $rule Stub|Client
 714       * @param   mixed   $payload
 715       * @return  boolean
 716       * @access  private
 717       * @todo    decide if error checking is needed
 718       */
 719      function _callCacheRule($rule,$payload) {
 720          $method = '_cacheRule'.$this->cacheOptions[$rule.'CacheRule'];
 721          return call_user_func(array(&$this,$method),$payload);
 722      }
 723  }
 724  /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 725  ?>


Generated: Wed Jan 14 11:33:29 2009 Cross-referenced by PHPXref 0.7