[ Index ]

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

title

Body

[close]

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

   1  <?php
   2  /**
   3   * OO AJAX Implementation for PHP
   4   *
   5   * SVN Rev: $Id: AJAX.php,v 1.1.2.1 2008/10/03 07:09:50 nicolasconnault Exp $
   6   *
   7   * @category  HTML
   8   * @package   AJAX
   9   * @author    Joshua Eichorn <josh@bluga.net>
  10   * @author    Arpad Ray <arpad@php.net>
  11   * @author    David Coallier <davidc@php.net>
  12   * @author    Elizabeth Smith <auroraeosrose@gmail.com>
  13   * @copyright 2005-2008 Joshua Eichorn, Arpad Ray, David Coallier, Elizabeth Smith
  14   * @license   http://www.opensource.org/licenses/lgpl-license.php   LGPL
  15   * @version   Release: 0.5.6
  16   * @link      http://pear.php.net/package/HTML_AJAX
  17   */
  18  
  19  /**
  20   * This is a quick hack, loading serializers as needed doesn't work in php5
  21   */
  22  require_once "HTML/AJAX/Serializer/JSON.php";
  23  require_once "HTML/AJAX/Serializer/Null.php";
  24  require_once "HTML/AJAX/Serializer/Error.php";
  25  require_once "HTML/AJAX/Serializer/XML.php";
  26  require_once "HTML/AJAX/Serializer/PHP.php";
  27  require_once 'HTML/AJAX/Debug.php';
  28      
  29  /**
  30   * OO AJAX Implementation for PHP
  31   *
  32   * @category  HTML
  33   * @package   AJAX
  34   * @author    Joshua Eichorn <josh@bluga.net>
  35   * @author    Arpad Ray <arpad@php.net>
  36   * @author    David Coallier <davidc@php.net>
  37   * @author    Elizabeth Smith <auroraeosrose@gmail.com>
  38   * @copyright 2005-2008 Joshua Eichorn, Arpad Ray, David Coallier, Elizabeth Smith
  39   * @license   http://www.opensource.org/licenses/lgpl-license.php   LGPL
  40   * @version   Release: 0.5.6
  41   * @link      http://pear.php.net/package/HTML_AJAX
  42   */
  43  class HTML_AJAX
  44  {
  45      /**
  46       * An array holding the instances were exporting
  47       *
  48       * key is the exported name
  49       *
  50       * row format is 
  51       * <code>
  52       * array('className'=>'','exportedName'=>'','instance'=>'','exportedMethods=>'')
  53       * </code>
  54       *
  55       * @var object
  56       * @access private
  57       */    
  58      var $_exportedInstances = array();
  59  
  60      /**
  61       * Set the server url in the generated stubs to this value
  62       * If set to false, serverUrl will not be set
  63       * @var false|string
  64       */
  65      var $serverUrl = false;
  66  
  67      /**
  68       * What encoding your going to use for serializing data 
  69       * from php being sent to javascript.
  70       *
  71       * @var string  JSON|PHP|Null
  72       */
  73      var $serializer = 'JSON';
  74  
  75      /**
  76       * What encoding your going to use for unserializing data sent from javascript
  77       * @var string  JSON|PHP|Null
  78       */
  79      var $unserializer = 'JSON';
  80  
  81      /**
  82       * Option to use loose typing for JSON encoding
  83       * @var bool
  84       * @access public
  85       */
  86      var $jsonLooseType = true;
  87  
  88      /**
  89       * Content-type map
  90       *
  91       * Used in to automatically choose serializers as needed
  92       */
  93      var $contentTypeMap = array(
  94              'JSON'  => 'application/json',
  95              'XML'   => 'application/xml',
  96              'Null'  => 'text/plain',
  97              'Error' => 'application/error',
  98              'PHP'   => 'application/php-serialized',
  99              'Urlencoded' => 'application/x-www-form-urlencoded'
 100          );
 101      
 102      /**
 103       * This is the debug variable that we will be passing the
 104       * HTML_AJAX_Debug instance to.
 105       *
 106       * @param object HTML_AJAX_Debug
 107       */
 108      var $debug;
 109  
 110      /**
 111       * This is to tell if debug is enabled or not. If so, then
 112       * debug is called, instantiated then saves the file and such.
 113       */
 114      var $debugEnabled = false;
 115      
 116      /**
 117       * This puts the error into a session variable is set to true.
 118       * set to false by default.
 119       *
 120       * @access public
 121       */
 122      var $debugSession = false;
 123  
 124      /**
 125       * Boolean telling if the Content-Length header should be sent. 
 126       *
 127       * If your using a gzip handler on an output buffer, or run into 
 128       * any compatability problems, try disabling this.
 129       *
 130       * @access public
 131       * @var boolean
 132       */
 133      var $sendContentLength = true;
 134  
 135      /**
 136       * Make Generated code compatible with php4 by lowercasing all 
 137       * class/method names before exporting to JavaScript.
 138       *
 139       * If you have code that works on php4 but not on php5 then setting 
 140       * this flag can fix the problem. The recommended solution is too 
 141       * specify the class and method names when registering the class 
 142       * letting you have function case in php4 as well
 143       *
 144       * @access public
 145       * @var boolean
 146       */
 147      var $php4CompatCase = false;
 148  
 149      /**
 150       * Automatically pack all generated JavaScript making it smaller
 151       *
 152       * If your using output compression this might not make sense
 153       */
 154      var $packJavaScript = false;
 155  
 156      /**
 157       * Holds current payload info
 158       *
 159       * @access private
 160       * @var string
 161       */
 162      var $_payload;
 163  
 164      /**
 165       * Holds iframe id IF this is an iframe xmlhttprequest
 166       *
 167       * @access private
 168       * @var string
 169       */
 170      var $_iframe;
 171  
 172      /**
 173       * Holds the list of classes permitted to be unserialized
 174       *
 175       * @access private
 176       * @var array
 177       */
 178      var $_allowedClasses = array();
 179      
 180      /**
 181       * Holds serializer instances
 182       */
 183      var $_serializers = array();
 184      
 185      /**
 186       * PHP callbacks we're exporting
 187       */
 188      var $_validCallbacks = array();
 189  
 190      /**
 191       * Interceptor instance
 192       */
 193      var $_interceptor = false;
 194  
 195      /**
 196       * Set a class to handle requests
 197       *
 198       * @param object &$instance       An instance to export
 199       * @param mixed  $exportedName    Name used for the javascript class, 
 200       *                                if false the name of the php class is used
 201       * @param mixed  $exportedMethods If false all functions without a _ prefix 
 202       *                                are exported, if an array only the methods 
 203       *                                listed in the array are exported
 204       *
 205       * @return void
 206       */
 207      function registerClass(&$instance, $exportedName = false, 
 208          $exportedMethods = false)
 209      {
 210          $className = strtolower(get_class($instance));
 211  
 212          if ($exportedName === false) {
 213              $exportedName = get_class($instance);
 214              if ($this->php4CompatCase) {
 215                  $exportedName = strtolower($exportedName);
 216              }
 217          }
 218  
 219          if ($exportedMethods === false) {
 220              $exportedMethods = $this->_getMethodsToExport($className);
 221          }
 222  
 223  
 224          $index                                               = strtolower($exportedName);
 225          $this->_exportedInstances[$index]                    = array();
 226          $this->_exportedInstances[$index]['className']       = $className;
 227          $this->_exportedInstances[$index]['exportedName']    = $exportedName;
 228          $this->_exportedInstances[$index]['instance']        =& $instance;
 229          $this->_exportedInstances[$index]['exportedMethods'] = $exportedMethods;
 230      }
 231  
 232      /**
 233       * Get a list of methods in a class to export
 234       *
 235       * This function uses get_class_methods to get a list of callable methods, 
 236       * so if you're on PHP5 extending this class with a class you want to export 
 237       * should export its protected methods, while normally only its public methods 
 238       * would be exported. All methods starting with _ are removed from the export list.
 239       * This covers PHP4 style private by naming as well as magic methods in either PHP4 or PHP5
 240       *
 241       * @param string $className Name of the class
 242       *
 243       * @return array all methods of the class that are public
 244       * @access private
 245       */    
 246      function _getMethodsToExport($className)
 247      {
 248          $funcs = get_class_methods($className);
 249  
 250          foreach ($funcs as $key => $func) {
 251              if (strtolower($func) === $className || substr($func, 0, 1) === '_') {
 252                  unset($funcs[$key]);
 253              } else if ($this->php4CompatCase) {
 254                  $funcs[$key] = strtolower($func);
 255              }
 256          }
 257          return $funcs;
 258      }
 259  
 260      /**
 261       * Generate the client Javascript code
 262       *
 263       * @return   string   generated javascript client code
 264       */
 265      function generateJavaScriptClient()
 266      {
 267          $client = '';
 268  
 269          $names = array_keys($this->_exportedInstances);
 270          foreach ($names as $name) {
 271              $client .= $this->generateClassStub($name);
 272          }
 273          return $client;
 274      }
 275  
 276      /**
 277       * Return the stub for a class
 278       *
 279       * @param string $name name of the class to generated the stub for, 
 280       * note that this is the exported name not the php class name
 281       *
 282       * @return string javascript proxy stub code for a single class
 283       */
 284      function generateClassStub($name)
 285      {
 286          if (!isset($this->_exportedInstances[$name])) {
 287              return '';
 288          }
 289  
 290          $client  = "// Client stub for the {$this->_exportedInstances[$name]['exportedName']} PHP Class\n";
 291          $client .= "function {$this->_exportedInstances[$name]['exportedName']}(callback) {\n";
 292          $client .= "\tmode = 'sync';\n";
 293          $client .= "\tif (callback) { mode = 'async'; }\n";
 294          $client .= "\tthis.className = '{$this->_exportedInstances[$name]['exportedName']}';\n";
 295          if ($this->serverUrl) {
 296              $client .= "\tthis.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,'{$this->serverUrl}','{$this->unserializer}');\n}\n";
 297          } else {
 298              $client .= "\tthis.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,false,'{$this->unserializer}');\n}\n";
 299          }
 300          $client .= "{$this->_exportedInstances[$name]['exportedName']}.prototype  = {\n";
 301          $client .= "\tSync: function() { this.dispatcher.Sync(); }, \n";
 302          $client .= "\tAsync: function(callback) { this.dispatcher.Async(callback); },\n";
 303          foreach ($this->_exportedInstances[$name]['exportedMethods'] as $method) {
 304              $client .= $this->_generateMethodStub($method);
 305          }
 306          $client  = substr($client, 0, (strlen($client)-2))."\n";
 307          $client .= "}\n\n";
 308  
 309          if ($this->packJavaScript) {
 310                  $client = $this->packJavaScript($client);
 311          }
 312          return $client;
 313      }
 314  
 315      /**
 316       * Returns a methods stub
 317       *
 318       * @param string $method the method name
 319       *
 320       * @return string the js code
 321       * @access private
 322       */    
 323      function _generateMethodStub($method)
 324      {
 325          $stub = "\t{$method}: function() { return ".
 326              "this.dispatcher.doCall('{$method}',arguments); },\n";
 327          return $stub;
 328      }
 329  
 330      /**
 331       * Populates the current payload
 332       *
 333       * @return string the js code
 334       * @access private
 335       */    
 336      function populatePayload()
 337      {
 338          if (isset($_REQUEST['Iframe_XHR'])) {
 339              $this->_iframe = $_REQUEST['Iframe_XHR_id'];
 340              if (isset($_REQUEST['Iframe_XHR_headers']) && 
 341                  is_array($_REQUEST['Iframe_XHR_headers'])) {
 342                  foreach ($_REQUEST['Iframe_XHR_headers'] as $header) {
 343  
 344                      $array    = explode(':', $header);
 345                      $array[0] = strip_tags(strtoupper(str_replace('-', '_', $array[0])));
 346                      //only content-length and content-type can go in without an 
 347                      //http_ prefix - security
 348                      if (strpos($array[0], 'HTTP_') !== 0
 349                            && strcmp('CONTENT_TYPE', $array[0])
 350                            && strcmp('CONTENT_LENGTH', $array[0])) {
 351                          $array[0] = 'HTTP_' . $array[0];
 352                      }
 353                      $_SERVER[$array[0]] = strip_tags($array[1]);
 354                  }
 355              }
 356              $this->_payload = (isset($_REQUEST['Iframe_XHR_data']) 
 357                  ? $_REQUEST['Iframe_XHR_data'] : '');
 358  
 359              if (isset($_REQUEST['Iframe_XHR_method'])) {
 360                  $_GET['m'] = $_REQUEST['Iframe_XHR_method'];
 361              }
 362              if (isset($_REQUEST['Iframe_XHR_class'])) {
 363                  $_GET['c'] = $_REQUEST['Iframe_XHR_class'];
 364              }
 365          }
 366      }
 367  
 368      /**
 369       * Handle a ajax request if needed
 370       *
 371       * The current check is if GET variables c (class) and m (method) are set, 
 372       * more options may be available in the future
 373       *
 374       * @return boolean true if an ajax call was handled, false otherwise
 375       */
 376      function handleRequest()
 377      {
 378          set_error_handler(array(&$this,'_errorHandler'));
 379          if (function_exists('set_exception_handler')) {
 380              set_exception_handler(array(&$this,'_exceptionHandler'));
 381          }
 382          if (isset($_GET['px'])) {
 383              if ($this->_iframeGrabProxy()) {
 384                  restore_error_handler();
 385                  if (function_exists('restore_exception_handler')) {
 386                      restore_exception_handler();
 387                  }
 388                  return true;
 389              }
 390          }
 391          
 392          $class       = strtolower($this->_getVar('c'));
 393          $method      = $this->_getVar('m');
 394          $phpCallback = $this->_getVar('cb');
 395  
 396          
 397          if (!empty($class) && !empty($method)) {
 398              if (!isset($this->_exportedInstances[$class])) {
 399                  // handle error
 400                  trigger_error('Unknown class: '. $class); 
 401              }
 402              if (!in_array(($this->php4CompatCase ? strtolower($method) : $method),
 403                  $this->_exportedInstances[$class]['exportedMethods'])) {
 404                  // handle error
 405                  trigger_error('Unknown method: ' . $method);
 406              }
 407          } else if (!empty($phpCallback)) {
 408              if (strpos($phpCallback, '.') !== false) {
 409                  $phpCallback = explode('.', $phpCallback);
 410              }
 411              if (!$this->_validatePhpCallback($phpCallback)) {
 412                  restore_error_handler();
 413                  if (function_exists('restore_exception_handler')) {
 414                      restore_exception_handler();
 415                  }
 416                  return false;
 417              }
 418          } else {
 419              restore_error_handler();
 420              if (function_exists('restore_exception_handler')) {
 421                  restore_exception_handler();
 422              }
 423              return false;
 424          }
 425  
 426          // auto-detect serializer to use from content-type
 427          $type = $this->unserializer;
 428          $key  = array_search($this->_getClientPayloadContentType(),
 429              $this->contentTypeMap);
 430          if ($key) {
 431              $type = $key;
 432          }
 433          $unserializer = $this->_getSerializer($type);
 434  
 435          $args = $unserializer->unserialize($this->_getClientPayload(), $this->_allowedClasses);
 436          if (!is_array($args)) {
 437              $args = array($args);
 438          }
 439  
 440          if ($this->_interceptor !== false) {
 441              $args = $this->_processInterceptor($class, $method, $phpCallback, $args);
 442          }
 443          
 444          if (empty($phpCallback)) {
 445              $ret = call_user_func_array(array(&$this->_exportedInstances[$class]['instance'], $method), $args);
 446          } else {
 447              $ret = call_user_func_array($phpCallback, $args);
 448          }
 449          
 450          restore_error_handler();
 451          $this->_sendResponse($ret);
 452          return true;
 453      }
 454  
 455      /**
 456       * Determines the content type of the client payload
 457       *
 458       * @return string
 459       *   a MIME content type
 460       */
 461      function _getClientPayloadContentType()
 462      {
 463          //OPERA IS STUPID FIX
 464          if (isset($_SERVER['HTTP_X_CONTENT_TYPE'])) {
 465              $type = $this->_getServer('HTTP_X_CONTENT_TYPE');
 466              $pos  = strpos($type, ';');
 467  
 468              return strtolower($pos ? substr($type, 0, $pos) : $type);
 469          } else if (isset($_SERVER['CONTENT_TYPE'])) {
 470              $type = $this->_getServer('CONTENT_TYPE');
 471              $pos  = strpos($type, ';');
 472  
 473              return strtolower($pos ? substr($type, 0, $pos) : $type);
 474          }
 475          return 'text/plain';
 476      }
 477  
 478      /**
 479       * Send a reponse adding needed headers and serializing content
 480       *
 481       * Note: this method echo's output as well as setting headers to prevent caching
 482       * Iframe Detection: if this has been detected as an iframe response, it has to
 483       * be wrapped in different code and headers changed (quite a mess)
 484       *
 485       * @param mixed $response content to serialize and send
 486       *
 487       * @access private
 488       * @return void
 489       */
 490      function _sendResponse($response)
 491      {
 492          if (is_object($response) && is_a($response, 'HTML_AJAX_Response')) {
 493              $output  = $response->getPayload();
 494              $content = $response->getContentType();
 495  
 496          } elseif (is_a($response, 'PEAR_Error')) {
 497              $serializer = $this->_getSerializer('Error');
 498              $output     = $serializer->serialize(array(
 499                  'message'  => $response->getMessage(),
 500                  'userinfo' => $response->getUserInfo(),
 501                  'code'     => $response->getCode(),
 502                  'mode'     => $response->getMode()
 503                  ));
 504              $content    = $this->contentTypeMap['Error'];
 505  
 506          } else {
 507              $serializer = $this->_getSerializer($this->serializer);
 508              $output     = $serializer->serialize($response);
 509  
 510              $serializerType = $this->serializer;
 511              // let a serializer change its output type
 512              if (isset($serializer->serializerNewType)) {
 513                  $serializerType = $serializer->serializerNewType;
 514              }
 515  
 516              if (isset($this->contentTypeMap[$serializerType])) {
 517                  $content = $this->contentTypeMap[$serializerType];
 518              }
 519          }
 520          // headers to force things not to be cached:
 521          $headers = array();
 522          //OPERA IS STUPID FIX
 523          if (isset($_SERVER['HTTP_X_CONTENT_TYPE'])) {
 524              $headers['X-Content-Type'] = $content;
 525              $content                   = 'text/plain';
 526          }
 527  
 528          if ($this->_sendContentLength()) {
 529              $headers['Content-Length'] = strlen($output);
 530          }
 531  
 532          $headers['Expires']       = 'Mon, 26 Jul 1997 05:00:00 GMT';
 533          $headers['Last-Modified'] = gmdate("D, d M Y H:i:s").'GMT';
 534          $headers['Cache-Control'] = 'no-cache, must-revalidate';
 535          $headers['Pragma']        = 'no-cache';
 536          $headers['Content-Type']  = $content.'; charset=utf-8';
 537  
 538          //intercept to wrap iframe return data
 539          if ($this->_iframe) {
 540              $output                  = $this->_iframeWrapper($this->_iframe, 
 541                                           $output, $headers);
 542              $headers['Content-Type'] = 'text/html; charset=utf-8';
 543          }
 544  
 545          $this->_sendHeaders($headers);
 546          echo $output;
 547      }
 548  
 549      /**
 550       * Decide if we should send a Content-length header
 551       *
 552       * @return   bool true if it's ok to send the header, false otherwise
 553       * @access   private
 554       */
 555      function _sendContentLength() 
 556      {
 557          if (!$this->sendContentLength) {
 558              return false;
 559          }
 560          $ini_tests = array( "output_handler",
 561                              "zlib.output_compression",
 562                              "zlib.output_handler");
 563          foreach ($ini_tests as $test) {
 564              if (ini_get($test)) {
 565                  return false;
 566              }
 567          }
 568          return (ob_get_level() <= 0);
 569      }
 570  
 571      /**
 572       * Actually send a list of headers
 573       *
 574       * @param array $array list of headers to send
 575       *
 576       * @access private
 577       * @return void
 578       */
 579      function _sendHeaders($array)
 580      {
 581          foreach ($array as $header => $value) {
 582              header($header . ': ' . $value);
 583          }
 584      }
 585  
 586      /**
 587       * Get an instance of a serializer class
 588       *
 589       * @param string $type Last part of the class name
 590       *
 591       * @access private
 592       * @return HTML_AJAX_Serializer
 593       */
 594      function _getSerializer($type)
 595      {
 596          if (isset($this->_serializers[$type])) {
 597              return $this->_serializers[$type];
 598          }
 599      
 600          $class = 'HTML_AJAX_Serializer_'.$type;
 601  
 602          if ( (version_compare(phpversion(), 5, '>') && !class_exists($class, false)) 
 603              || (version_compare(phpversion(), 5, '<') && !class_exists($class)) ) {
 604              // include the class only if it isn't defined
 605              include_once "HTML/AJAX/Serializer/{$type}.php";
 606          }
 607  
 608          //handle JSON loose typing option for associative arrays
 609          if ($type == 'JSON') {
 610              $this->_serializers[$type] = new $class($this->jsonLooseType);
 611          } else {
 612              $this->_serializers[$type] = new $class();
 613          }
 614          return $this->_serializers[$type];
 615      }
 616  
 617      /**
 618       * Get payload in its submitted form, currently only supports raw post
 619       *
 620       * @access   private
 621       * @return   string   raw post data
 622       */
 623      function _getClientPayload()
 624      {
 625          if (empty($this->_payload)) {
 626              if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
 627                  $this->_payload = $GLOBALS['HTTP_RAW_POST_DATA'];
 628              } else if (function_exists('file_get_contents')) {
 629                  // both file_get_contents() and php://input require PHP >= 4.3.0
 630                  $this->_payload = file_get_contents('php://input');
 631              } else {
 632                  $this->_payload = '';
 633              }
 634          }
 635          return $this->_payload;
 636      }
 637  
 638      /**
 639       * stub for getting get vars - applies strip_tags
 640       *
 641       * @param string $var variable to get
 642       *
 643       * @access   private
 644       * @return   string   filtered _GET value
 645       */
 646      function _getVar($var)
 647      {
 648          if (!isset($_GET[$var])) {
 649              return null;
 650          } else {
 651              return strip_tags($_GET[$var]);
 652          }
 653      }
 654  
 655      /**
 656       * stub for getting server vars - applies strip_tags
 657       *
 658       * @param string $var variable to get
 659       *
 660       * @access   private
 661       * @return   string   filtered _GET value
 662       */
 663      function _getServer($var)
 664      {
 665          if (!isset($_SERVER[$var])) {
 666              return null;
 667          } else {
 668              return strip_tags($_SERVER[$var]);
 669          }
 670      }
 671  
 672      /**
 673       * Exception handler, passes them to _errorHandler to do the actual work
 674       *
 675       * @param Exception $ex Exception to be handled
 676       *
 677       * @access private
 678       * @return void
 679       */
 680      function _exceptionHandler($ex)
 681      {
 682          $this->_errorHandler($ex->getCode(), $ex->getMessage(), $ex->getFile(), $ex->getLine());
 683      }
 684        
 685  
 686      /**
 687       * Error handler that sends it errors to the client side
 688       *
 689       * @param int    $errno   Error number
 690       * @param string $errstr  Error string
 691       * @param string $errfile Error file
 692       * @param string $errline Error line
 693       *
 694       * @access private
 695       * @return void
 696       */
 697      function _errorHandler($errno, $errstr, $errfile, $errline)
 698      {
 699          if ($errno & error_reporting()) {
 700              $e          = new stdClass();
 701              $e->errNo   = $errno;
 702              $e->errStr  = $errstr;
 703              $e->errFile = $errfile;
 704              $e->errLine = $errline;
 705  
 706  
 707              $this->serializer = 'Error';
 708              $this->_sendResponse($e);
 709              if ($this->debugEnabled) {
 710                  $this->debug = new HTML_AJAX_Debug($errstr, $errline, $errno, $errfile);
 711                  if ($this->debugSession) {
 712                      $this->debug->sessionError();
 713                  }
 714                  $this->debug->_saveError();
 715              }
 716              die();
 717          }
 718      }
 719  
 720      /**
 721       * Creates html to wrap serialized info for iframe xmlhttprequest fakeout
 722       *
 723       * @param string $id      iframe instance id
 724       * @param string $data    data to pass
 725       * @param string $headers headers to pass
 726       *
 727       * @access private
 728       * @return string html page with iframe passing code
 729       */
 730      function _iframeWrapper($id, $data, $headers = array())
 731      {
 732          $string = '<html><script type="text/javascript">'."\n".
 733              'var Iframe_XHR_headers = new Object();';
 734  
 735          foreach ($headers as $label => $value) {
 736              $string .= 'Iframe_XHR_headers["'.preg_replace("/\r?\n/", "\\n", 
 737                  addslashes($label)).'"] = "'.preg_replace("/\r?\n/", "\\n", 
 738                  addslashes($value))."\";\n";
 739          }
 740          $string .= 'var Iframe_XHR_data = "' . preg_replace("/\r?\n/", "\\n", 
 741              addslashes($data)) . '";</script>'
 742              . '<body onload="parent.HTML_AJAX_IframeXHR_instances[\''.$id.'\']'
 743              . '.isLoaded(Iframe_XHR_headers, Iframe_XHR_data);"></body></html>';
 744          return $string;
 745      }
 746  
 747      /**
 748       * Handles a proxied grab request
 749       *
 750       * @return bool true to end the response, false to continue trying to handle it
 751       * @access private
 752       */
 753      function _iframeGrabProxy()
 754      {
 755          if (!isset($_REQUEST['Iframe_XHR_id'])) {
 756              trigger_error('Invalid iframe ID');
 757              return false;
 758          }
 759          $this->_iframe  = $_REQUEST['Iframe_XHR_id'];
 760          $this->_payload = (isset($_REQUEST['Iframe_XHR_data']) ? $_REQUEST['Iframe_XHR_data'] : '');
 761          $url            = urldecode($_GET['px']);
 762          $url_parts      = parse_url($url);
 763          $urlregex       = '#^https?://#i';
 764  
 765          if (!preg_match($urlregex, $url) || $url_parts['host'] != $_SERVER['HTTP_HOST']) {
 766              trigger_error('Invalid URL for grab proxy');
 767              return true;
 768          }
 769          $method = (isset($_REQUEST['Iframe_XHR_HTTP_method'])
 770              ? strtoupper($_REQUEST['Iframe_XHR_HTTP_method'])
 771              : 'GET');
 772          // validate method
 773          if ($method != 'GET' && $method != 'POST') {
 774              trigger_error('Invalid grab URL');
 775              return true;
 776          }
 777          // validate headers
 778          $headers = '';
 779          if (isset($_REQUEST['Iframe_XHR_headers'])) {
 780              foreach ($_REQUEST['Iframe_XHR_headers'] as $header) {
 781                  if (strpos($header, "\r") !== false
 782                          || strpos($header, "\n") !== false) {
 783                      trigger_error('Invalid grab header');
 784                      return true;
 785                  }
 786                  $headers .= $header . "\r\n";
 787              }
 788          }
 789          // tries to make request with file_get_contents()
 790          if (ini_get('allow_url_fopen') && version_compare(phpversion(), '5.0.0'. '>=')) {
 791              $opts = array(
 792                  $url_parts['scheme'] => array(
 793                      'method'  => $method,
 794                      'headers' => $headers,
 795                      'content' => $this->_payload
 796                  )
 797              );
 798              $ret  = @file_get_contents($url, false, stream_context_create($opts));
 799              if (!empty($ret)) {
 800                  $this->_sendResponse($ret);
 801                  return true;
 802              }
 803          }
 804          // tries to make request using the curl extension
 805          if (function_exists('curl_setopt')) {
 806              $ch = curl_init();
 807              curl_setopt($ch, CURLOPT_URL, $url);
 808              curl_setopt($ch, CURLOPT_HEADER, $headers);
 809              curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 810              $ret = curl_exec($ch);
 811              if ($ret !== false) {
 812                  curl_close($ch);
 813                  $this->_sendResponse($ret);
 814                  return true;
 815              }
 816          }
 817          if (isset($url_parts['port'])) {
 818              $port = $url_parts['port'];
 819          } else { 
 820              $port = getservbyname(strtolower($url_parts['scheme']), 'tcp');
 821              if ($port === false) {
 822                  trigger_error('Grab proxy: Unknown port or service, defaulting to 80', E_USER_WARNING);
 823                  $port = 80;
 824              }
 825          }
 826          if (!isset($url_parts['path'])) {
 827              $url_parts['path'] = '/';
 828          }
 829          if (!empty($url_parts['query'])) {
 830              $url_parts['path'] .= '?' . $url_parts['query'];
 831          }
 832          $request = "$method {$url_parts['path']} HTTP/1.0\r\n"
 833              . "Host: {$url['host']}\r\n"
 834              . "Connection: close\r\n"
 835              . "$headers\r\n";
 836          // tries to make request using the socket functions
 837          $fp = fsockopen($_SERVER['HTTP_HOST'], $port, $errno, $errstr, 4);
 838          if ($fp) {
 839              fputs($fp, $request);
 840  
 841              $ret          = '';
 842              $done_headers = false;
 843  
 844              while (!feof($fp)) {
 845                  $ret .= fgets($fp, 2048);
 846                  if ($done_headers || ($contentpos = strpos($ret, "\r\n\r\n")) === false) {
 847                      continue;
 848                  }
 849                  $done_headers = true;
 850                  $ret          = substr($ret, $contentpos + 4);
 851              }
 852              fclose($fp);
 853              $this->_sendResponse($ret);
 854              return true;
 855          }
 856          // tries to make the request using the socket extension
 857          $host = gethostbyname($url['host']);
 858          if (($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0
 859              || ($connected = socket_connect($socket, $host, $port)) < 0
 860              || ($written = socket_write($socket, $request)) < strlen($request)) {
 861               trigger_error('Grab proxy failed: ' . socket_strerror($socket));
 862               return true;
 863          }
 864  
 865          $ret          = '';
 866          $done_headers = false;
 867  
 868          while ($out = socket_read($socket, 2048)) {
 869              $ret .= $out;
 870              if ($done_headers || ($contentpos = strpos($ret, "\r\n\r\n")) === false) {
 871                  continue;
 872              }
 873              $done_headers = true;
 874              $ret          = substr($ret, $contentpos + 4);
 875          }
 876          socket_close($socket);
 877          $this->_sendResponse($ret);
 878          return true;
 879      }
 880  
 881      /**
 882       * Add a class or classes to those allowed to be unserialized
 883       *
 884       * @param mixed $classes the class or array of classes to add
 885       *
 886       * @access public
 887       * @return void
 888       */
 889      function addAllowedClasses($classes)
 890      {
 891          if (!is_array($classes)) {
 892              $this->_allowedClasses[] = $classes;
 893          } else {
 894              $this->_allowedClasses = array_merge($this->_allowedClasses, $classes);
 895          }
 896          $this->_allowedClasses = array_unique($this->_allowedClasses);
 897      }
 898      
 899      /**
 900       * Checks that the given callback is callable and allowed to be called
 901       *
 902       * @param callback $callback the callback to check
 903       *
 904       * @return bool true if the callback is valid, false otherwise
 905       * @access private
 906       */
 907      function _validatePhpCallback($callback)
 908      {
 909          if (!is_callable($callback)) {
 910              return false;
 911          }
 912          $sig = md5(serialize($callback));
 913          return isset($this->_validCallbacks[$sig]);
 914      }
 915      
 916      /**
 917       * Register a callback so it may be called from JS
 918       * 
 919       * @param callback $callback the callback to register
 920       *
 921       * @access public
 922       * @return void
 923       */
 924      function registerPhpCallback($callback)
 925      {
 926          $this->_validCallbacks[md5(serialize($callback))] = 1;
 927      }
 928  
 929      /**
 930       * Make JavaScript code smaller
 931       *
 932       * Currently just strips whitespace and comments, needs to remain fast
 933       * Strips comments only if they are not preceeded by code
 934       * Strips /*-style comments only if they span over more than one line
 935       * Since strings cannot span over multiple lines, it cannot be defeated by a 
 936       * string containing /*
 937       *
 938       * @param string $input Javascript to pack
 939       *
 940       * @access public
 941       * @return string packed javascript
 942       */
 943      function packJavaScript($input) 
 944      {
 945          $stripPregs    = array(
 946              '/^\s*$/',
 947              '/^\s*\/\/.*$/'
 948          );
 949          $blockStart    = '/^\s*\/\/\*/';
 950          $blockEnd      = '/\*\/\s*(.*)$/';
 951          $inlineComment = '/\/\*.*\*\//';
 952          $out           = '';
 953  
 954          $lines   = explode("\n", $input);
 955          $inblock = false;
 956          foreach ($lines as $line) {
 957              $keep = true;
 958              if ($inblock) {
 959                  if (preg_match($blockEnd, $line)) {
 960                      $inblock = false;
 961                      $line    = preg_match($blockEnd, '$1', $line);
 962                      $keep    = strlen($line) > 0;
 963                  }
 964              } elseif (preg_match($inlineComment, $line)) {
 965                  $keep = true;
 966              } elseif (preg_match($blockStart, $line)) {
 967                  $inblock = true;
 968                  $keep    = false;
 969              }
 970  
 971              if (!$inblock) {
 972                  foreach ($stripPregs as $preg) {
 973                      if (preg_match($preg, $line)) {
 974                          $keep = false;
 975                          break;
 976                      }
 977                  }
 978              }
 979  
 980              if ($keep && !$inblock) {
 981                  $out .= trim($line)."\n";
 982              }
 983              /* Enable to see what your striping out
 984              else {
 985                  echo $line."<br>";
 986              }//*/
 987          }
 988          $out .= "\n";
 989          return $out;
 990      }
 991  
 992      /**
 993       * Set an interceptor class
 994       *
 995       * An interceptor class runs during the process of handling a request, 
 996       * it allows you to run security checks globally. It also allows you to 
 997       * rewrite parameters
 998       *
 999       * You can throw errors and exceptions in your intercptor methods and 
1000       * they will be passed to javascript
1001       * 
1002       * You can add interceptors are 3 levels
1003       * For a particular class/method, this is done by add a method to you class 
1004       *   named ClassName_MethodName($params)
1005       * For a particular class, method ClassName($methodName,$params)
1006       * Globally, method intercept($className,$methodName,$params)
1007       * 
1008       * Only one match is done, using the most specific interceptor
1009       *
1010       * All methods have to return $params, if you want to empty all of the 
1011       * parameters return an empty array
1012       *
1013       * @param Object $instance an instance of you interceptor class
1014       *
1015       * @todo handle php callbacks
1016       * @access public
1017       * @return void
1018       */
1019      function setInterceptor($instance) 
1020      {
1021          $this->_interceptor = $instance;
1022      }
1023  
1024      /**
1025       * Attempt to intercept a call
1026       *
1027       * @param string $className  Class Name
1028       * @param string $methodName Method Name
1029       * @param string $callback   Not implemented
1030       * @param array  $params     Array of parameters to pass to the interceptor
1031       *
1032       * @todo handle php callbacks
1033       * @access private
1034       * @return array Updated params
1035       */
1036      function _processInterceptor($className,$methodName,$callback,$params) 
1037      {
1038  
1039          $m = $className.'_'.$methodName;
1040          if (method_exists($this->_interceptor, $m)) {
1041              return $this->_interceptor->$m($params);
1042          }
1043  
1044          $m = $className;
1045          if (method_exists($this->_interceptor, $m)) {
1046              return $this->_interceptor->$m($methodName, $params);
1047          }
1048  
1049          $m = 'intercept';
1050          if (method_exists($this->_interceptor, $m)) {
1051              return $this->_interceptor->$m($className, $methodName, $params);
1052          }
1053  
1054          return $params;
1055      }
1056  }
1057  
1058  /**
1059   * PHP 4 compat function for interface/class exists
1060   *
1061   * @param string $class    Class name
1062   * @param bool   $autoload Should the autoloader be called
1063   *
1064   * @access public
1065   * @return bool
1066   */
1067  function HTML_AJAX_Class_exists($class, $autoload) 
1068  {
1069      if (function_exists('interface_exists')) {
1070          return class_exists($class, $autoload);
1071      } else {
1072          return class_exists($class);
1073      }
1074  }
1075  /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
1076  ?>


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