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

Documentation generated on Mon, 11 Mar 2019 14:40:41 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.