|
1 | 1 | <?php
|
2 | 2 | /**
|
3 | 3 | * @see https://github.com/zendframework/zend-diactoros for the canonical source repository
|
4 |
| - * @copyright Copyright (c) 2015-2018 Zend Technologies USA Inc. (http://www.zend.com) |
| 4 | + * @copyright Copyright (c) 2015-2019 Zend Technologies USA Inc. (http://www.zend.com) |
5 | 5 | * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
6 | 6 | */
|
7 | 7 |
|
|
16 | 16 | use Zend\Diactoros\Response;
|
17 | 17 | use Zend\Diactoros\Stream;
|
18 | 18 |
|
| 19 | +use function curl_close; |
| 20 | +use function curl_exec; |
| 21 | +use function curl_getinfo; |
| 22 | +use function curl_init; |
| 23 | +use function curl_setopt; |
| 24 | +use function file_exists; |
| 25 | +use function file_put_contents; |
| 26 | +use function gmdate; |
19 | 27 | use function in_array;
|
20 |
| -use function libxml_set_streams_context; |
21 | 28 | use function preg_match;
|
22 |
| -use function stream_context_create; |
| 29 | +use function sprintf; |
| 30 | +use function strtotime; |
| 31 | + |
| 32 | +use const CURLINFO_HTTP_CODE; |
| 33 | +use const CURLOPT_HTTPHEADER; |
| 34 | +use const CURLOPT_RETURNTRANSFER; |
| 35 | +use const CURLOPT_TIMEOUT; |
| 36 | +use const LOCK_EX; |
23 | 37 |
|
24 | 38 | class ResponseTest extends TestCase
|
25 | 39 | {
|
@@ -51,28 +65,68 @@ public function testReasonPhraseDefaultsToStandards()
|
51 | 65 | $this->assertSame('Unprocessable Entity', $response->getReasonPhrase());
|
52 | 66 | }
|
53 | 67 |
|
54 |
| - public function ianaCodesReasonPhrasesProvider() |
| 68 | + private function fetchIanaStatusCodes() : DOMDocument |
55 | 69 | {
|
56 |
| - $ianaHttpStatusCodes = new DOMDocument(); |
57 |
| - |
58 |
| - libxml_set_streams_context( |
59 |
| - stream_context_create( |
60 |
| - [ |
61 |
| - 'http' => [ |
62 |
| - 'method' => 'GET', |
63 |
| - 'timeout' => 30, |
64 |
| - 'user_agent' => 'PHP', |
65 |
| - ], |
66 |
| - ] |
67 |
| - ) |
68 |
| - ); |
69 |
| - |
70 |
| - $ianaHttpStatusCodes->load('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml'); |
71 |
| - |
72 |
| - if (! $ianaHttpStatusCodes->relaxNGValidate(__DIR__ . '/TestAsset/http-status-codes.rng')) { |
73 |
| - self::fail('Unable to retrieve IANA response status codes due to timeout or invalid XML'); |
| 70 | + $updated = null; |
| 71 | + $ianaHttpStatusCodesFile = __DIR__ . '/TestAsset/.cache/http-status-codes.xml'; |
| 72 | + $ianaHttpStatusCodes = null; |
| 73 | + if (file_exists($ianaHttpStatusCodesFile)) { |
| 74 | + $ianaHttpStatusCodes = new DOMDocument(); |
| 75 | + $ianaHttpStatusCodes->load($ianaHttpStatusCodesFile); |
| 76 | + if (! $ianaHttpStatusCodes->relaxNGValidate(__DIR__ . '/TestAsset/http-status-codes.rng')) { |
| 77 | + $ianaHttpStatusCodes = null; |
| 78 | + } |
| 79 | + } |
| 80 | + if ($ianaHttpStatusCodes) { |
| 81 | + if (! getenv('ALWAYS_REFRESH_IANA_HTTP_STATUS_CODES')) { |
| 82 | + // use cached codes |
| 83 | + return $ianaHttpStatusCodes; |
| 84 | + } |
| 85 | + $xpath = new DOMXPath($ianaHttpStatusCodes); |
| 86 | + $xpath->registerNamespace('ns', 'http://www.iana.org/assignments'); |
| 87 | + $updated = $xpath->query('//ns:updated')->item(0)->nodeValue; |
| 88 | + $updated = strtotime($updated); |
| 89 | + } |
| 90 | + |
| 91 | + $ch = curl_init('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml'); |
| 92 | + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
| 93 | + curl_setopt($ch, CURLOPT_TIMEOUT, 30); |
| 94 | + curl_setopt($ch, CURLOPT_USERAGENT, 'PHP Curl'); |
| 95 | + if ($updated) { |
| 96 | + $ifModifiedSince = sprintf( |
| 97 | + 'If-Modified-Since: %s', |
| 98 | + gmdate('D, d M Y H:i:s \G\M\T', $updated) |
| 99 | + ); |
| 100 | + curl_setopt($ch, CURLOPT_HTTPHEADER, [$ifModifiedSince]); |
| 101 | + } |
| 102 | + $response = curl_exec($ch); |
| 103 | + $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
| 104 | + curl_close($ch); |
| 105 | + |
| 106 | + if ($responseCode === 304 && $ianaHttpStatusCodes) { |
| 107 | + // status codes did not change |
| 108 | + return $ianaHttpStatusCodes; |
74 | 109 | }
|
75 | 110 |
|
| 111 | + if ($responseCode === 200) { |
| 112 | + $downloadedIanaHttpStatusCodes = new DOMDocument(); |
| 113 | + $downloadedIanaHttpStatusCodes->loadXML($response); |
| 114 | + if ($downloadedIanaHttpStatusCodes->relaxNGValidate(__DIR__ . '/TestAsset/http-status-codes.rng')) { |
| 115 | + file_put_contents($ianaHttpStatusCodesFile, $response, LOCK_EX); |
| 116 | + return $downloadedIanaHttpStatusCodes; |
| 117 | + } |
| 118 | + } |
| 119 | + if ($ianaHttpStatusCodes) { |
| 120 | + // return cached codes if available |
| 121 | + return $ianaHttpStatusCodes; |
| 122 | + } |
| 123 | + self::fail('Unable to retrieve IANA response status codes due to timeout or invalid XML'); |
| 124 | + } |
| 125 | + |
| 126 | + public function ianaCodesReasonPhrasesProvider() |
| 127 | + { |
| 128 | + $ianaHttpStatusCodes = $this->fetchIanaStatusCodes(); |
| 129 | + |
76 | 130 | $ianaCodesReasonPhrases = [];
|
77 | 131 |
|
78 | 132 | $xpath = new DOMXPath($ianaHttpStatusCodes);
|
|
0 commit comments