Overview

Namespaces

  • Contrib
    • Bundle
      • CoverallsBundle
        • Console
        • Entity
      • CoverallsV1Bundle
        • Api
        • Collector
        • Command
        • Config
        • Entity
          • Git
    • Component
      • File
      • Log
      • System
        • Git
  • Guzzle
    • Batch
      • Exception
    • Cache
    • Common
      • Exception
    • Http
      • Curl
      • Exception
      • Message
      • QueryAggregator
    • Inflection
    • Iterator
    • Log
    • Parser
      • Cookie
      • Message
      • UriTemplate
      • Url
    • Plugin
      • Async
      • Backoff
      • Cache
      • Cookie
        • CookieJar
        • Exception
      • CurlAuth
      • ErrorResponse
        • Exception
      • History
      • Log
      • Md5
      • Mock
      • Oauth
    • Service
      • Builder
      • Command
        • Factory
        • LocationVisitor
          • Request
          • Response
      • Description
      • Exception
      • Resource
    • Stream
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Config
        • Definition
          • Builder
          • Exception
        • Exception
        • Loader
        • Resource
        • Util
      • Console
        • Command
        • Formatter
        • Helper
        • Input
        • Output
        • Tester
      • EventDispatcher
        • Debug
      • Finder
        • Adapter
        • Comparator
        • Exception
        • Expression
        • Iterator
        • Shell
      • Stopwatch
      • Yaml
        • Exception

Classes

  • PhpStreamRequestFactory
  • Stream

Interfaces

  • StreamInterface
  • StreamRequestFactoryInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Todo
  1: <?php
  2: 
  3: namespace Guzzle\Stream;
  4: 
  5: use Guzzle\Common\Exception\InvalidArgumentException;
  6: use Guzzle\Common\Exception\RuntimeException;
  7: use Guzzle\Http\Message\EntityEnclosingRequestInterface;
  8: use Guzzle\Http\Message\RequestInterface;
  9: use Guzzle\Http\Url;
 10: 
 11: /**
 12:  * Factory used to create fopen streams using PHP's http and https stream wrappers
 13:  *
 14:  * Note: PHP's http stream wrapper only supports streaming downloads. It does not support streaming uploads.
 15:  */
 16: class PhpStreamRequestFactory implements StreamRequestFactoryInterface
 17: {
 18:     /**
 19:      * @var resource Stream context options
 20:      */
 21:     protected $context;
 22: 
 23:     /**
 24:      * @var array Stream context
 25:      */
 26:     protected $contextOptions;
 27: 
 28:     /**
 29:      * @var Url Stream URL
 30:      */
 31:     protected $url;
 32: 
 33:     /**
 34:      * @var array Last response headers received by the HTTP request
 35:      */
 36:     protected $lastResponseHeaders;
 37: 
 38:     /**
 39:      * {@inheritdoc}
 40:      *
 41:      * The $params array can contain the following custom keys specific to the PhpStreamRequestFactory:
 42:      * - stream_class: The name of a class to create instead of a Guzzle\Stream\Stream object
 43:      */
 44:     public function fromRequest(RequestInterface $request, $context = array(), array $params = array())
 45:     {
 46:         if (is_resource($context)) {
 47:             $this->contextOptions = stream_context_get_options($context);
 48:             $this->context = $context;
 49:         } elseif (is_array($context) || !$context) {
 50:             $this->contextOptions = $context;
 51:             $this->createContext($params);
 52:         } elseif ($context) {
 53:             throw new InvalidArgumentException('$context must be an array or resource');
 54:         }
 55: 
 56:         $this->setUrl($request);
 57:         $this->addDefaultContextOptions($request);
 58:         $this->addSslOptions($request);
 59:         $this->addBodyOptions($request);
 60:         $this->addProxyOptions($request);
 61: 
 62:         // Dispatch the before send event
 63:         $request->dispatch('request.before_send', array(
 64:             'request'         => $request,
 65:             'context'         => $this->context,
 66:             'context_options' => $this->contextOptions
 67:         ));
 68: 
 69:         // Create the file handle but silence errors
 70:         return $this->createStream($params)
 71:             ->setCustomData('request', $request)
 72:             ->setCustomData('response_headers', $this->getLastResponseHeaders());
 73:     }
 74: 
 75:     /**
 76:      * Set an option on the context and the internal options array
 77:      *
 78:      * @param string $wrapper   Stream wrapper name of http
 79:      * @param string $name      Context name
 80:      * @param mixed  $value     Context value
 81:      * @param bool   $overwrite Set to true to overwrite an existing value
 82:      */
 83:     protected function setContextValue($wrapper, $name, $value, $overwrite = false)
 84:     {
 85:         if (!isset($this->contextOptions[$wrapper])) {
 86:             $this->contextOptions[$wrapper] = array($name => $value);
 87:         } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) {
 88:             return;
 89:         }
 90:         $this->contextOptions[$wrapper][$name] = $value;
 91:         stream_context_set_option($this->context, $wrapper, $name, $value);
 92:     }
 93: 
 94:     /**
 95:      * Create a stream context
 96:      *
 97:      * @param array $params Parameter array
 98:      */
 99:     protected function createContext(array $params)
