Source for file socket.php

Documentation is available at socket.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Platform
  4.  * @subpackage  HTTP
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM'or die;
  11.  
  12. /**
  13.  * HTTP transport class for using sockets directly.
  14.  *
  15.  * @package     Joomla.Platform
  16.  * @subpackage  HTTP
  17.  * @since       11.3
  18.  */
  19. class JHttpTransportSocket implements JHttpTransport
  20. {
  21.     /**
  22.      * @var    array  Reusable socket connections.
  23.      * @since  11.3
  24.      */
  25.     protected $connections;
  26.  
  27.     /**
  28.      * @var    JRegistry  The client options.
  29.      * @since  11.3
  30.      */
  31.     protected $options;
  32.  
  33.     /**
  34.      * Constructor.
  35.      *
  36.      * @param   JRegistry  $options  Client options object.
  37.      *
  38.      * @since   11.3
  39.      * @throws  RuntimeException
  40.      */
  41.     public function __construct(JRegistry $options)
  42.     {
  43.         if (!self::isSupported())
  44.         {
  45.             throw new RuntimeException('Cannot use a socket transport when fsockopen() is not available.');
  46.         }
  47.  
  48.         $this->options = $options;
  49.     }
  50.  
  51.     /**
  52.      * Send a request to the server and return a JHttpResponse object with the response.
  53.      *
  54.      * @param   string   $method     The HTTP method for sending the request.
  55.      * @param   JUri     $uri        The URI to the resource to request.
  56.      * @param   mixed    $data       Either an associative array or a string to be sent with the request.
  57.      * @param   array    $headers    An array of request headers to send with the request.
  58.      * @param   integer  $timeout    Read timeout in seconds.
  59.      * @param   string   $userAgent  The optional user agent string to send with the request.
  60.      *
  61.      * @return  JHttpResponse 
  62.      *
  63.      * @since   11.3
  64.      * @throws  RuntimeException
  65.      */
  66.     public function request($methodJUri $uri$data nullarray $headers null$timeout null$userAgent null)
  67.     {
  68.         $connection $this->connect($uri$timeout);
  69.  
  70.         // Make sure the connection is alive and valid.
  71.         if (is_resource($connection))
  72.         {
  73.             // Make sure the connection has not timed out.
  74.             $meta stream_get_meta_data($connection);
  75.  
  76.             if ($meta['timed_out'])
  77.             {
  78.                 throw new RuntimeException('Server connection timed out.');
  79.             }
  80.         }
  81.         else
  82.         {
  83.             throw new RuntimeException('Not connected to server.');
  84.         }
  85.  
  86.         // Get the request path from the URI object.
  87.         $path $uri->toString(array('path''query'));
  88.  
  89.         // If we have data to send make sure our request is setup for it.
  90.         if (!empty($data))
  91.         {
  92.             // If the data is not a scalar value encode it to be sent with the request.
  93.             if (!is_scalar($data))
  94.             {
  95.                 $data http_build_query($data);
  96.             }
  97.  
  98.             if (!isset($headers['Content-Type']))
  99.             {
  100.                 $headers['Content-Type''application/x-www-form-urlencoded; charset=utf-8';
  101.             }
  102.  
  103.             // Add the relevant headers.
  104.             $headers['Content-Length'strlen($data);
  105.         }
  106.  
  107.         // Build the request payload.
  108.         $request array();
  109.         $request[strtoupper($method' ' ((empty($path)) '/' $path' HTTP/1.0';
  110.         $request['Host: ' $uri->getHost();
  111.  
  112.         // If an explicit user agent is given use it.
  113.         if (isset($userAgent))
  114.         {
  115.             $headers['User-Agent'$userAgent;
  116.         }
  117.  
  118.         // If there are custom headers to send add them to the request payload.
  119.         if (is_array($headers))
  120.         {
  121.             foreach ($headers as $k => $v)
  122.             {
  123.                 $request[$k ': ' $v;
  124.             }
  125.         }
  126.  
  127.         // If we have data to send add it to the request payload.
  128.         if (!empty($data))
  129.         {
  130.             $request[null;
  131.             $request[$data;
  132.         }
  133.  
  134.         // Send the request to the server.
  135.         fwrite($connectionimplode("\r\n"$request"\r\n\r\n");
  136.  
  137.         // Get the response data from the server.
  138.         $content '';
  139.  
  140.         while (!feof($connection))
  141.         {
  142.             $content .= fgets($connection4096);
  143.         }
  144.  
  145.         return $this->getResponse($content);
  146.     }
  147.  
  148.     /**
  149.      * Method to get a response object from a server response.
  150.      *
  151.      * @param   string  $content  The complete server response, including headers.
  152.      *
  153.      * @return  JHttpResponse 
  154.      *
  155.      * @since   11.3
  156.      * @throws  UnexpectedValueException
  157.      */
  158.     protected function getResponse($content)
  159.     {
  160.         // Create the response object.
  161.         $return new JHttpResponse;
  162.  
  163.         if (empty($content))
  164.         {
  165.             throw new UnexpectedValueException('No content in response.');
  166.         }
  167.  
  168.         // Split the response into headers and body.
  169.         $response explode("\r\n\r\n"$content2);
  170.  
  171.         // Get the response headers as an array.
  172.         $headers explode("\r\n"$response[0]);
  173.  
  174.         // Set the body for the response.
  175.         $return->body empty($response[1]'' $response[1];
  176.  
  177.         // Get the response code from the first offset of the response headers.
  178.         preg_match('/[0-9]{3}/'array_shift($headers)$matches);
  179.         $code $matches[0];
  180.  
  181.         if (is_numeric($code))
  182.         {
  183.             $return->code = (int) $code;
  184.         }
  185.  
  186.         // No valid response code was detected.
  187.         else
  188.         {
  189.             throw new UnexpectedValueException('No HTTP response code found.');
  190.         }
  191.  
  192.         // Add the response headers to the response object.
  193.         foreach ($headers as $header)
  194.         {
  195.             $pos strpos($header':');
  196.             $return->headers[trim(substr($header0$pos))trim(substr($header($pos 1)));
  197.         }
  198.  
  199.         return $return;
  200.     }
  201.  
  202.     /**
  203.      * Method to connect to a server and get the resource.
  204.      *
  205.      * @param   JUri     $uri      The URI to connect with.
  206.      * @param   integer  $timeout  Read timeout in seconds.
  207.      *
  208.      * @return  resource  Socket connection resource.
  209.      *
  210.      * @since   11.3
  211.      * @throws  RuntimeException
  212.      */
  213.     protected function connect(JUri $uri$timeout null)
  214.     {
  215.         $errno null;
  216.         $err null;
  217.  
  218.         // Get the host from the uri.
  219.         $host ($uri->isSSL()) 'ssl://' $uri->getHost($uri->getHost();
  220.  
  221.         // If the port is not explicitly set in the URI detect it.
  222.         if (!$uri->getPort())
  223.         {
  224.             $port ($uri->getScheme(== 'https'443 80;
  225.         }
  226.  
  227.         // Use the set port.
  228.         else
  229.         {
  230.             $port $uri->getPort();
  231.         }
  232.  
  233.         // Build the connection key for resource memory caching.
  234.         $key md5($host $port);
  235.  
  236.         // If the connection already exists, use it.
  237.         if (!empty($this->connections[$key]&& is_resource($this->connections[$key]))
  238.         {
  239.             // Connection reached EOF, cannot be used anymore
  240.             $meta stream_get_meta_data($this->connections[$key]);
  241.  
  242.             if ($meta['eof'])
  243.             {
  244.                 if (!fclose($this->connections[$key]))
  245.                 {
  246.                     throw new RuntimeException('Cannot close connection');
  247.                 }
  248.             }
  249.  
  250.             // Make sure the connection has not timed out.
  251.             elseif (!$meta['timed_out'])
  252.             {
  253.                 return $this->connections[$key];
  254.             }
  255.         }
  256.  
  257.         if (!is_numeric($timeout))
  258.         {
  259.             $timeout ini_get('default_socket_timeout');
  260.         }
  261.  
  262.         // Capture PHP errors
  263.         $php_errormsg '';
  264.         $track_errors ini_get('track_errors');
  265.         ini_set('track_errors'true);
  266.  
  267.         // PHP sends a warning if the uri does not exists; we silence it and throw an exception instead.
  268.         // Attempt to connect to the server
  269.         $connection @fsockopen($host$port$errno$err$timeout);
  270.  
  271.         if (!$connection)
  272.         {
  273.             if (!$php_errormsg)
  274.             {
  275.                 // Error but nothing from php? Create our own
  276.                 $php_errormsg sprintf('Could not connect to resource: %s'$uri$err$errno);
  277.             }
  278.  
  279.             // Restore error tracking to give control to the exception handler
  280.             ini_set('track_errors'$track_errors);
  281.  
  282.             throw new RuntimeException($php_errormsg);
  283.         }
  284.  
  285.         // Restore error tracking to what it was before.
  286.         ini_set('track_errors'$track_errors);
  287.  
  288.         // Since the connection was successful let's store it in case we need to use it later.
  289.         $this->connections[$key$connection;
  290.  
  291.         // If an explicit timeout is set, set it.
  292.         if (isset($timeout))
  293.         {
  294.             stream_set_timeout($this->connections[$key](int) $timeout);
  295.         }
  296.  
  297.         return $this->connections[$key];
  298.     }
  299.  
  300.     /**
  301.      * Method to check if http transport socket available for use
  302.      *
  303.      * @return  boolean   True if available else false
  304.      *
  305.      * @since   12.1
  306.      */
  307.     public static function isSupported()
  308.     {
  309.         return function_exists('fsockopen'&& is_callable('fsockopen');
  310.     }
  311.  
  312. }

Documentation generated on Tue, 19 Nov 2013 15:14:01 +0100 by phpDocumentor 1.4.3