• Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • Examples
  • File List

system/classes/remoterequest.php

00001 <?php
00007 namespace Habari;
00015 abstract class RequestProcessor
00016 {
00017   
00018   protected $response_body = '';
00019   protected $response_headers = array();
00020   
00021   protected $executed = false;
00022   
00023   protected $can_followlocation = true;
00024   
00025   abstract public function execute( $method, $url, $headers, $body, $config );
00026 
00027   public function get_response_body ( ) {
00028     
00029     if ( !$this->executed ) {
00030       throw new \Exception( _t( 'Unable to get response body. Request did not yet execute.' ) );
00031     }
00032     
00033     return $this->response_body;
00034     
00035   }
00036   public function get_response_headers ( ) {
00037     
00038     if ( !$this->executed ) {
00039       throw new \Exception( _t( 'Unable to get response headers. Request did not yet execute.' ) );
00040     }
00041     
00042     return $this->response_headers;
00043     
00044   }
00045 }
00046 
00051 class RemoteRequest
00052 {
00053   private $method = 'GET';
00054   private $url;
00055   private $params = array();
00056   private $headers = array(
00057     'Accept' => '*/*',
00058   );
00059   private $postdata = array();
00060   private $files = array();
00061   private $body = '';
00062   private $processor = null;
00063   private $executed = false;
00064 
00065   private $response_body = '';
00066   private $response_headers = '';
00067 
00068   private $user_agent = 'Habari';
00069   
00070   // Array of adapter configuration options
00071   private $config = array(
00072     'connect_timeout'   => 30,
00073     'timeout'           => 180,
00074     'buffer_size'       => 16384,
00075     'follow_redirects'  => true,
00076     'max_redirects'     => 5,
00077 
00078     // These are configured in the main config file
00079     'proxy' => array(
00080       'server' => null,
00081       'port' => null,
00082       'username' => null,
00083       'password' => null,
00084       'auth_type' => 'basic',
00085       'exceptions' => array(),
00086       'type' => 'http',   // http is the default, 'socks' for a SOCKS5 proxy
00087     ),
00088 
00089     // TODO: These don't apply to SocketRequestProcessor yet
00090     'ssl' => array(
00091       'verify_peer' => true,
00092       'verify_host' => 2,   // 1 = check CN of ssl cert, 2 = check and verify @see http://php.net/curl_setopt
00093       'cafile' => null,
00094       'capath' => null,
00095       'local_cert' => null,
00096       'passphrase' => null,
00097       
00098     ),
00099   );
00100 
00107   public function __construct( $url, $method = 'GET', $timeout = 180 )
00108   {
00109     $this->method = strtoupper( $method );
00110     $this->url = $url;
00111     $this->set_timeout( $timeout );
00112     
00113     // load the proxy configuration, if it exists
00114     $default = new \stdClass();
00115     $proxy = Config::get( 'proxy', $default );
00116     if ( isset( $proxy->server ) ) {
00117       $this->set_config( array( 'proxy' => (array)$proxy ) );
00118     }
00119 
00120     // populate the default proxy exceptions list, since we can't up there
00121     $this->config['proxy']['exceptions'] = array_merge( $this->config['proxy']['exceptions'], array(
00122       'localhost',
00123       '127.0.0.1',
00124       '::1',    // ipv6 localhost
00125     ) );
00126     
00127     // these next two could be duplicates of 'localhost' and 127.0.0.1 / ::1 if you're on localhost - that's ok
00128     if ( isset( $_SERVER['SERVER_NAME'] ) ) {
00129       $this->config['proxy']['exceptions'][] = $_SERVER['SERVER_NAME'];
00130     }
00131     
00132     if ( isset( $_SERVER['SERVER_ADDR'] ) ) {
00133       $this->config['proxy']['exceptions'][] = $_SERVER['SERVER_ADDR'];
00134     }
00135 
00136     $this->user_agent .= '/' . Version::get_habariversion();
00137     $this->add_header( array( 'User-Agent' => $this->user_agent ) );
00138 
00139     // if they've manually specified that we should not use curl, use sockets instead
00140     if ( Config::get( 'remote_request_processor' ) == 'socket' ) {
00141       $this->processor = new SocketRequestProcessor();
00142     }
00143     else {
00144       // otherwise, see if we can use curl and fall back to sockets if not
00145       if ( function_exists( 'curl_init' )
00146         && ! ( ini_get( 'safe_mode' ) || ini_get( 'open_basedir' ) ) ) {
00147         $this->processor = new CURLRequestProcessor;
00148       }
00149       else {
00150         $this->processor = new SocketRequestProcessor;
00151       }
00152       
00153     }
00154   }
00155 
00160   public function __set_processor( $processor )
00161   {
00162     $this->processor = $processor;
00163   }
00164 
00171   public function set_config( $config, $value = null )
00172   {
00173     if ( is_array( $config ) ) {
00174       foreach ( $config as $name => $value ) {
00175         $this->set_config( $name, $value );
00176       }
00177     }
00178     else {
00179       if ( is_array( $value ) ) {
00180         $this->config[ $config ] = array_merge( $this->config[ $config ], $value );
00181       }
00182       else {
00183         $this->config[ $config ] = $value;
00184       }
00185     }
00186   }
00187   
00192   public function add_header( $header )
00193   {
00194     if ( is_array( $header ) ) {
00195       $this->headers = array_merge( $this->headers, $header );
00196     }
00197     else {
00198       list( $k, $v )= explode( ': ', $header );
00199       $this->headers[$k] = $v;
00200     }
00201   }
00202 
00207   public function add_headers( $headers )
00208   {
00209     foreach ( $headers as $header ) {
00210       $this->add_header( $header );
00211     }
00212   }
00213 
00220   public function set_body( $body )
00221   {
00222     if ( $this->method == 'GET' ) {
00223       throw new \Exception( _t( 'Trying to add a request body to a GET request.' ) );
00224     }
00225 
00226     $this->body = $body;
00227   }
00228 
00234   public function set_params( $params )
00235   {
00236     if ( ! is_array( $params ) ) {
00237       parse_str( $params, $params );
00238     }
00239 
00240     $this->params = $params;
00241   }
00242 
00247   public function set_timeout( $timeout )
00248   {
00249     $this->config['timeout'] = $timeout;
00250     return $this->config['timeout'];
00251   }
00252 
00260   public function set_postdata( $name, $value = null )
00261   {
00262     if ( is_array( $name ) ) {
00263       $this->postdata = array_merge( $this->postdata, $name );
00264     }
00265     else {
00266       $this->postdata[$name] = $value;
00267     }
00268   }
00269 
00280   public function set_file( $name, $filename, $content_type = null, $override_filename = null )
00281   {
00282     if ( !file_exists( $filename ) ) {
00283       throw new \Exception( _t( 'File %s not found.', array( $filename ) ) );
00284     }
00285     if ( empty( $content_type ) ) $content_type = 'application/octet-stream';
00286     $this->files[$name] = array( 'filename' => $filename, 'content_type' => $content_type, 'override_filename' => $override_filename );
00287     $this->headers['Content-Type'] = 'multipart/form-data';
00288   }
00289 
00293   private function prepare()
00294   {
00295     // remove anchors (#foo) from the URL
00296     $this->url = $this->strip_anchors( $this->url );
00297     // merge query params from the URL with params given
00298     $this->url = $this->merge_query_params( $this->url, $this->params );
00299 
00300     if ( $this->method === 'POST' ) {
00301       if ( !isset( $this->headers['Content-Type'] ) || ( $this->headers['Content-Type'] == 'application/x-www-form-urlencoded' ) ) {
00302         // TODO should raise a warning
00303         $this->add_header( array( 'Content-Type' => 'application/x-www-form-urlencoded' ) );
00304 
00305         if ( $this->body != '' && count( $this->postdata ) > 0 ) {
00306           $this->body .= '&';
00307         }
00308         $this->body .= http_build_query( $this->postdata, '', '&' );
00309       }
00310       elseif ( $this->headers['Content-Type'] == 'multipart/form-data' ) {
00311         $boundary = md5( Utils::nonce() );
00312         $this->headers['Content-Type'] .= '; boundary=' . $boundary;
00313 
00314         $parts = array();
00315         if ( $this->postdata && is_array( $this->postdata ) ) {
00316           reset( $this->postdata );
00317           while ( list( $name, $value ) = each( $this->postdata ) ) {
00318             $parts[] = "Content-Disposition: form-data; name=\"{$name}\"\r\n\r\n{$value}\r\n";
00319           }
00320         }
00321 
00322         if ( $this->files && is_array( $this->files ) ) {
00323           reset( $this->files );
00324           while ( list( $name, $fileinfo ) = each( $this->files ) ) {
00325             $filename = basename( $fileinfo['filename'] );
00326             if ( !empty( $fileinfo['override_filename'] ) ) {
00327               $filename = $fileinfo['override_filename'];
00328             }
00329             $part = "Content-Disposition: form-data; name=\"{$name}\"; filename=\"{$filename}\"\r\n";
00330             $part .= "Content-Type: {$fileinfo['content_type']}\r\n\r\n";
00331             $part .= file_get_contents( $fileinfo['filename'] ) . "\r\n";
00332             $parts[] = $part;
00333           }
00334         }
00335 
00336         if ( !empty( $parts ) ) {
00337           $this->body = "--{$boundary}\r\n" . join( "--{$boundary}\r\n", $parts ) . "--{$boundary}--\r\n";
00338         }
00339       }
00340       $this->add_header( array( 'Content-Length' => strlen( $this->body ) ) );
00341     }
00342   }
00343 
00351   public function execute()
00352   {
00353     $this->prepare();
00354     $result = $this->processor->execute( $this->method, $this->url, $this->headers, $this->body, $this->config );
00355 
00356     if ( $result ) { // XXX exceptions?
00357       $this->response_headers = $this->processor->get_response_headers();
00358       $this->response_body = $this->processor->get_response_body();
00359       $this->executed = true;
00360 
00361       return true;
00362     }
00363     else {
00364       // processor->execute should throw an Exception which would bubble up
00365       $this->executed = false;
00366 
00367       return $result;
00368     }
00369   }
00370 
00371   public function executed()
00372   {
00373     return $this->executed;
00374   }
00375 
00380   public function get_response_headers()
00381   {
00382     if ( !$this->executed ) {
00383       throw new \Exception( _t( 'Unable to fetch response headers for a pending request.' ) );
00384     }
00385 
00386     return $this->response_headers;
00387   }
00388 
00393   public function get_response_body()
00394   {
00395     if ( !$this->executed ) {
00396       throw new \Exception( _t( 'Unable to fetch response body for a pending request.' ) );
00397     }
00398 
00399     return $this->response_body;
00400   }
00401 
00405   private function strip_anchors( $url )
00406   {
00407     return preg_replace( '/(#.*?)?$/', '', $url );
00408   }
00409 
00413   private function __filter( $data, $url )
00414   {
00415     return Plugins::filter( 'remoterequest', $data, $url );
00416   }
00417 
00423   private function merge_query_params( $url, $params )
00424   {
00425     $urlparts = InputFilter::parse_url( $url );
00426 
00427     if ( ! isset( $urlparts['query'] ) ) {
00428       $urlparts['query'] = '';
00429     }
00430 
00431     if ( ! is_array( $params ) ) {
00432       parse_str( $params, $params );
00433     }
00434 
00435     $urlparts['query'] = http_build_query( array_merge( Utils::get_params( $urlparts['query'] ), $params ), '', '&' );
00436 
00437     return InputFilter::glue_url( $urlparts );
00438   }
00439 
00453   public static function get_contents( $url, $use_include_path = false, $context = null, $offset = 0, $maxlen = -1 )
00454   {
00455     try {
00456       $rr = new RemoteRequest( $url );
00457       if ( $rr->execute() === true ) {
00458         return ( $maxlen != -1
00459           ? MultiByte::substr( $rr->get_response_body(), $offset, $maxlen )
00460           : MultiByte::substr( $rr->get_response_body(), $offset ) );
00461       }
00462       else {
00463         return false;
00464       }
00465     }
00466     catch ( \Exception $e ) {
00467       // catch any exceptions to try and emulate file_get_contents() as closely as possible.
00468       // if you want more control over the errors, instantiate RemoteRequest manually
00469       return false;
00470     }
00471   }
00472 
00473 }
00474 
00475 class RemoteRequest_Timeout extends \Exception { }
00476 
00477 ?>

Generated on Sun Aug 4 2013 12:51:43 for Habari by  doxygen 1.7.1