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

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