| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  | <?php | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Adapter for HTTP_Request2 wrapping around cURL extension | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * PHP version 5 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * LICENSE: | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |  * Copyright (c) 2008-2012, Alexey Borzov <avb@php.net> | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |  * All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  |  * modification, are permitted provided that the following conditions | 
					
						
							|  |  |  |  * are met: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *    * Redistributions of source code must retain the above copyright | 
					
						
							|  |  |  |  *      notice, this list of conditions and the following disclaimer. | 
					
						
							|  |  |  |  *    * Redistributions in binary form must reproduce the above copyright | 
					
						
							|  |  |  |  *      notice, this list of conditions and the following disclaimer in the | 
					
						
							|  |  |  |  *      documentation and/or other materials provided with the distribution. | 
					
						
							|  |  |  |  *    * The names of the authors may not be used to endorse or promote products | 
					
						
							|  |  |  |  *      derived from this software without specific prior written permission. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
					
						
							|  |  |  |  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
					
						
							|  |  |  |  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
					
						
							|  |  |  |  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | 
					
						
							|  |  |  |  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
					
						
							|  |  |  |  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
					
						
							|  |  |  |  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
					
						
							|  |  |  |  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
					
						
							|  |  |  |  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | 
					
						
							|  |  |  |  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 
					
						
							|  |  |  |  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |  * @category HTTP | 
					
						
							|  |  |  |  * @package  HTTP_Request2 | 
					
						
							|  |  |  |  * @author   Alexey Borzov <avb@php.net> | 
					
						
							|  |  |  |  * @license  http://opensource.org/licenses/bsd-license.php New BSD License | 
					
						
							|  |  |  |  * @version  SVN: $Id: Curl.php 324746 2012-04-03 15:09:16Z avb $ | 
					
						
							|  |  |  |  * @link     http://pear.php.net/package/HTTP_Request2 | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Base class for HTTP_Request2 adapters | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | require_once 'HTTP/Request2/Adapter.php'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Adapter for HTTP_Request2 wrapping around cURL extension | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |  * @category HTTP | 
					
						
							|  |  |  |  * @package  HTTP_Request2 | 
					
						
							|  |  |  |  * @author   Alexey Borzov <avb@php.net> | 
					
						
							|  |  |  |  * @license  http://opensource.org/licenses/bsd-license.php New BSD License | 
					
						
							|  |  |  |  * @version  Release: 2.1.1 | 
					
						
							|  |  |  |  * @link     http://pear.php.net/package/HTTP_Request2 | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |  */ | 
					
						
							|  |  |  | class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Mapping of header names to cURL options | 
					
						
							|  |  |  |      * @var  array | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected static $headerMap = array( | 
					
						
							|  |  |  |         'accept-encoding' => CURLOPT_ENCODING, | 
					
						
							|  |  |  |         'cookie'          => CURLOPT_COOKIE, | 
					
						
							|  |  |  |         'referer'         => CURLOPT_REFERER, | 
					
						
							|  |  |  |         'user-agent'      => CURLOPT_USERAGENT | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Mapping of SSL context options to cURL options | 
					
						
							|  |  |  |      * @var  array | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected static $sslContextMap = array( | 
					
						
							|  |  |  |         'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER, | 
					
						
							|  |  |  |         'ssl_cafile'      => CURLOPT_CAINFO, | 
					
						
							|  |  |  |         'ssl_capath'      => CURLOPT_CAPATH, | 
					
						
							|  |  |  |         'ssl_local_cert'  => CURLOPT_SSLCERT, | 
					
						
							|  |  |  |         'ssl_passphrase'  => CURLOPT_SSLCERTPASSWD | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Mapping of CURLE_* constants to Exception subclasses and error codes | 
					
						
							|  |  |  |      * @var  array | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |     protected static $errorMap = array( | 
					
						
							|  |  |  |         CURLE_UNSUPPORTED_PROTOCOL  => array('HTTP_Request2_MessageException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::NON_HTTP_REDIRECT), | 
					
						
							|  |  |  |         CURLE_COULDNT_RESOLVE_PROXY => array('HTTP_Request2_ConnectionException'), | 
					
						
							|  |  |  |         CURLE_COULDNT_RESOLVE_HOST  => array('HTTP_Request2_ConnectionException'), | 
					
						
							|  |  |  |         CURLE_COULDNT_CONNECT       => array('HTTP_Request2_ConnectionException'), | 
					
						
							|  |  |  |         // error returned from write callback
 | 
					
						
							|  |  |  |         CURLE_WRITE_ERROR           => array('HTTP_Request2_MessageException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::NON_HTTP_REDIRECT), | 
					
						
							|  |  |  |         CURLE_OPERATION_TIMEOUTED   => array('HTTP_Request2_MessageException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::TIMEOUT), | 
					
						
							|  |  |  |         CURLE_HTTP_RANGE_ERROR      => array('HTTP_Request2_MessageException'), | 
					
						
							|  |  |  |         CURLE_SSL_CONNECT_ERROR     => array('HTTP_Request2_ConnectionException'), | 
					
						
							|  |  |  |         CURLE_LIBRARY_NOT_FOUND     => array('HTTP_Request2_LogicException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::MISCONFIGURATION), | 
					
						
							|  |  |  |         CURLE_FUNCTION_NOT_FOUND    => array('HTTP_Request2_LogicException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::MISCONFIGURATION), | 
					
						
							|  |  |  |         CURLE_ABORTED_BY_CALLBACK   => array('HTTP_Request2_MessageException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::NON_HTTP_REDIRECT), | 
					
						
							|  |  |  |         CURLE_TOO_MANY_REDIRECTS    => array('HTTP_Request2_MessageException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::TOO_MANY_REDIRECTS), | 
					
						
							|  |  |  |         CURLE_SSL_PEER_CERTIFICATE  => array('HTTP_Request2_ConnectionException'), | 
					
						
							|  |  |  |         CURLE_GOT_NOTHING           => array('HTTP_Request2_MessageException'), | 
					
						
							|  |  |  |         CURLE_SSL_ENGINE_NOTFOUND   => array('HTTP_Request2_LogicException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::MISCONFIGURATION), | 
					
						
							|  |  |  |         CURLE_SSL_ENGINE_SETFAILED  => array('HTTP_Request2_LogicException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::MISCONFIGURATION), | 
					
						
							|  |  |  |         CURLE_SEND_ERROR            => array('HTTP_Request2_MessageException'), | 
					
						
							|  |  |  |         CURLE_RECV_ERROR            => array('HTTP_Request2_MessageException'), | 
					
						
							|  |  |  |         CURLE_SSL_CERTPROBLEM       => array('HTTP_Request2_LogicException', | 
					
						
							|  |  |  |                                              HTTP_Request2_Exception::INVALID_ARGUMENT), | 
					
						
							|  |  |  |         CURLE_SSL_CIPHER            => array('HTTP_Request2_ConnectionException'), | 
					
						
							|  |  |  |         CURLE_SSL_CACERT            => array('HTTP_Request2_ConnectionException'), | 
					
						
							|  |  |  |         CURLE_BAD_CONTENT_ENCODING  => array('HTTP_Request2_MessageException'), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Response being received | 
					
						
							|  |  |  |      * @var  HTTP_Request2_Response | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected $response; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Whether 'sentHeaders' event was sent to observers | 
					
						
							|  |  |  |      * @var  boolean | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected $eventSentHeaders = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Whether 'receivedHeaders' event was sent to observers | 
					
						
							|  |  |  |      * @var boolean | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected $eventReceivedHeaders = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Position within request body | 
					
						
							|  |  |  |      * @var  integer | 
					
						
							|  |  |  |      * @see  callbackReadBody() | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected $position = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Information about last transfer, as returned by curl_getinfo() | 
					
						
							|  |  |  |      * @var  array | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected $lastInfo; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Creates a subclass of HTTP_Request2_Exception from curl error data | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param resource $ch curl handle | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return HTTP_Request2_Exception | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |     protected static function wrapCurlError($ch) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $nativeCode = curl_errno($ch); | 
					
						
							|  |  |  |         $message    = 'Curl error: ' . curl_error($ch); | 
					
						
							|  |  |  |         if (!isset(self::$errorMap[$nativeCode])) { | 
					
						
							|  |  |  |             return new HTTP_Request2_Exception($message, 0, $nativeCode); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $class = self::$errorMap[$nativeCode][0]; | 
					
						
							|  |  |  |             $code  = empty(self::$errorMap[$nativeCode][1]) | 
					
						
							|  |  |  |                      ? 0 : self::$errorMap[$nativeCode][1]; | 
					
						
							|  |  |  |             return new $class($message, $code, $nativeCode); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Sends request to the remote server and returns its response | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param HTTP_Request2 $request HTTP request message | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return   HTTP_Request2_Response | 
					
						
							|  |  |  |      * @throws   HTTP_Request2_Exception | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     public function sendRequest(HTTP_Request2 $request) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!extension_loaded('curl')) { | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |             throw new HTTP_Request2_LogicException( | 
					
						
							|  |  |  |                 'cURL extension not available', HTTP_Request2_Exception::MISCONFIGURATION | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->request              = $request; | 
					
						
							|  |  |  |         $this->response             = null; | 
					
						
							|  |  |  |         $this->position             = 0; | 
					
						
							|  |  |  |         $this->eventSentHeaders     = false; | 
					
						
							|  |  |  |         $this->eventReceivedHeaders = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             if (false === curl_exec($ch = $this->createCurlHandle())) { | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |                 $e = self::wrapCurlError($ch); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |             } | 
					
						
							|  |  |  |         } catch (Exception $e) { | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |         if (isset($ch)) { | 
					
						
							|  |  |  |             $this->lastInfo = curl_getinfo($ch); | 
					
						
							|  |  |  |             curl_close($ch); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->response; | 
					
						
							|  |  |  |         unset($this->request, $this->requestBody, $this->response); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (!empty($e)) { | 
					
						
							|  |  |  |             throw $e; | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($jar = $request->getCookieJar()) { | 
					
						
							|  |  |  |             $jar->addCookiesFromResponse($response, $request->getUrl()); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (0 < $this->lastInfo['size_download']) { | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |             $request->setLastEvent('receivedBody', $response); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |         return $response; | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Returns information about last transfer | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return   array   associative array as returned by curl_getinfo() | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     public function getInfo() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return $this->lastInfo; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Creates a new cURL handle and populates it with data from the request | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return   resource    a cURL handle, as created by curl_init() | 
					
						
							|  |  |  |      * @throws   HTTP_Request2_LogicException | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected function createCurlHandle() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $ch = curl_init(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         curl_setopt_array($ch, array( | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |             // setup write callbacks
 | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |             CURLOPT_HEADERFUNCTION => array($this, 'callbackWriteHeader'), | 
					
						
							|  |  |  |             CURLOPT_WRITEFUNCTION  => array($this, 'callbackWriteBody'), | 
					
						
							|  |  |  |             // buffer size
 | 
					
						
							|  |  |  |             CURLOPT_BUFFERSIZE     => $this->request->getConfig('buffer_size'), | 
					
						
							|  |  |  |             // connection timeout
 | 
					
						
							|  |  |  |             CURLOPT_CONNECTTIMEOUT => $this->request->getConfig('connect_timeout'), | 
					
						
							|  |  |  |             // save full outgoing headers, in case someone is interested
 | 
					
						
							|  |  |  |             CURLINFO_HEADER_OUT    => true, | 
					
						
							|  |  |  |             // request url
 | 
					
						
							|  |  |  |             CURLOPT_URL            => $this->request->getUrl()->getUrl() | 
					
						
							|  |  |  |         )); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |         // set up redirects
 | 
					
						
							|  |  |  |         if (!$this->request->getConfig('follow_redirects')) { | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             if (!@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true)) { | 
					
						
							|  |  |  |                 throw new HTTP_Request2_LogicException( | 
					
						
							|  |  |  |                     'Redirect support in curl is unavailable due to open_basedir or safe_mode setting', | 
					
						
							|  |  |  |                     HTTP_Request2_Exception::MISCONFIGURATION | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_MAXREDIRS, $this->request->getConfig('max_redirects')); | 
					
						
							|  |  |  |             // limit redirects to http(s), works in 5.2.10+
 | 
					
						
							|  |  |  |             if (defined('CURLOPT_REDIR_PROTOCOLS')) { | 
					
						
							|  |  |  |                 curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // works in 5.3.2+, http://bugs.php.net/bug.php?id=49571
 | 
					
						
							|  |  |  |             if ($this->request->getConfig('strict_redirects') && defined('CURLOPT_POSTREDIR')) { | 
					
						
							|  |  |  |                 curl_setopt($ch, CURLOPT_POSTREDIR, 3); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         // request timeout
 | 
					
						
							|  |  |  |         if ($timeout = $this->request->getConfig('timeout')) { | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // set HTTP version
 | 
					
						
							|  |  |  |         switch ($this->request->getConfig('protocol_version')) { | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |         case '1.0': | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case '1.1': | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // set request method
 | 
					
						
							|  |  |  |         switch ($this->request->getMethod()) { | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |         case HTTP_Request2::METHOD_GET: | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_HTTPGET, true); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case HTTP_Request2::METHOD_POST: | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_POST, true); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case HTTP_Request2::METHOD_HEAD: | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_NOBODY, true); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case HTTP_Request2::METHOD_PUT: | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_UPLOAD, true); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->request->getMethod()); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // set proxy, if needed
 | 
					
						
							|  |  |  |         if ($host = $this->request->getConfig('proxy_host')) { | 
					
						
							|  |  |  |             if (!($port = $this->request->getConfig('proxy_port'))) { | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |                 throw new HTTP_Request2_LogicException( | 
					
						
							|  |  |  |                     'Proxy port not provided', HTTP_Request2_Exception::MISSING_VALUE | 
					
						
							|  |  |  |                 ); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |             } | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_PROXY, $host . ':' . $port); | 
					
						
							|  |  |  |             if ($user = $this->request->getConfig('proxy_user')) { | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |                 curl_setopt( | 
					
						
							|  |  |  |                     $ch, CURLOPT_PROXYUSERPWD, | 
					
						
							|  |  |  |                     $user . ':' . $this->request->getConfig('proxy_password') | 
					
						
							|  |  |  |                 ); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |                 switch ($this->request->getConfig('proxy_auth_scheme')) { | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |                 case HTTP_Request2::AUTH_BASIC: | 
					
						
							|  |  |  |                     curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case HTTP_Request2::AUTH_DIGEST: | 
					
						
							|  |  |  |                     curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if ($type = $this->request->getConfig('proxy_type')) { | 
					
						
							|  |  |  |                 switch ($type) { | 
					
						
							|  |  |  |                 case 'http': | 
					
						
							|  |  |  |                     curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case 'socks5': | 
					
						
							|  |  |  |                     curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 default: | 
					
						
							|  |  |  |                     throw new HTTP_Request2_NotImplementedException( | 
					
						
							|  |  |  |                         "Proxy type '{$type}' is not supported" | 
					
						
							|  |  |  |                     ); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // set authentication data
 | 
					
						
							|  |  |  |         if ($auth = $this->request->getAuth()) { | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_USERPWD, $auth['user'] . ':' . $auth['password']); | 
					
						
							|  |  |  |             switch ($auth['scheme']) { | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |             case HTTP_Request2::AUTH_BASIC: | 
					
						
							|  |  |  |                 curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case HTTP_Request2::AUTH_DIGEST: | 
					
						
							|  |  |  |                 curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // set SSL options
 | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |         foreach ($this->request->getConfig() as $name => $value) { | 
					
						
							|  |  |  |             if ('ssl_verify_host' == $name && null !== $value) { | 
					
						
							|  |  |  |                 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $value? 2: 0); | 
					
						
							|  |  |  |             } elseif (isset(self::$sslContextMap[$name]) && null !== $value) { | 
					
						
							|  |  |  |                 curl_setopt($ch, self::$sslContextMap[$name], $value); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $headers = $this->request->getHeaders(); | 
					
						
							|  |  |  |         // make cURL automagically send proper header
 | 
					
						
							|  |  |  |         if (!isset($headers['accept-encoding'])) { | 
					
						
							|  |  |  |             $headers['accept-encoding'] = ''; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |         if (($jar = $this->request->getCookieJar()) | 
					
						
							|  |  |  |             && ($cookies = $jar->getMatching($this->request->getUrl(), true)) | 
					
						
							|  |  |  |         ) { | 
					
						
							|  |  |  |             $headers['cookie'] = (empty($headers['cookie'])? '': $headers['cookie'] . '; ') . $cookies; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         // set headers having special cURL keys
 | 
					
						
							|  |  |  |         foreach (self::$headerMap as $name => $option) { | 
					
						
							|  |  |  |             if (isset($headers[$name])) { | 
					
						
							|  |  |  |                 curl_setopt($ch, $option, $headers[$name]); | 
					
						
							|  |  |  |                 unset($headers[$name]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->calculateRequestLength($headers); | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |         if (isset($headers['content-length'])) { | 
					
						
							|  |  |  |             $this->workaroundPhpBug47204($ch, $headers); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // set headers not having special keys
 | 
					
						
							|  |  |  |         $headersFmt = array(); | 
					
						
							|  |  |  |         foreach ($headers as $name => $value) { | 
					
						
							|  |  |  |             $canonicalName = implode('-', array_map('ucfirst', explode('-', $name))); | 
					
						
							|  |  |  |             $headersFmt[]  = $canonicalName . ': ' . $value; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         curl_setopt($ch, CURLOPT_HTTPHEADER, $headersFmt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $ch; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Workaround for PHP bug #47204 that prevents rewinding request body
 | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * The workaround consists of reading the entire request body into memory | 
					
						
							|  |  |  |      * and setting it as CURLOPT_POSTFIELDS, so it isn't recommended for large | 
					
						
							|  |  |  |      * file uploads, use Socket adapter instead. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param resource $ch       cURL handle | 
					
						
							|  |  |  |      * @param array    &$headers Request headers | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |     protected function workaroundPhpBug47204($ch, &$headers) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // no redirects, no digest auth -> probably no rewind needed
 | 
					
						
							|  |  |  |         if (!$this->request->getConfig('follow_redirects') | 
					
						
							|  |  |  |             && (!($auth = $this->request->getAuth()) | 
					
						
							|  |  |  |                 || HTTP_Request2::AUTH_DIGEST != $auth['scheme']) | 
					
						
							|  |  |  |         ) { | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_READFUNCTION, array($this, 'callbackReadBody')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |             // rewind may be needed, read the whole body into memory
 | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |             if ($this->requestBody instanceof HTTP_Request2_MultipartBody) { | 
					
						
							|  |  |  |                 $this->requestBody = $this->requestBody->__toString(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             } elseif (is_resource($this->requestBody)) { | 
					
						
							|  |  |  |                 $fp = $this->requestBody; | 
					
						
							|  |  |  |                 $this->requestBody = ''; | 
					
						
							|  |  |  |                 while (!feof($fp)) { | 
					
						
							|  |  |  |                     $this->requestBody .= fread($fp, 16384); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // curl hangs up if content-length is present
 | 
					
						
							|  |  |  |             unset($headers['content-length']); | 
					
						
							|  |  |  |             curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Callback function called by cURL for reading the request body | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param resource $ch     cURL handle | 
					
						
							|  |  |  |      * @param resource $fd     file descriptor (not used) | 
					
						
							|  |  |  |      * @param integer  $length maximum length of data to return | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return   string      part of the request body, up to $length bytes | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected function callbackReadBody($ch, $fd, $length) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!$this->eventSentHeaders) { | 
					
						
							|  |  |  |             $this->request->setLastEvent( | 
					
						
							|  |  |  |                 'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT) | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |             $this->eventSentHeaders = true; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |         if (in_array($this->request->getMethod(), self::$bodyDisallowed) | 
					
						
							|  |  |  |             || 0 == $this->contentLength || $this->position >= $this->contentLength | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         ) { | 
					
						
							|  |  |  |             return ''; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (is_string($this->requestBody)) { | 
					
						
							|  |  |  |             $string = substr($this->requestBody, $this->position, $length); | 
					
						
							|  |  |  |         } elseif (is_resource($this->requestBody)) { | 
					
						
							|  |  |  |             $string = fread($this->requestBody, $length); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $string = $this->requestBody->read($length); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $this->request->setLastEvent('sentBodyPart', strlen($string)); | 
					
						
							|  |  |  |         $this->position += strlen($string); | 
					
						
							|  |  |  |         return $string; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Callback function called by cURL for saving the response headers | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param resource $ch     cURL handle | 
					
						
							|  |  |  |      * @param string   $string response header (with trailing CRLF) | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return   integer     number of bytes saved | 
					
						
							|  |  |  |      * @see      HTTP_Request2_Response::parseHeaderLine() | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected function callbackWriteHeader($ch, $string) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // we may receive a second set of headers if doing e.g. digest auth
 | 
					
						
							|  |  |  |         if ($this->eventReceivedHeaders || !$this->eventSentHeaders) { | 
					
						
							|  |  |  |             // don't bother with 100-Continue responses (bug #15785)
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |             if (!$this->eventSentHeaders | 
					
						
							|  |  |  |                 || $this->response->getStatus() >= 200 | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |             ) { | 
					
						
							|  |  |  |                 $this->request->setLastEvent( | 
					
						
							|  |  |  |                     'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT) | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |             $upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD); | 
					
						
							|  |  |  |             // if body wasn't read by a callback, send event with total body size
 | 
					
						
							|  |  |  |             if ($upload > $this->position) { | 
					
						
							|  |  |  |                 $this->request->setLastEvent( | 
					
						
							|  |  |  |                     'sentBodyPart', $upload - $this->position | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  |                 $this->position = $upload; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if ($upload && (!$this->eventSentHeaders | 
					
						
							|  |  |  |                             || $this->response->getStatus() >= 200) | 
					
						
							|  |  |  |             ) { | 
					
						
							|  |  |  |                 $this->request->setLastEvent('sentBody', $upload); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |             $this->eventSentHeaders = true; | 
					
						
							|  |  |  |             // we'll need a new response object
 | 
					
						
							|  |  |  |             if ($this->eventReceivedHeaders) { | 
					
						
							|  |  |  |                 $this->eventReceivedHeaders = false; | 
					
						
							|  |  |  |                 $this->response             = null; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (empty($this->response)) { | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |             $this->response = new HTTP_Request2_Response( | 
					
						
							|  |  |  |                 $string, false, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL) | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             $this->response->parseHeaderLine($string); | 
					
						
							|  |  |  |             if ('' == trim($string)) { | 
					
						
							|  |  |  |                 // don't bother with 100-Continue responses (bug #15785)
 | 
					
						
							|  |  |  |                 if (200 <= $this->response->getStatus()) { | 
					
						
							|  |  |  |                     $this->request->setLastEvent('receivedHeaders', $this->response); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if ($this->request->getConfig('follow_redirects') && $this->response->isRedirect()) { | 
					
						
							|  |  |  |                     $redirectUrl = new Net_URL2($this->response->getHeader('location')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     // for versions lower than 5.2.10, check the redirection URL protocol
 | 
					
						
							|  |  |  |                     if (!defined('CURLOPT_REDIR_PROTOCOLS') && $redirectUrl->isAbsolute() | 
					
						
							|  |  |  |                         && !in_array($redirectUrl->getScheme(), array('http', 'https')) | 
					
						
							|  |  |  |                     ) { | 
					
						
							|  |  |  |                         return -1; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if ($jar = $this->request->getCookieJar()) { | 
					
						
							|  |  |  |                         $jar->addCookiesFromResponse($this->response, $this->request->getUrl()); | 
					
						
							|  |  |  |                         if (!$redirectUrl->isAbsolute()) { | 
					
						
							|  |  |  |                             $redirectUrl = $this->request->getUrl()->resolve($redirectUrl); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         if ($cookies = $jar->getMatching($redirectUrl, true)) { | 
					
						
							|  |  |  |                             curl_setopt($ch, CURLOPT_COOKIE, $cookies); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |                 $this->eventReceivedHeaders = true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return strlen($string); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 14:29:02 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Callback function called by cURL for saving the response body | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param resource $ch     cURL handle (not used) | 
					
						
							|  |  |  |      * @param string   $string part of the response body | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return   integer     number of bytes saved | 
					
						
							|  |  |  |      * @throws   HTTP_Request2_MessageException | 
					
						
							|  |  |  |      * @see      HTTP_Request2_Response::appendBody() | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |     protected function callbackWriteBody($ch, $string) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |         // cURL calls WRITEFUNCTION callback without calling HEADERFUNCTION if
 | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         // response doesn't start with proper HTTP status line (see bug #15716)
 | 
					
						
							|  |  |  |         if (empty($this->response)) { | 
					
						
							| 
									
										
										
										
											2011-06-22 15:56:27 -04:00
										 |  |  |             throw new HTTP_Request2_MessageException( | 
					
						
							|  |  |  |                 "Malformed response: {$string}", | 
					
						
							|  |  |  |                 HTTP_Request2_Exception::MALFORMED_RESPONSE | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2009-10-28 15:29:20 -04:00
										 |  |  |         } | 
					
						
							|  |  |  |         if ($this->request->getConfig('store_body')) { | 
					
						
							|  |  |  |             $this->response->appendBody($string); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $this->request->setLastEvent('receivedBodyPart', $string); | 
					
						
							|  |  |  |         return strlen($string); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ?>
 |