AJAX
[ class tree: AJAX ] [ index: AJAX ] [ all elements ]

Source for file AJAX.php

Documentation is available at AJAX.php

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

Documentation generated on Mon, 11 Mar 2019 15:19:36 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.