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

Source for file Curl.php

Documentation is available at Curl.php

  1. <?php
  2.  
  3. /**
  4.  * Zend Framework
  5.  *
  6.  * LICENSE
  7.  *
  8.  * This source file is subject to the new BSD license that is bundled
  9.  * with this package in the file LICENSE.txt.
  10.  * It is also available through the world-wide-web at this URL:
  11.  * http://framework.zend.com/license/new-bsd
  12.  * If you did not receive a copy of the license and are unable to
  13.  * obtain it through the world-wide-web, please send an email
  14.  * to license@zend.com so we can send you a copy immediately.
  15.  *
  16.  * @category   Microsoft
  17.  * @package    Microsoft_Http
  18.  * @subpackage Client_Adapter
  19.  * @version    $Id: Curl.php 19238 2009-11-25 17:13:38Z bate $
  20.  * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  21.  * @license    http://framework.zend.com/license/new-bsd     New BSD License
  22.  */
  23.  
  24. /**
  25.  * @see Microsoft_AutoLoader
  26.  */
  27. require_once dirname(__FILE__) . '/../../../AutoLoader.php';
  28.  
  29. /**
  30.  * An adapter class for Microsoft_Http_Client based on the curl extension.
  31.  * Curl requires libcurl. See for full requirements the PHP manual: http://php.net/curl
  32.  *
  33.  * @category   Microsoft
  34.  * @package    Microsoft_Http
  35.  * @subpackage Client_Adapter
  36.  * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  37.  * @license    http://framework.zend.com/license/new-bsd     New BSD License
  38.  */
  39. class Microsoft_Http_Client_Adapter_Curl implements Microsoft_Http_Client_Adapter_Interface, Microsoft_Http_Client_Adapter_Stream
  40. {
  41.     /**
  42.      * Parameters array
  43.      *
  44.      * @var array 
  45.      */
  46.     protected $_config = array();
  47.  
  48.     /**
  49.      * What host/port are we connected to?
  50.      *
  51.      * @var array 
  52.      */
  53.     protected $_connected_to = array(null, null);
  54.  
  55.     /**
  56.      * The curl session handle
  57.      *
  58.      * @var resource|null
  59.      */
  60.     protected $_curl = null;
  61.  
  62.     /**
  63.      * List of cURL options that should never be overwritten
  64.      *
  65.      * @var array 
  66.      */
  67.     protected $_invalidOverwritableCurlOptions = array(
  68.         CURLOPT_HTTPGET,
  69.         CURLOPT_POST,
  70.         CURLOPT_PUT,
  71.         CURLOPT_CUSTOMREQUEST,
  72.         CURLOPT_HEADER,
  73.         CURLOPT_RETURNTRANSFER,
  74.         CURLOPT_HTTPHEADER,
  75.         CURLOPT_POSTFIELDS,
  76.         CURLOPT_INFILE,
  77.         CURLOPT_INFILESIZE,
  78.         CURLOPT_PORT,
  79.         CURLOPT_MAXREDIRS,
  80.         CURLOPT_CONNECTTIMEOUT,
  81.         CURL_HTTP_VERSION_1_1,
  82.         CURL_HTTP_VERSION_1_0,
  83.     );
  84.  
  85.     /**
  86.      * Response gotten from server
  87.      *
  88.      * @var string 
  89.      */
  90.     protected $_response = null;
  91.  
  92.     /**
  93.      * Stream for storing output
  94.      *
  95.      * @var resource 
  96.      */
  97.     protected $out_stream;
  98.  
  99.     /**
  100.      * Adapter constructor
  101.      *
  102.      * Config is set using setConfig()
  103.      *
  104.      * @return void 
  105.      * @throws Microsoft_Http_Client_Adapter_Exception
  106.      */
  107.     public function __construct()
  108.     {
  109.         if (!extension_loaded('curl')) {
  110.             //require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  111.             throw new Microsoft_Http_Client_Adapter_Exception('cURL extension has to be loaded to use this Microsoft_Http_Client adapter.');
  112.         }
  113.     }
  114.  
  115.     /**
  116.      * Set the configuration array for the adapter
  117.      *
  118.      * @throws Microsoft_Http_Client_Adapter_Exception
  119.      * @param  array $config 
  120.      * @return Microsoft_Http_Client_Adapter_Curl 
  121.      */
  122.     public function setConfig($config = array())
  123.     {
  124.         if (! is_array($config)) {
  125.             //require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  126.             throw new Microsoft_Http_Client_Adapter_Exception(
  127.                 'Array expected, got ' . gettype($config)
  128.             );
  129.         }
  130.  
  131.         if(isset($config['proxy_user']) && isset($config['proxy_pass'])) {
  132.             $this->setCurlOption(CURLOPT_PROXYUSERPWD, $config['proxy_user'].":".$config['proxy_pass']);
  133.             unset($config['proxy_user'], $config['proxy_pass']);
  134.         }
  135.  
  136.         foreach ($config as $k => $v) {
  137.             $option = strtolower($k);
  138.             switch($option) {
  139.                 case 'proxy_host':
  140.                     $this->setCurlOption(CURLOPT_PROXY, $v);
  141.                     break;
  142.                 case 'proxy_port':
  143.                     $this->setCurlOption(CURLOPT_PROXYPORT, $v);
  144.                     break;
  145.                 default:
  146.                     $this->_config[$option] = $v;
  147.                     break;
  148.             }
  149.         }
  150.  
  151.         return $this;
  152.     }
  153.  
  154.     /**
  155.       * Retrieve the array of all configuration options
  156.       *
  157.       * @return array 
  158.       */
  159.      public function getConfig()
  160.      {
  161.          return $this->_config;
  162.      }
  163.  
  164.     /**
  165.      * Direct setter for cURL adapter related options.
  166.      *
  167.      * @param  string|int$option 
  168.      * @param  mixed $value 
  169.      * @return Microsoft_Http_Adapter_Curl 
  170.      */
  171.     public function setCurlOption($option, $value)
  172.     {
  173.         if (!isset($this->_config['curloptions'])) {
  174.             $this->_config['curloptions'] = array();
  175.         }
  176.         $this->_config['curloptions'][$option] = $value;
  177.         return $this;
  178.     }
  179.  
  180.     /**
  181.      * Initialize curl
  182.      *
  183.      * @param  string  $host 
  184.      * @param  int     $port 
  185.      * @param  boolean $secure 
  186.      * @return void 
  187.      * @throws Microsoft_Http_Client_Adapter_Exception if unable to connect
  188.      */
  189.     public function connect($host, $port = 80, $secure = false)
  190.     {
  191.         // If we're already connected, disconnect first
  192.         if ($this->_curl) {
  193.             $this->close();
  194.         }
  195.  
  196.         // If we are connected to a different server or port, disconnect first
  197.         if ($this->_curl
  198.             && is_array($this->_connected_to)
  199.             && ($this->_connected_to[0] != $host
  200.             || $this->_connected_to[1] != $port)
  201.         ) {
  202.             $this->close();
  203.         }
  204.  
  205.         // Do the actual connection
  206.         $this->_curl = curl_init();
  207.         if ($port != 80) {
  208.             curl_setopt($this->_curl, CURLOPT_PORT, intval($port));
  209.         }
  210.  
  211.         // Set timeout
  212.         curl_setopt($this->_curl, CURLOPT_CONNECTTIMEOUT, $this->_config['timeout']);
  213.  
  214.         // Set Max redirects
  215.         curl_setopt($this->_curl, CURLOPT_MAXREDIRS, $this->_config['maxredirects']);
  216.  
  217.         if (!$this->_curl) {
  218.             $this->close();
  219.  
  220.             //require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  221.             throw new Microsoft_Http_Client_Adapter_Exception('Unable to Connect to ' .  $host . ':' . $port);
  222.         }
  223.  
  224.         if ($secure !== false) {
  225.             // Behave the same like Microsoft_Http_Adapter_Socket on SSL options.
  226.             if (isset($this->_config['sslcert'])) {
  227.                 curl_setopt($this->_curl, CURLOPT_SSLCERT, $this->_config['sslcert']);
  228.             }
  229.             if (isset($this->_config['sslpassphrase'])) {
  230.                 curl_setopt($this->_curl, CURLOPT_SSLCERTPASSWD, $this->_config['sslpassphrase']);
  231.             }
  232.         }
  233.  
  234.         // Update connected_to
  235.         $this->_connected_to = array($host, $port);
  236.     }
  237.  
  238.     /**
  239.      * Send request to the remote server
  240.      *
  241.      * @param  string        $method 
  242.      * @param  Microsoft_Uri_Http $uri 
  243.      * @param  float         $http_ver 
  244.      * @param  array         $headers 
  245.      * @param  string        $body 
  246.      * @return string        $request
  247.      * @throws Microsoft_Http_Client_Adapter_Exception If connection fails, connected to wrong host, no PUT file defined, unsupported method, or unsupported cURL option
  248.      */
  249.     public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $body = '')
  250.     {
  251.         // Make sure we're properly connected
  252.         if (!$this->_curl) {
  253.             //require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  254.             throw new Microsoft_Http_Client_Adapter_Exception("Trying to write but we are not connected");
  255.         }
  256.  
  257.         if ($this->_connected_to[0] != $uri->getHost() || $this->_connected_to[1] != $uri->getPort()) {
  258.             //require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  259.             throw new Microsoft_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong host");
  260.         }
  261.  
  262.         // set URL
  263.         curl_setopt($this->_curl, CURLOPT_URL, $uri->__toString());
  264.  
  265.         // ensure correct curl call
  266.         $curlValue = true;
  267.         switch ($method) {
  268.             case Microsoft_Http_Client::GET:
  269.                 $curlMethod = CURLOPT_HTTPGET;
  270.                 break;
  271.  
  272.             case Microsoft_Http_Client::POST:
  273.                 $curlMethod = CURLOPT_POST;
  274.                 break;
  275.  
  276.             case Microsoft_Http_Client::PUT:
  277.                 // There are two different types of PUT request, either a Raw Data string has been set
  278.                 // or CURLOPT_INFILE and CURLOPT_INFILESIZE are used.
  279.                 if(is_resource($body)) {
  280.                     $this->_config['curloptions'][CURLOPT_INFILE] = $body;
  281.                 }
  282.                 if (isset($this->_config['curloptions'][CURLOPT_INFILE])) {
  283.                     // Now we will probably already have Content-Length set, so that we have to delete it
  284.                     // from $headers at this point:
  285.                     foreach ($headers AS $k => $header) {
  286.                         if (preg_match('/Content-Length:\s*(\d+)/i', $header, $m)) {
  287.                             if(is_resource($body)) {
  288.                                 $this->_config['curloptions'][CURLOPT_INFILESIZE] = (int)$m[1];
  289.                             }
  290.                             unset($headers[$k]);
  291.                         }
  292.                     }
  293.  
  294.                     if (!isset($this->_config['curloptions'][CURLOPT_INFILESIZE])) {
  295.                         //require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  296.                         throw new Microsoft_Http_Client_Adapter_Exception("Cannot set a file-handle for cURL option CURLOPT_INFILE without also setting its size in CURLOPT_INFILESIZE.");
  297.                     }
  298.  
  299.                     if(is_resource($body)) {
  300.                         $body = '';
  301.                     }
  302.  
  303.                     $curlMethod = CURLOPT_PUT;
  304.                 } else {
  305.                     $curlMethod = CURLOPT_CUSTOMREQUEST;
  306.                     $curlValue = "PUT";
  307.                 }
  308.                 break;
  309.  
  310.             case Microsoft_Http_Client::DELETE:
  311.                 $curlMethod = CURLOPT_CUSTOMREQUEST;
  312.                 $curlValue = "DELETE";
  313.                 break;
  314.  
  315.             case Microsoft_Http_Client::OPTIONS:
  316.                 $curlMethod = CURLOPT_CUSTOMREQUEST;
  317.                 $curlValue = "OPTIONS";
  318.                 break;
  319.  
  320.             case Microsoft_Http_Client::TRACE:
  321.                 $curlMethod = CURLOPT_CUSTOMREQUEST;
  322.                 $curlValue = "TRACE";
  323.                 break;
  324.  
  325.             default:
  326.                 // For now, through an exception for unsupported request methods
  327.                 //require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  328.                 throw new Microsoft_Http_Client_Adapter_Exception("Method currently not supported");
  329.         }
  330.  
  331.         if(is_resource($body) && $curlMethod != CURLOPT_PUT) {
  332.             //require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  333.             throw new Microsoft_Http_Client_Adapter_Exception("Streaming requests are allowed only with PUT");
  334.         }
  335.  
  336.         // get http version to use
  337.         $curlHttp = ($httpVersion == 1.1) ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
  338.  
  339.         // mark as HTTP request and set HTTP method
  340.         curl_setopt($this->_curl, $curlHttp, true);
  341.         curl_setopt($this->_curl, $curlMethod, $curlValue);
  342.  
  343.         if($this->out_stream) {
  344.             // headers will be read into the response
  345.             curl_setopt($this->_curl, CURLOPT_HEADER, false);
  346.             curl_setopt($this->_curl, CURLOPT_HEADERFUNCTION, array($this, "readHeader"));
  347.             // and data will be written into the file
  348.             curl_setopt($this->_curl, CURLOPT_FILE, $this->out_stream);
  349.         } else {
  350.             // ensure headers are also returned
  351.             curl_setopt($this->_curl, CURLOPT_HEADER, true);
  352.  
  353.             // ensure actual response is returned
  354.             curl_setopt($this->_curl, CURLOPT_RETURNTRANSFER, true);
  355.         }
  356.  
  357.         // set additional headers
  358.         $headers['Accept'] = '';
  359.         curl_setopt($this->_curl, CURLOPT_HTTPHEADER, $headers);
  360.  
  361.         /**
  362.          * Make sure POSTFIELDS is set after $curlMethod is set:
  363.          * @link http://de2.php.net/manual/en/function.curl-setopt.php#81161
  364.          */
  365.         if ($method == Microsoft_Http_Client::POST) {
  366.             curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
  367.         } elseif ($curlMethod == CURLOPT_PUT) {
  368.             // this covers a PUT by file-handle:
  369.             // Make the setting of this options explicit (rather than setting it through the loop following a bit lower)
  370.             // to group common functionality together.
  371.             curl_setopt($this->_curl, CURLOPT_INFILE, $this->_config['curloptions'][CURLOPT_INFILE]);
  372.             curl_setopt($this->_curl, CURLOPT_INFILESIZE, $this->_config['curloptions'][CURLOPT_INFILESIZE]);
  373.             unset($this->_config['curloptions'][CURLOPT_INFILE]);
  374.             unset($this->_config['curloptions'][CURLOPT_INFILESIZE]);
  375.         } elseif ($method == Microsoft_Http_Client::PUT) {
  376.             // This is a PUT by a setRawData string, not by file-handle
  377.             curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
  378.         }
  379.  
  380.         // set additional curl options
  381.         if (isset($this->_config['curloptions'])) {
  382.             foreach ((array)$this->_config['curloptions'] as $k => $v) {
  383.                 if (!in_array($k, $this->_invalidOverwritableCurlOptions)) {
  384.                     if (curl_setopt($this->_curl, $k, $v) == false) {
  385.                         //require_once 'Microsoft/Http/Client/Exception.php';
  386.                         throw new Microsoft_Http_Client_Exception(sprintf("Unknown or erroreous cURL option '%s' set", $k));
  387.                     }
  388.                 }
  389.             }
  390.         }
  391.  
  392.         // send the request
  393.         $response = curl_exec($this->_curl);
  394.  
  395.         // if we used streaming, headers are already there
  396.         if(!is_resource($this->out_stream)) {
  397.             $this->_response = $response;
  398.         }
  399.  
  400.         $request  = curl_getinfo($this->_curl, CURLINFO_HEADER_OUT);
  401.         $request .= $body;
  402.  
  403.         if (empty($this->_response)) {
  404.             //require_once 'Microsoft/Http/Client/Exception.php';
  405.             throw new Microsoft_Http_Client_Exception("Error in cURL request: " . curl_error($this->_curl));
  406.         }
  407.  
  408.         // cURL automatically decodes chunked-messages, this means we have to disallow the Microsoft_Http_Response to do it again
  409.         if (stripos($this->_response, "Transfer-Encoding: chunked\r\n")) {
  410.             $this->_response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $this->_response);
  411.         }
  412.  
  413.         // Eliminate multiple HTTP responses.
  414.         do {
  415.             $parts  = preg_split('|(?:\r?\n){2}|m', $this->_response, 2);
  416.             $again  = false;
  417.  
  418.             if (isset($parts[1]) && preg_match("|^HTTP/1\.[01](.*?)\r\n|mi", $parts[1])) {
  419.                 $this->_response    = $parts[1];
  420.                 $again              = true;
  421.             }
  422.         } while ($again);
  423.  
  424.         // cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string:
  425.         if (stripos($this->_response, "HTTP/1.0 200 Connection established\r\n\r\n") !== false) {
  426.             $this->_response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $this->_response);
  427.         }
  428.  
  429.         return $request;
  430.     }
  431.  
  432.     /**
  433.      * Return read response from server
  434.      *
  435.      * @return string 
  436.      */
  437.     public function read()
  438.     {
  439.         return $this->_response;
  440.     }
  441.  
  442.     /**
  443.      * Close the connection to the server
  444.      *
  445.      */
  446.     public function close()
  447.     {
  448.         if(is_resource($this->_curl)) {
  449.             curl_close($this->_curl);
  450.         }
  451.         $this->_curl         = null;
  452.         $this->_connected_to = array(null, null);
  453.     }
  454.  
  455.     /**
  456.      * Get cUrl Handle
  457.      *
  458.      * @return resource 
  459.      */
  460.     public function getHandle()
  461.     {
  462.         return $this->_curl;
  463.     }
  464.  
  465.     /**
  466.      * Set output stream for the response
  467.      *
  468.      * @param resource $stream 
  469.      * @return Microsoft_Http_Client_Adapter_Socket 
  470.      */
  471.     public function setOutputStream($stream)
  472.     {
  473.         $this->out_stream = $stream;
  474.         return $this;
  475.     }
  476.  
  477.     /**
  478.      * Header reader function for CURL
  479.      *
  480.      * @param resource $curl 
  481.      * @param string $header 
  482.      * @return int 
  483.      */
  484.     public function readHeader($curl, $header)
  485.     {
  486.         $this->_response .= $header;
  487.         return strlen($header);
  488.     }
  489. }

Documentation generated on Sat, 03 Dec 2011 13:59:20 +0100 by phpDocumentor 1.4.3