100:     {
101:         $options = $this->contextOptions;
102:         $this->context = $this->createResource(function () use ($params, $options) {
103:             return stream_context_create($options, $params);
104:         });
105:     }
106: 
107:     /**
108:      * Get the last response headers received by the HTTP request
109:      *
110:      * @return array
111:      */
112:     public function getLastResponseHeaders()
113:     {
114:         return $this->lastResponseHeaders;
115:     }
116: 
117:     /**
118:      * Adds the default context options to the stream context options
119:      *
120:      * @param RequestInterface $request Request
121:      */
122:     protected function addDefaultContextOptions(RequestInterface $request)
123:     {
124:         $this->setContextValue('http', 'method', $request->getMethod());
125:         $this->setContextValue('http', 'header', $request->getHeaderLines());
126:         // Force 1.0 for now until PHP fully support chunked transfer-encoding decoding
127:         $this->setContextValue('http', 'protocol_version', '1.0');
128:         $this->setContextValue('http', 'ignore_errors', true);
129:     }
130: 
131:     /**
132:      * Set the URL to use with the factory
133:      *
134:      * @param RequestInterface $request Request that owns the URL
135:      */
136:     protected function setUrl(RequestInterface $request)
137:     {
138:         $this->url = $request->getUrl(true);
139: 
140:         // Check for basic Auth username
141:         if ($request->getUsername()) {
142:             $this->url->setUsername($request->getUsername());
143:         }
144: 
145:         // Check for basic Auth password
146:         if ($request->getPassword()) {
147:             $this->url->setPassword($request->getPassword());
148:         }
149:     }
150: 
151:     /**
152:      * Add SSL options to the stream context
153:      *
154:      * @param RequestInterface $request Request
155:      */
156:     protected function addSslOptions(RequestInterface $request)
157:     {
158:         if ($verify = $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) {
159:             $this->setContextValue('ssl', 'verify_peer', true, true);
160:             if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) {
161:                 $this->setContextValue('ssl', 'cafile', $cafile, true);
162:             }
163:         } else {
164:             $this->setContextValue('ssl', 'verify_peer', false, true);
165:         }
166:     }
167: 
168:     /**
169:      * Add body (content) specific options to the context options
170:      *
171:      * @param RequestInterface $request
172:      */
173:     protected function addBodyOptions(RequestInterface $request)
174:     {
175:         // Add the content for the request if needed
176:         if (!($request instanceof EntityEnclosingRequestInterface)) {
177:             return;
178:         }
179: 
180:         if (count($request->getPostFields())) {
181:             $this->setContextValue('http', 'content', (string) $request->getPostFields(), true);
182:         } elseif ($request->getBody()) {
183:             $this->setContextValue('http', 'content', (string) $request->getBody(), true);
184:         }
185: 
186:         // Always ensure a content-length header is sent
187:         if (isset($this->contextOptions['http']['content'])) {
188:             $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array();
189:             $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']);
190:             $this->setContextValue('http', 'header', $headers, true);
191:         }
192:     }
193: 
194:     /**
195:      * Add proxy parameters to the context if needed
196:      *
197:      * @param RequestInterface $request Request
198:      */
199:     protected function addProxyOptions(RequestInterface $request)
200:     {
201:         if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) {
202:             $this->setContextValue('http', 'proxy', $proxy);
203:         }
204:     }
205: 
206:     /**
207:      * Create the stream for the request with the context options
208:      *
209:      * @param array $params Parameters of the stream
210:      *
211:      * @return StreamInterface
212:      */
213:     protected function createStream(array $params)
214:     {
215:         $http_response_header = null;
216:         $url = $this->url;
217:         $context = $this->context;
218:         $fp = $this->createResource(function () use ($context, $url, &$http_response_header) {
219:             return fopen((string) $url, 'r', false, $context);
220:         });
221: 
222:         // Track the response headers of the request
223:         if (isset($http_response_header)) {
224:             $this->lastResponseHeaders = $http_response_header;
225:         }
226: 
227:         // Determine the class to instantiate
228:         $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream';
229: 
230:         return new $className($fp);
231:     }
232: 
233:     /**
234:      * Create a resource and check to ensure it was created successfully
235:      *
236:      * @param callable $callback Closure to invoke that must return a valid resource
237:      *
238:      * @return resource
239:      * @throws RuntimeException on error
240:      */
241:     protected function createResource($callback)
242:     {
243:         // Turn off error reporting while we try to initiate the request
244:         $level = error_reporting(0);
245:         $resource = call_user_func($callback);
246:         error_reporting($level);
247: 
248:         // If the resource could not be created, then grab the last error and throw an exception
249:         if (false === $resource) {
250:             $message = 'Error creating resource. ';
251:             foreach (error_get_last() as $key => $value) {
252:                 $message .= "[{$key}] {$value} ";
253:             }
254:             throw new RuntimeException(trim($message));
255:         }
256: 
257:         return $resource;
258:     }
259: }
260: 
php-coveralls API documentation generated by ApiGen 2.8.0