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

Source for file Request.php

Documentation is available at Request.php

  1. <?php
  2. // +-----------------------------------------------------------------------+
  3. // | Copyright (c) 2002-2003, Richard Heyes                                |
  4. // | All rights reserved.                                                  |
  5. // |                                                                       |
  6. // | Redistribution and use in source and binary forms, with or without    |
  7. // | modification, are permitted provided that the following conditions    |
  8. // | are met:                                                              |
  9. // |                                                                       |
  10. // | o Redistributions of source code must retain the above copyright      |
  11. // |   notice, this list of conditions and the following disclaimer.       |
  12. // | o Redistributions in binary form must reproduce the above copyright   |
  13. // |   notice, this list of conditions and the following disclaimer in the |
  14. // |   documentation and/or other materials provided with the distribution.|
  15. // | o The names of the authors may not be used to endorse or promote      |
  16. // |   products derived from this software without specific prior written  |
  17. // |   permission.                                                         |
  18. // |                                                                       |
  19. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
  20. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
  21. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  22. // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
  23. // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  24. // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
  25. // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  27. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
  28. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  29. // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
  30. // |                                                                       |
  31. // +-----------------------------------------------------------------------+
  32. // | Author: Richard Heyes <richard@phpguru.org>                           |
  33. // +-----------------------------------------------------------------------+
  34. //
  35. // $Id: Request.php,v 1.36 2004/04/29 13:15:28 avb Exp $
  36. //
  37. // HTTP_Request Class
  38. //
  39. // Simple example, (Fetches yahoo.com and displays it):
  40. //
  41. // $a = &new HTTP_Request('http://www.yahoo.com/');
  42. // $a->sendRequest();
  43. // echo $a->getResponseBody();
  44. //
  45.  
  46. require_once 'PEAR.php';
  47. require_once 'Net/Socket.php';
  48. require_once 'Net/URL.php';
  49.  
  50. define('HTTP_REQUEST_METHOD_GET',     'GET',     true);
  51. define('HTTP_REQUEST_METHOD_HEAD',    'HEAD',    true);
  52. define('HTTP_REQUEST_METHOD_POST',    'POST',    true);
  53. define('HTTP_REQUEST_METHOD_PUT',     'PUT',     true);
  54. define('HTTP_REQUEST_METHOD_DELETE',  'DELETE',  true);
  55. define('HTTP_REQUEST_METHOD_OPTIONS''OPTIONS'true);
  56. define('HTTP_REQUEST_METHOD_TRACE',   'TRACE',   true);
  57.  
  58. define('HTTP_REQUEST_HTTP_VER_1_0''1.0'true);
  59. define('HTTP_REQUEST_HTTP_VER_1_1''1.1'true);
  60.  
  61. class HTTP_Request {
  62.  
  63.     /**
  64.     * Instance of Net_URL
  65.     * @var object 
  66.     */
  67.     var $_url;
  68.  
  69.     /**
  70.     * Type of request
  71.     * @var string 
  72.     */
  73.     var $_method;
  74.  
  75.     /**
  76.     * HTTP Version
  77.     * @var string 
  78.     */
  79.     var $_http;
  80.  
  81.     /**
  82.     * Request headers
  83.     * @var array 
  84.     */
  85.     var $_requestHeaders;
  86.  
  87.     /**
  88.     * Basic Auth Username
  89.     * @var string 
  90.     */
  91.     var $_user;
  92.     
  93.     /**
  94.     * Basic Auth Password
  95.     * @var string 
  96.     */
  97.     var $_pass;
  98.  
  99.     /**
  100.     * Socket object
  101.     * @var object 
  102.     */
  103.     var $_sock;
  104.     
  105.     /**
  106.     * Proxy server
  107.     * @var string 
  108.     */
  109.     var $_proxy_host;
  110.     
  111.     /**
  112.     * Proxy port
  113.     * @var integer 
  114.     */
  115.     var $_proxy_port;
  116.     
  117.     /**
  118.     * Proxy username
  119.     * @var string 
  120.     */
  121.     var $_proxy_user;
  122.     
  123.     /**
  124.     * Proxy password
  125.     * @var string 
  126.     */
  127.     var $_proxy_pass;
  128.  
  129.     /**
  130.     * Post data
  131.     * @var mixed 
  132.     */
  133.     var $_postData;
  134.  
  135.    /**
  136.     * Files to post
  137.     * @var array 
  138.     */
  139.     var $_postFiles = array();
  140.  
  141.     /**
  142.     * Connection timeout.
  143.     * @var integer 
  144.     */
  145.     var $_timeout;
  146.     
  147.     /**
  148.     * HTTP_Response object
  149.     * @var object 
  150.     */
  151.     var $_response;
  152.     
  153.     /**
  154.     * Whether to allow redirects
  155.     * @var boolean 
  156.     */
  157.     var $_allowRedirects;
  158.     
  159.     /**
  160.     * Maximum redirects allowed
  161.     * @var integer 
  162.     */
  163.     var $_maxRedirects;
  164.     
  165.     /**
  166.     * Current number of redirects
  167.     * @var integer 
  168.     */
  169.     var $_redirects;
  170.  
  171.    /**
  172.     * Whether to append brackets [] to array variables
  173.     * @var bool 
  174.     */
  175.     var $_useBrackets = true;
  176.  
  177.    /**
  178.     * Attached listeners
  179.     * @var array 
  180.     */
  181.     var $_listeners = array();
  182.  
  183.    /**
  184.     * Whether to save response body in response object property
  185.     * @var bool 
  186.     */
  187.     var $_saveBody = true;
  188.  
  189.    /**
  190.     * Timeout for reading from socket (array(seconds, microseconds))
  191.     * @var array 
  192.     */
  193.     var $_readTimeout = null;
  194.  
  195.    /**
  196.     * Options to pass to Net_Socket::connect. See stream_context_create
  197.     * @var array 
  198.     */
  199.     var $_socketOptions = null;
  200.  
  201.     /**
  202.     * Constructor
  203.     *
  204.     * Sets up the object
  205.     * @param $url The url to fetch/access
  206.     * @param $params Associative array of parameters which can be:
  207.     *                   method         - Method to use, GET, POST etc
  208.     *                   http           - HTTP Version to use, 1.0 or 1.1
  209.     *                   user           - Basic Auth username
  210.     *                   pass           - Basic Auth password
  211.     *                   proxy_host     - Proxy server host
  212.     *                   proxy_port     - Proxy server port
  213.     *                   proxy_user     - Proxy auth username
  214.     *                   proxy_pass     - Proxy auth password
  215.     *                   timeout        - Connection timeout in seconds.
  216.     *                   allowRedirects - Whether to follow redirects or not
  217.     *                   maxRedirects   - Max number of redirects to follow
  218.     *                   useBrackets    - Whether to append [] to array variable names
  219.     *                   saveBody       - Whether to save response body in response object property
  220.     * @access public
  221.     */
  222.     function HTTP_Request($url ''$params = array())
  223.     {
  224.         $this->_sock           &new Net_Socket();
  225.         $this->_method         =  HTTP_REQUEST_METHOD_GET;
  226.         $this->_http           =  HTTP_REQUEST_HTTP_VER_1_1;
  227.         $this->_requestHeaders = array();
  228.         $this->_postData       = null;
  229.  
  230.         $this->_user = null;
  231.         $this->_pass = null;
  232.  
  233.         $this->_proxy_host = null;
  234.         $this->_proxy_port = null;
  235.         $this->_proxy_user = null;
  236.         $this->_proxy_pass = null;
  237.  
  238.         $this->_allowRedirects = false;
  239.         $this->_maxRedirects   = 3;
  240.         $this->_redirects      = 0;
  241.  
  242.         $this->_timeout  = null;
  243.         $this->_response = null;
  244.  
  245.         foreach ($params as $key => $value{
  246.             $this->{'_' $key$value;
  247.         }
  248.  
  249.         if (!empty($url)) {
  250.             $this->setURL($url);
  251.         }
  252.  
  253.         // Default useragent
  254.         $this->addHeader('User-Agent''PEAR HTTP_Request class ( http://pear.php.net/ )');
  255.  
  256.         // Default Content-Type
  257.         $this->addHeader('Content-Type''application/x-www-form-urlencoded');
  258.  
  259.         // Make sure keepalives dont knobble us
  260.         $this->addHeader('Connection''close');
  261.  
  262.         // Basic authentication
  263.         if (!empty($this->_user)) {
  264.             $this->_requestHeaders['Authorization''Basic ' base64_encode($this->_user . ':' $this->_pass);
  265.         }
  266.  
  267.         // Use gzip encoding if possible
  268.         if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) {
  269.             $this->addHeader('Accept-Encoding''gzip');
  270.         }
  271.     }
  272.     
  273.     /**
  274.     * Generates a Host header for HTTP/1.1 requests
  275.     *
  276.     * @access private
  277.     * @return string 
  278.     */
  279.     function _generateHostHeader()
  280.     {
  281.         if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol'http'== 0{
  282.             $host $this->_url->host . ':' $this->_url->port;
  283.  
  284.         elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol'https'== 0{
  285.             $host $this->_url->host . ':' $this->_url->port;
  286.  
  287.         elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol'https'== 0 AND strpos($this->_url->url':443'!== false{
  288.             $host $this->_url->host . ':' $this->_url->port;
  289.         
  290.         else {
  291.             $host $this->_url->host;
  292.         }
  293.  
  294.         return $host;
  295.     }
  296.     
  297.     /**
  298.     * Resets the object to its initial state (DEPRECATED).
  299.     * Takes the same parameters as the constructor.
  300.     *
  301.     * @param  string $url    The url to be requested
  302.     * @param  array  $params Associative array of parameters
  303.     *                         (see constructor for details)
  304.     * @access public
  305.     * @deprecated deprecated since 1.2, call the constructor if this is necessary
  306.     */
  307.     function reset($url$params = array())
  308.     {
  309.         $this->HTTP_Request($url$params);
  310.     }
  311.  
  312.     /**
  313.     * Sets the URL to be requested
  314.     *
  315.     * @param  string The url to be requested
  316.     * @access public
  317.     */
  318.     function setURL($url)
  319.     {
  320.         $this->_url = &new Net_URL($url$this->_useBrackets);
  321.  
  322.         // If port is 80 and protocol is https, assume port 443 is to be used
  323.         // This does mean you can't send an https request to port 80 without
  324.         // some fudge. (mmm...)
  325.         if (strcasecmp($this->_url->protocol'https'== 0 AND $this->_url->port == 80{
  326.             $this->_url->port = 443;
  327.         }
  328.         if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http{
  329.             $this->addHeader('Host'$this->_generateHostHeader());
  330.         }
  331.     }
  332.     
  333.     /**
  334.     * Sets a proxy to be used
  335.     *
  336.     * @param string     Proxy host
  337.     * @param int        Proxy port
  338.     * @param string     Proxy username
  339.     * @param string     Proxy password
  340.     * @access public
  341.     */
  342.     function setProxy($host$port = 8080$user = null$pass = null)
  343.     {
  344.         $this->_proxy_host = $host;
  345.         $this->_proxy_port = $port;
  346.         $this->_proxy_user = $user;
  347.         $this->_proxy_pass = $pass;
  348.  
  349.         if (!empty($user)) {
  350.             $this->addHeader('Proxy-Authorization''Basic ' base64_encode($user ':' $pass));
  351.         }
  352.     }
  353.  
  354.     /**
  355.     * Sets basic authentication parameters
  356.     *
  357.     * @param string     Username
  358.     * @param string     Password
  359.     */
  360.     function setBasicAuth($user$pass)
  361.     {
  362.         $this->_user = $user;
  363.         $this->_pass = $pass;
  364.  
  365.         $this->addHeader('Authorization''Basic ' base64_encode($user ':' $pass));
  366.     }
  367.  
  368.     /**
  369.     * Sets the method to be used, GET, POST etc.
  370.     *
  371.     * @param string     Method to use. Use the defined constants for this
  372.     * @access public
  373.     */
  374.     function setMethod($method)
  375.     {
  376.         $this->_method = $method;
  377.     }
  378.  
  379.     /**
  380.     * Sets the HTTP version to use, 1.0 or 1.1
  381.     *
  382.     * @param string     Version to use. Use the defined constants for this
  383.     * @access public
  384.     */
  385.     function setHttpVer($http)
  386.     {
  387.         $this->_http = $http;
  388.     }
  389.  
  390.     /**
  391.     * Adds a request header
  392.     *
  393.     * @param string     Header name
  394.     * @param string     Header value
  395.     * @access public
  396.     */
  397.     function addHeader($name$value)
  398.     {
  399.         $this->_requestHeaders[$name$value;
  400.     }
  401.  
  402.     /**
  403.     * Removes a request header
  404.     *
  405.     * @param string     Header name to remove
  406.     * @access public
  407.     */
  408.     function removeHeader($name)
  409.     {
  410.         if (isset($this->_requestHeaders[$name])) {
  411.             unset($this->_requestHeaders[$name]);
  412.         }
  413.     }
  414.  
  415.     /**
  416.     * Adds a querystring parameter
  417.     *
  418.     * @param string     Querystring parameter name
  419.     * @param string     Querystring parameter value
  420.     * @param bool       Whether the value is already urlencoded or not, default = not
  421.     * @access public
  422.     */
  423.     function addQueryString($name$value$preencoded = false)
  424.     {
  425.         $this->_url->addQueryString($name$value$preencoded);
  426.     }    
  427.     
  428.     /**
  429.     * Sets the querystring to literally what you supply
  430.     *
  431.     * @param string     The querystring data. Should be of the format foo=bar&x=y etc
  432.     * @param bool       Whether data is already urlencoded or not, default = already encoded
  433.     * @access public
  434.     */
  435.     function addRawQueryString($querystring$preencoded = true)
  436.     {
  437.         $this->_url->addRawQueryString($querystring$preencoded);
  438.     }
  439.  
  440.     /**
  441.     * Adds postdata items
  442.     *
  443.     * @param string     Post data name
  444.     * @param string     Post data value
  445.     * @param bool       Whether data is already urlencoded or not, default = not
  446.     * @access public
  447.     */
  448.     function addPostData($name$value$preencoded = false)
  449.     {
  450.         if ($preencoded{
  451.             $this->_postData[$name$value;
  452.         else {
  453.             $this->_postData[$nameis_array($value)array_map('urlencode'$value)urlencode($value);
  454.         }
  455.     }
  456.  
  457.    /**
  458.     * Adds a file to upload
  459.     * 
  460.     * This also changes content-type to 'multipart/form-data' for proper upload
  461.     * 
  462.     * @access public
  463.     * @param  string    variable name
  464.     * @param  mixed     file name(s)
  465.     * @param  mixed     content-type(s) of file(s) being uploaded
  466.     * @return bool      true on success
  467.     * @throws PEAR_Error
  468.     */
  469.     function addFile($name$filename$contentType 'application/octet-stream')
  470.     {
  471.         if (!is_array($filename&& !is_readable($filename)) {
  472.             return PEAR::raiseError("File '{$filename}' is not readable");
  473.         elseif (is_array($filename)) {
  474.             foreach ($filename as $name{
  475.                 if (!is_readable($name)) {
  476.                     return PEAR::raiseError("File '{$name}' is not readable");
  477.                 }
  478.             }
  479.         }
  480.         $this->addHeader('Content-Type''multipart/form-data');
  481.         $this->_postFiles[$name= array(
  482.             'name' => $filename,
  483.             'type' => $contentType
  484.         );
  485.         return true;
  486.     }
  487.  
  488.     /**
  489.     * Adds raw postdata
  490.     *
  491.     * @param string     The data
  492.     * @param bool       Whether data is preencoded or not, default = already encoded
  493.     * @access public
  494.     */
  495.     function addRawPostData($postdata$preencoded = true)
  496.     {
  497.         $this->_postData = $preencoded $postdata urlencode($postdata);
  498.     }
  499.  
  500.     /**
  501.     * Clears any postdata that has been added (DEPRECATED).
  502.     * 
  503.     * Useful for multiple request scenarios.
  504.     *
  505.     * @access public
  506.     * @deprecated deprecated since 1.2
  507.     */
  508.     function clearPostData()
  509.     {
  510.         $this->_postData = null;
  511.     }
  512.  
  513.     /**
  514.     * Appends a cookie to "Cookie:" header
  515.     * 
  516.     * @param string $name cookie name
  517.     * @param string $value cookie value
  518.     * @access public
  519.     */
  520.     function addCookie($name$value)
  521.     {
  522.         $cookies = isset($this->_requestHeaders['Cookie']$this->_requestHeaders['Cookie']'; ' '';
  523.         $this->addHeader('Cookie'$cookies $name '=' $value);
  524.     }
  525.     
  526.     /**
  527.     * Clears any cookies that have been added (DEPRECATED).
  528.     * 
  529.     * Useful for multiple request scenarios
  530.     *
  531.     * @access public
  532.     * @deprecated deprecated since 1.2
  533.     */
  534.     function clearCookies()
  535.     {
  536.         $this->removeHeader('Cookie');
  537.     }
  538.  
  539.     /**
  540.     * Sends the request
  541.     *
  542.     * @access public
  543.     * @param  bool   Whether to store response body in Response object property,
  544.     *                 set this to false if downloading a LARGE file and using a Listener
  545.     * @return mixed  PEAR error on error, true otherwise
  546.     */
  547.     function sendRequest($saveBody = true)
  548.     {
  549.         if (!is_a($this->_url'Net_URL')) {
  550.             return PEAR::raiseError('No URL given.');
  551.         }
  552.  
  553.         $host = isset($this->_proxy_host$this->_proxy_host : $this->_url->host;
  554.         $port = isset($this->_proxy_port$this->_proxy_port : $this->_url->port;
  555.  
  556.         // 4.3.0 supports SSL connections using OpenSSL. The function test determines
  557.         // we running on at least 4.3.0
  558.         if (strcasecmp($this->_url->protocol'https'== 0 AND function_exists('file_get_contents'AND extension_loaded('openssl')) {
  559.             if (isset($this->_proxy_host)) {
  560.                 return PEAR::raiseError('HTTPS proxies are not supported.');
  561.             }
  562.             $host 'ssl://' $host;
  563.         }
  564.  
  565.         // If this is a second request, we may get away without
  566.         // re-connecting if they're on the same server
  567.         if (PEAR::isError($err $this->_sock->connect($host$portnull$this->_timeout$this->_socketOptions)) ||
  568.             PEAR::isError($err $this->_sock->write($this->_buildRequest()))) {
  569.  
  570.             return $err;
  571.         }
  572.         if (!empty($this->_readTimeout)) {
  573.             $this->_sock->setTimeout($this->_readTimeout[0]$this->_readTimeout[1]);
  574.         }
  575.  
  576.         $this->_notify('sentRequest');
  577.  
  578.         // Read the response
  579.         $this->_response = &new HTTP_Response($this->_sock$this->_listeners);
  580.         if (PEAR::isError($err $this->_response->process($this->_saveBody && $saveBody)) ) {
  581.             return $err;
  582.         }
  583.  
  584.         // Check for redirection
  585.         // Bugfix (PEAR) bug #18, 6 oct 2003 by Dave Mertens (headers are also stored lowercase, so we're gonna use them here)
  586.         // some non RFC2616 compliant servers (scripts) are returning lowercase headers ('location: xxx')
  587.         if (    $this->_allowRedirects
  588.             AND $this->_redirects <= $this->_maxRedirects
  589.             AND $this->getResponseCode(> 300
  590.             AND $this->getResponseCode(< 399
  591.             AND !empty($this->_response->_headers['location'])) {
  592.  
  593.             
  594.             $redirect $this->_response->_headers['location'];
  595.  
  596.             // Absolute URL
  597.             if (preg_match('/^https?:\/\//i'$redirect)) {
  598.                 $this->_url = &new Net_URL($redirect);
  599.                 $this->addHeader('Host'$this->_generateHostHeader());
  600.             // Absolute path
  601.             elseif ($redirect{0== '/'{
  602.                 $this->_url->path = $redirect;
  603.             
  604.             // Relative path
  605.             elseif (substr($redirect03== '../' OR substr($redirect02== './'{
  606.                 if (substr($this->_url->path-1== '/'{
  607.                     $redirect $this->_url->path . $redirect;
  608.                 else {
  609.                     $redirect dirname($this->_url->path'/' $redirect;
  610.                 }
  611.                 $redirect = Net_URL::resolvePath($redirect);
  612.                 $this->_url->path = $redirect;
  613.                 
  614.             // Filename, no path
  615.             else {
  616.                 if (substr($this->_url->path-1== '/'{
  617.                     $redirect $this->_url->path . $redirect;
  618.                 else {
  619.                     $redirect dirname($this->_url->path'/' $redirect;
  620.                 }
  621.                 $this->_url->path = $redirect;
  622.             }
  623.  
  624.             $this->_redirects++;
  625.             return $this->sendRequest($saveBody);
  626.  
  627.         // Too many redirects
  628.         elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects{
  629.             return PEAR::raiseError('Too many redirects');
  630.         }
  631.         
  632.         return true;
  633.     }
  634.  
  635.     /**
  636.     * Returns the response code
  637.     *
  638.     * @access public
  639.     * @return mixed     Response code, false if not set
  640.     */
  641.     function getResponseCode()
  642.     {
  643.         return isset($this->_response->_code$this->_response->_code : false;
  644.     }
  645.  
  646.     /**
  647.     * Returns either the named header or all if no name given
  648.     *
  649.     * @access public
  650.     * @param string     The header name to return, do not set to get all headers
  651.     * @return mixed     either the value of $headername (false if header is not present)
  652.     *                    or an array of all headers
  653.     */
  654.     function getResponseHeader($headername = null)
  655.     {
  656.         if (!isset($headername)) {
  657.             return isset($this->_response->_headers)$this->_response->_headers: array();
  658.         else {
  659.             return isset($this->_response->_headers[$headername]$this->_response->_headers[$headername: false;
  660.         }
  661.     }
  662.  
  663.     /**
  664.     * Returns the body of the response
  665.     *
  666.     * @access public
  667.     * @return mixed     response body, false if not set
  668.     */
  669.     function getResponseBody()
  670.     {
  671.         return isset($this->_response->_body$this->_response->_body : false;
  672.     }
  673.  
  674.     /**
  675.     * Returns cookies set in response
  676.     * 
  677.     * @access public
  678.     * @return mixed     array of response cookies, false if none are present
  679.     */
  680.     function getResponseCookies()
  681.     {
  682.         return isset($this->_response->_cookies$this->_response->_cookies : false;
  683.     }
  684.  
  685.     /**
  686.     * Builds the request string
  687.     *
  688.     * @access private
  689.     * @return string The request string
  690.     */
  691.     function _buildRequest()
  692.     {
  693.         $querystring ($querystring $this->_url->getQueryString()) '?' $querystring '';
  694.  
  695.         $host = isset($this->_proxy_host$this->_url->protocol . '://' $this->_url->host : '';
  696.         $port (isset($this->_proxy_hostAND $this->_url->port != 80':' $this->_url->port : '';
  697.         $path (empty($this->_url->path)'/'$this->_url->path$querystring;
  698.         $url  $host $port $path;
  699.  
  700.         $request $this->_method . ' ' $url ' HTTP/' $this->_http . "\r\n";
  701.  
  702.         if ('multipart/form-data' == $this->_requestHeaders['Content-Type']{
  703.             $boundary 'HTTP_Request_' md5(uniqid('request'microtime());
  704.             $this->addHeader('Content-Type''multipart/form-data; boundary=' $boundary);
  705.         }
  706.  
  707.         // Request Headers
  708.         if (!empty($this->_requestHeaders)) {
  709.             foreach ($this->_requestHeaders as $name => $value{
  710.                 $request .= $name ': ' $value "\r\n";
  711.             }
  712.         }
  713.  
  714.         // Post data if it's an array
  715.         if ((!empty($this->_postData&& is_array($this->_postData)) || !empty($this->_postFiles)) {
  716.             // multipart request, probably with file uploads
  717.             if (isset($boundary)) {
  718.                 $postdata '';
  719.                 foreach ($this->_postData as $name => $value{
  720.                     if (is_array($value)) {
  721.                         foreach ($value as $k => $v{
  722.                             $postdata .= '--' $boundary "\r\n";
  723.                             $postdata .= 'Content-Disposition: form-data; name="' $name ($this->_useBrackets? '[' $k ']''''"';
  724.                             $postdata .= "\r\n\r\n" urldecode($v"\r\n";
  725.                         }
  726.                     else {
  727.                         $postdata .= '--' $boundary "\r\n";
  728.                         $postdata .= 'Content-Disposition: form-data; name="' $name '"';
  729.                         $postdata .= "\r\n\r\n" urldecode($value"\r\n";
  730.                     }
  731.                 }
  732.                 foreach ($this->_postFiles as $name => $value{
  733.                     if (is_array($value['name'])) {
  734.                         $varname       $name ($this->_useBrackets? '[]''');
  735.                     else {
  736.                         $varname       $name;
  737.                         $value['name'= array($value['name']);
  738.                     }
  739.                     foreach ($value['name'as $key => $filename{
  740.                         $fp   fopen($filename'r');
  741.                         $data fread($fpfilesize($filename));
  742.                         fclose($fp);
  743.                         $basename basename($filename);
  744.                         $type     is_array($value['type'])@$value['type'][$key]$value['type'];
  745.  
  746.                         $postdata .= '--' $boundary "\r\n";
  747.                         $postdata .= 'Content-Disposition: form-data; name="' $varname '"; filename="' $basename '"';
  748.                         $postdata .= "\r\nContent-Type: " $type;
  749.                         $postdata .= "\r\n\r\n" $data "\r\n";
  750.                     }
  751.                 }
  752.                 $postdata .= '--' $boundary "\r\n";
  753.             else {
  754.                 foreach($this->_postData as $name => $value{
  755.                     if (is_array($value)) {
  756.                         foreach ($value as $k => $v{
  757.                             $postdata[$this->_useBrackets? sprintf('%s[%s]=%s'$name$k$v)$name '=' $v;
  758.                         }
  759.                     else {
  760.                         $postdata[$name '=' $value;
  761.                     }
  762.                 }
  763.     
  764.                 $postdata implode('&'$postdata);
  765.             }
  766.             $request .= 'Content-Length: ' strlen($postdata"\r\n\r\n";
  767.             $request .= $postdata;
  768.  
  769.         // Post data if it's raw
  770.         elseif(!empty($this->_postData)) {
  771.             $request .= 'Content-Length: ' strlen($this->_postData"\r\n\r\n";
  772.             $request .= $this->_postData;
  773.  
  774.         // No post data, so simply add a final CRLF
  775.         else {
  776.             $request .= "\r\n";
  777.         }
  778.         
  779.         return $request;
  780.     }
  781.  
  782.  
  783.    /**
  784.     * Adds a Listener to the list of listeners that are notified of
  785.     * the object's events
  786.     * 
  787.     * @param    object   HTTP_Request_Listener instance to attach
  788.     * @return   boolean  whether the listener was successfully attached
  789.     * @access   public
  790.     */
  791.     function attach(&$listener)
  792.     {
  793.         if (!is_a($listener'HTTP_Request_Listener')) {
  794.             return false;
  795.         }
  796.         $this->_listeners[$listener->getId()=$listener;
  797.         return true;
  798.     }
  799.  
  800.  
  801.    /**
  802.     * Removes a Listener from the list of listeners
  803.     * 
  804.     * @param    object   HTTP_Request_Listener instance to detach
  805.     * @return   boolean  whether the listener was successfully detached
  806.     * @access   public
  807.     */
  808.     function detach(&$listener)
  809.     {
  810.         if (!is_a($listener'HTTP_Request_Listener'|| 
  811.             !isset($this->_listeners[$listener->getId()])) {
  812.             return false;
  813.         }
  814.         unset($this->_listeners[$listener->getId()]);
  815.         return true;
  816.     }
  817.  
  818.  
  819.    /**
  820.     * Notifies all registered listeners of an event.
  821.     * 
  822.     * Events sent by HTTP_Request object
  823.     * 'sentRequest': after the request was sent
  824.     * Events sent by HTTP_Response object
  825.     * 'gotHeaders': after receiving response headers (headers are passed in $data)
  826.     * 'tick': on receiving a part of response body (the part is passed in $data)
  827.     * 'gzTick': on receiving a gzip-encoded part of response body (ditto)
  828.     * 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
  829.     * 
  830.     * @param    string  Event name
  831.     * @param    mixed   Additional data
  832.     * @access   private
  833.     */
  834.     function _notify($event$data = null)
  835.     {
  836.         foreach (array_keys($this->_listenersas $id{
  837.             $this->_listeners[$id]->update($this$event$data);
  838.         }
  839.     }
  840. }
  841.  
  842.  
  843. /**
  844. * Response class to complement the Request class
  845. */
  846. class HTTP_Response
  847. {
  848.     /**
  849.     * Socket object
  850.     * @var object 
  851.     */
  852.     var $_sock;
  853.  
  854.     /**
  855.     * Protocol
  856.     * @var string 
  857.     */
  858.     var $_protocol;
  859.     
  860.     /**
  861.     * Return code
  862.     * @var string 
  863.     */
  864.     var $_code;
  865.     
  866.     /**
  867.     * Response headers
  868.     * @var array 
  869.     */
  870.     var $_headers;
  871.  
  872.     /**
  873.     * Cookies set in response
  874.     * @var array 
  875.     */
  876.     var $_cookies;
  877.  
  878.     /**
  879.     * Response body
  880.     * @var string 
  881.     */
  882.     var $_body '';
  883.  
  884.    /**
  885.     * Used by _readChunked(): remaining length of the current chunk
  886.     * @var string 
  887.     */
  888.     var $_chunkLength = 0;
  889.  
  890.    /**
  891.     * Attached listeners
  892.     * @var array 
  893.     */
  894.     var $_listeners = array();
  895.  
  896.     /**
  897.     * Constructor
  898.     *
  899.     * @param  object Net_Socket     socket to read the response from
  900.     * @param  array                 listeners attached to request
  901.     * @return mixed PEAR Error on error, true otherwise
  902.     */
  903.     function HTTP_Response(&$sock&$listeners)
  904.     {
  905.         $this->_sock      =$sock;
  906.         $this->_listeners =$listeners;
  907.     }
  908.  
  909.  
  910.    /**
  911.     * Processes a HTTP response
  912.     * 
  913.     * This extracts response code, headers, cookies and decodes body if it
  914.     * was encoded in some way
  915.     *
  916.     * @access public
  917.     * @param  bool      Whether to store response body in object property, set
  918.     *                    this to false if downloading a LARGE file and using a Listener.
  919.     *                    This is assumed to be true if body is gzip-encoded.
  920.     * @throws PEAR_Error
  921.     * @return mixed     true on success, PEAR_Error in case of malformed response
  922.     */
  923.     function process($saveBody = true)
  924.     {
  925.         do {
  926.             $line $this->_sock->readLine();
  927.             if (sscanf($line'HTTP/%s %s'$http_version$returncode!= 2{
  928.                 return PEAR::raiseError('Malformed response.');
  929.             else {
  930.                 $this->_protocol 'HTTP/' $http_version;
  931.                 $this->_code     intval($returncode);
  932.             }
  933.             while ('' !== ($header $this->_sock->readLine())) {
  934.                 $this->_processHeader($header);
  935.             }
  936.         while (100 == $this->_code);
  937.  
  938.         $this->_notify('gotHeaders'$this->_headers);
  939.  
  940.         // If response body is present, read it and decode
  941.         $chunked = isset($this->_headers['transfer-encoding']&& ('chunked' == $this->_headers['transfer-encoding']);
  942.         $gzipped = isset($this->_headers['content-encoding']&& ('gzip' == $this->_headers['content-encoding']);
  943.         $hasBody = false;
  944.         while (!$this->_sock->eof()) {
  945.             if ($chunked{
  946.                 $data $this->_readChunked();
  947.             else {
  948.                 $data $this->_sock->read(4096);
  949.             }
  950.             if ('' != $data{
  951.                 $hasBody = true;
  952.                 if ($saveBody || $gzipped{
  953.                     $this->_body .= $data;
  954.                 }
  955.                 $this->_notify($gzipped'gzTick''tick'$data);
  956.             }
  957.         }
  958.         if ($hasBody{
  959.             // Uncompress the body if needed
  960.             if ($gzipped{
  961.                 $this->_body gzinflate(substr($this->_body10));
  962.                 $this->_notify('gotBody'$this->_body);
  963.             else {
  964.                 $this->_notify('gotBody');
  965.             }
  966.         }
  967.         return true;
  968.     }
  969.  
  970.  
  971.    /**
  972.     * Processes the response header
  973.     *
  974.     * @access private
  975.     * @param  string    HTTP header
  976.     */
  977.     function _processHeader($header)
  978.     {
  979.         list($headername$headervalueexplode(':'$header2);
  980.         $headername_i strtolower($headername);
  981.         $headervalue  ltrim($headervalue);
  982.         
  983.         if ('set-cookie' != $headername_i{
  984.             $this->_headers[$headername]   $headervalue;
  985.             $this->_headers[$headername_i$headervalue;
  986.         else {
  987.             $this->_parseCookie($headervalue);
  988.         }
  989.     }
  990.  
  991.  
  992.    /**
  993.     * Parse a Set-Cookie header to fill $_cookies array
  994.     *
  995.     * @access private
  996.     * @param  string    value of Set-Cookie header
  997.     */
  998.     function _parseCookie($headervalue)
  999.     {
  1000.         $cookie = array(
  1001.             'expires' => null,
  1002.             'domain'  => null,
  1003.             'path'    => null,
  1004.             'secure'  => false
  1005.         );
  1006.  
  1007.         // Only a name=value pair
  1008.         if (!strpos($headervalue';')) {
  1009.             $pos strpos($headervalue'=');
  1010.             $cookie['name']  trim(substr($headervalue0$pos));
  1011.             $cookie['value'trim(substr($headervalue$pos + 1));
  1012.  
  1013.         // Some optional parameters are supplied
  1014.         else {
  1015.             $elements explode(';'$headervalue);
  1016.             $pos strpos($elements[0]'=');
  1017.             $cookie['name']  trim(substr($elements[0]0$pos));
  1018.             $cookie['value'trim(substr($elements[0]$pos + 1));
  1019.  
  1020.             for ($i = 1; $i count($elements)$i++{
  1021.                 list ($elName$elValuearray_map('trim'explode('='$elements[$i]));
  1022.                 $elName strtolower($elName);
  1023.                 if ('secure' == $elName{
  1024.                     $cookie['secure'= true;
  1025.                 elseif ('expires' == $elName{
  1026.                     $cookie['expires'str_replace('"'''$elValue);
  1027.                 elseif ('path' == $elName || 'domain' == $elName{
  1028.                     $cookie[$elNameurldecode($elValue);
  1029.                 else {
  1030.                     $cookie[$elName$elValue;
  1031.                 }
  1032.             }
  1033.         }
  1034.         $this->_cookies[$cookie;
  1035.     }
  1036.  
  1037.  
  1038.    /**
  1039.     * Read a part of response body encoded with chunked Transfer-Encoding
  1040.     * 
  1041.     * @access private
  1042.     * @return string 
  1043.     */
  1044.     function _readChunked()
  1045.     {
  1046.         // at start of the next chunk?
  1047.         if (0 == $this->_chunkLength{
  1048.             $line $this->_sock->readLine();
  1049.             if (preg_match('/^([0-9a-f]+)/i'$line$matches)) {
  1050.                 $this->_chunkLength hexdec($matches[1])
  1051.                 // Chunk with zero length indicates the end
  1052.                 if (0 == $this->_chunkLength{
  1053.                     $this->_sock->readAll()// make this an eof()
  1054.                     return '';
  1055.                 }
  1056.             }
  1057.         }
  1058.         $data $this->_sock->read($this->_chunkLength);
  1059.         $this->_chunkLength -= strlen($data);
  1060.         if (0 == $this->_chunkLength{
  1061.             $this->_sock->readLine()// Trailing CRLF
  1062.         }
  1063.         return $data;
  1064.     }
  1065.  
  1066.  
  1067.    /**
  1068.     * Notifies all registered listeners of an event.
  1069.     * 
  1070.     * @param    string  Event name
  1071.     * @param    mixed   Additional data
  1072.     * @access   private
  1073.     * @see HTTP_Request::_notify()
  1074.     */
  1075.     function _notify($event$data = null)
  1076.     {
  1077.         foreach (array_keys($this->_listenersas $id{
  1078.             $this->_listeners[$id]->update($this$event$data);
  1079.         }
  1080.     }
  1081. // End class HTTP_Response
  1082. ?>

Documentation generated on Mon, 11 Mar 2019 10:16:17 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.