Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
StreamHandler.php
001 <?php
002 namespace GuzzleHttp\Ring\Client;
003
004 use GuzzleHttp\Ring\Core;
005 use GuzzleHttp\Ring\Exception\ConnectException;
006 use GuzzleHttp\Ring\Exception\RingException;
007 use GuzzleHttp\Ring\Future\CompletedFutureArray;
008 use GuzzleHttp\Stream\InflateStream;
009 use GuzzleHttp\Stream\StreamInterface;
010 use GuzzleHttp\Stream\Stream;
011 use GuzzleHttp\Stream\Utils;
012
013 /**
014 * RingPHP client handler that uses PHP's HTTP stream wrapper.
015 */
016 class StreamHandler
017 {
018 private $options;
019 private $lastHeaders;
020
021 public function __construct(array $options = [])
022 {
023 $this->options = $options;
024 }
025
026 public function __invoke(array $request)
027 {
028 $url = Core::url($request);
029 Core::doSleep($request);
030
031 try {
032 // Does not support the expect header.
033 $request = Core::removeHeader($request, 'Expect');
034 $stream = $this->createStream($url, $request);
035 return $this->createResponse($request, $url, $stream);
036 } catch (RingException $e) {
037 return $this->createErrorResponse($url, $e);
038 }
039 }
040
041 private function createResponse(array $request, $url, $stream)
042 {
043 $hdrs = $this->lastHeaders;
044 $this->lastHeaders = null;
045 $parts = explode(' ', array_shift($hdrs), 3);
046 $response = [
047 'version' => substr($parts[0], 5),
048 'status' => $parts[1],
049 'reason' => isset($parts[2]) ? $parts[2] : null,
050 'headers' => Core::headersFromLines($hdrs),
051 'effective_url' => $url,
052 ];
053
054 $stream = $this->checkDecode($request, $response, $stream);
055
056 // If not streaming, then drain the response into a stream.
057 if (empty($request['client']['stream'])) {
058 $dest = isset($request['client']['save_to'])
059 ? $request['client']['save_to']
060 : fopen('php://temp', 'r+');
061 $stream = $this->drain($stream, $dest);
062 }
063
064 $response['body'] = $stream;
065
066 return new CompletedFutureArray($response);
067 }
068
069 private function checkDecode(array $request, array $response, $stream)
070 {
071 // Automatically decode responses when instructed.
072 if (!empty($request['client']['decode_content'])) {
073 switch (Core::firstHeader($response, 'Content-Encoding', true)) {
074 case 'gzip':
075 case 'deflate':
076 $stream = new InflateStream(Stream::factory($stream));
077 break;
078 }
079 }
080
081 return $stream;
082 }
083
084 /**
085 * Drains the stream into the "save_to" client option.
086 *
087 * @param resource $stream
088 * @param string|resource|StreamInterface $dest
089 *
090 * @return Stream
091 * @throws \RuntimeException when the save_to option is invalid.
092 */
093 private function drain($stream, $dest)
094 {
095 if (is_resource($stream)) {
096 if (!is_resource($dest)) {
097 $stream = Stream::factory($stream);
098 } else {
099 stream_copy_to_stream($stream, $dest);
100 fclose($stream);
101 rewind($dest);
102 return $dest;
103 }
104 }
105
106 // Stream the response into the destination stream
107 $dest = is_string($dest)
108 ? new Stream(Utils::open($dest, 'r+'))
109 : Stream::factory($dest);
110
111 Utils::copyToStream($stream, $dest);
112 $dest->seek(0);
113 $stream->close();
114
115 return $dest;
116 }
117
118 /**
119 * Creates an error response for the given stream.
120 *
121 * @param string $url
122 * @param RingException $e
123 *
124 * @return array
125 */
126 private function createErrorResponse($url, RingException $e)
127 {
128 // Determine if the error was a networking error.
129 $message = $e->getMessage();
130
131 // This list can probably get more comprehensive.
132 if (strpos($message, 'getaddrinfo') // DNS lookup failed
133 || strpos($message, 'Connection refused')
134 ) {
135 $e = new ConnectException($e->getMessage(), 0, $e);
136 }
137
138 return new CompletedFutureArray([
139 'status' => null,
140 'body' => null,
141 'headers' => [],
142 'effective_url' => $url,
143 'error' => $e
144 ]);
145 }
146
147 /**
148 * Create a resource and check to ensure it was created successfully
149 *
150 * @param callable $callback Callable that returns stream resource
151 *
152 * @return resource
153 * @throws \RuntimeException on error
154 */
155 private function createResource(callable $callback)
156 {
157 $errors = null;
158 set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
159 $errors[] = [
160 'message' => $msg,
161 'file' => $file,
162 'line' => $line
163 ];
164 return true;
165 });
166
167 $resource = $callback();
168 restore_error_handler();
169
170 if (!$resource) {
171 $message = 'Error creating resource: ';
172 foreach ($errors as $err) {
173 foreach ($err as $key => $value) {
174 $message .= "[$key] $value" . PHP_EOL;
175 }
176 }
177 throw new RingException(trim($message));
178 }
179
180 return $resource;
181 }
182
183 private function createStream($url, array $request)
184 {
185 static $methods;
186 if (!$methods) {
187 $methods = array_flip(get_class_methods(__CLASS__));
188 }
189
190 // HTTP/1.1 streams using the PHP stream wrapper require a
191 // Connection: close header
192 if ((!isset($request['version']) || $request['version'] == '1.1')
193 && !Core::hasHeader($request, 'Connection')
194 ) {
195 $request['headers']['Connection'] = ['close'];
196 }
197
198 // Ensure SSL is verified by default
199 if (!isset($request['client']['verify'])) {
200 $request['client']['verify'] = true;
201 }
202
203 $params = [];
204 $options = $this->getDefaultOptions($request);
205
206 if (isset($request['client'])) {
207 foreach ($request['client'] as $key => $value) {
208 $method = "add_{$key}";
209 if (isset($methods[$method])) {
210 $this->{$method}($request, $options, $value, $params);
211 }
212 }
213 }
214
215 return $this->createStreamResource(
216 $url,
217 $request,
218 $options,
219 $this->createContext($request, $options, $params)
220 );
221 }
222
223 private function getDefaultOptions(array $request)
224 {
225 $headers = "";
226 foreach ($request['headers'] as $name => $value) {
227 foreach ((array) $value as $val) {
228 $headers .= "$name: $val\r\n";
229 }
230 }
231
232 $context = [
233 'http' => [
234 'method' => $request['http_method'],
235 'header' => $headers,
236 'protocol_version' => isset($request['version']) ? $request['version'] : 1.1,
237 'ignore_errors' => true,
238 'follow_location' => 0,
239 ],
240 ];
241
242 $body = Core::body($request);
243 if (isset($body)) {
244 $context['http']['content'] = $body;
245 // Prevent the HTTP handler from adding a Content-Type header.
246 if (!Core::hasHeader($request, 'Content-Type')) {
247 $context['http']['header'] .= "Content-Type:\r\n";
248 }
249 }
250
251 $context['http']['header'] = rtrim($context['http']['header']);
252
253 return $context;
254 }
255
256 private function add_proxy(array $request, &$options, $value, &$params)
257 {
258 if (!is_array($value)) {
259 $options['http']['proxy'] = $value;
260 } else {
261 $scheme = isset($request['scheme']) ? $request['scheme'] : 'http';
262 if (isset($value[$scheme])) {
263 $options['http']['proxy'] = $value[$scheme];
264 }
265 }
266 }
267
268 private function add_timeout(array $request, &$options, $value, &$params)
269 {
270 $options['http']['timeout'] = $value;
271 }
272
273 private function add_verify(array $request, &$options, $value, &$params)
274 {
275 if ($value === true) {
276 // PHP 5.6 or greater will find the system cert by default. When
277 // < 5.6, use the Guzzle bundled cacert.
278 if (PHP_VERSION_ID < 50600) {
279 $options['ssl']['cafile'] = ClientUtils::getDefaultCaBundle();
280 }
281 } elseif (is_string($value)) {
282 $options['ssl']['cafile'] = $value;
283 if (!file_exists($value)) {
284 throw new RingException("SSL CA bundle not found: $value");
285 }
286 } elseif ($value === false) {
287 $options['ssl']['verify_peer'] = false;
288 $options['ssl']['allow_self_signed'] = true;
289 return;
290 } else {
291 throw new RingException('Invalid verify request option');
292 }
293
294 $options['ssl']['verify_peer'] = true;
295 $options['ssl']['allow_self_signed'] = false;
296 }
297
298 private function add_cert(array $request, &$options, $value, &$params)
299 {
300 if (is_array($value)) {
301 $options['ssl']['passphrase'] = $value[1];
302 $value = $value[0];
303 }
304
305 if (!file_exists($value)) {
306 throw new RingException("SSL certificate not found: {$value}");
307 }
308
309 $options['ssl']['local_cert'] = $value;
310 }
311
312 private function add_progress(array $request, &$options, $value, &$params)
313 {
314 $fn = function ($code, $_1, $_2, $_3, $transferred, $total) use ($value) {
315 if ($code == STREAM_NOTIFY_PROGRESS) {
316 $value($total, $transferred, null, null);
317 }
318 };
319
320 // Wrap the existing function if needed.
321 $params['notification'] = isset($params['notification'])
322 ? Core::callArray([$params['notification'], $fn])
323 : $fn;
324 }
325
326 private function add_debug(array $request, &$options, $value, &$params)
327 {
328 if ($value === false) {
329 return;
330 }
331
332 static $map = [
333 STREAM_NOTIFY_CONNECT => 'CONNECT',
334 STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
335 STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
336 STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
337 STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
338 STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
339 STREAM_NOTIFY_PROGRESS => 'PROGRESS',
340 STREAM_NOTIFY_FAILURE => 'FAILURE',
341 STREAM_NOTIFY_COMPLETED => 'COMPLETED',
342 STREAM_NOTIFY_RESOLVE => 'RESOLVE',
343 ];
344
345 static $args = ['severity', 'message', 'message_code',
346 'bytes_transferred', 'bytes_max'];
347
348 $value = Core::getDebugResource($value);
349 $ident = $request['http_method'] . ' ' . Core::url($request);
350 $fn = function () use ($ident, $value, $map, $args) {
351 $passed = func_get_args();
352 $code = array_shift($passed);
353 fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
354 foreach (array_filter($passed) as $i => $v) {
355 fwrite($value, $args[$i] . ': "' . $v . '" ');
356 }
357 fwrite($value, "\n");
358 };
359
360 // Wrap the existing function if needed.
361 $params['notification'] = isset($params['notification'])
362 ? Core::callArray([$params['notification'], $fn])
363 : $fn;
364 }
365
366 private function applyCustomOptions(array $request, array &$options)
367 {
368 if (!isset($request['client']['stream_context'])) {
369 return;
370 }
371
372 if (!is_array($request['client']['stream_context'])) {
373 throw new RingException('stream_context must be an array');
374 }
375
376 $options = array_replace_recursive(
377 $options,
378 $request['client']['stream_context']
379 );
380 }
381
382 private function createContext(array $request, array $options, array $params)
383 {
384 $this->applyCustomOptions($request, $options);
385 return $this->createResource(
386 function () use ($request, $options, $params) {
387 return stream_context_create($options, $params);
388 },
389 $request,
390 $options
391 );
392 }
393
394 private function createStreamResource(
395 $url,
396 array $request,
397 array $options,
398 $context
399 ) {
400 return $this->createResource(
401 function () use ($url, $context) {
402 if (false === strpos($url, 'http')) {
403 trigger_error("URL is invalid: {$url}", E_USER_WARNING);
404 return null;
405 }
406 $resource = fopen($url, 'r', null, $context);
407 $this->lastHeaders = $http_response_header;
408 return $resource;
409 },
410 $request,
411 $options
412 );
413 }
414 }
415