home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / HTML / AJAX.php next >
Encoding:
PHP Script  |  2008-07-02  |  34.1 KB  |  1,077 lines

  1. <?php
  2. /**
  3.  * OO AJAX Implementation for PHP
  4.  *
  5.  * SVN Rev: $Id$
  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. ?>
  1077.