Mercurial > hg > isophonics-drupal-site
comparison core/modules/media/src/OEmbed/ResourceFetcher.php @ 17:129ea1e6d783
Update, including to Drupal core 8.6.10
author | Chris Cannam |
---|---|
date | Thu, 28 Feb 2019 13:21:36 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
16:c2387f117808 | 17:129ea1e6d783 |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\media\OEmbed; | |
4 | |
5 use Drupal\Component\Serialization\Json; | |
6 use Drupal\Core\Cache\CacheBackendInterface; | |
7 use Drupal\Core\Cache\UseCacheBackendTrait; | |
8 use GuzzleHttp\ClientInterface; | |
9 use GuzzleHttp\Exception\RequestException; | |
10 | |
11 /** | |
12 * Fetches and caches oEmbed resources. | |
13 */ | |
14 class ResourceFetcher implements ResourceFetcherInterface { | |
15 | |
16 use UseCacheBackendTrait; | |
17 | |
18 /** | |
19 * The HTTP client. | |
20 * | |
21 * @var \GuzzleHttp\Client | |
22 */ | |
23 protected $httpClient; | |
24 | |
25 /** | |
26 * The oEmbed provider repository service. | |
27 * | |
28 * @var \Drupal\media\OEmbed\ProviderRepositoryInterface | |
29 */ | |
30 protected $providers; | |
31 | |
32 /** | |
33 * Constructs a ResourceFetcher object. | |
34 * | |
35 * @param \GuzzleHttp\ClientInterface $http_client | |
36 * The HTTP client. | |
37 * @param \Drupal\media\OEmbed\ProviderRepositoryInterface $providers | |
38 * The oEmbed provider repository service. | |
39 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend | |
40 * (optional) The cache backend. | |
41 */ | |
42 public function __construct(ClientInterface $http_client, ProviderRepositoryInterface $providers, CacheBackendInterface $cache_backend = NULL) { | |
43 $this->httpClient = $http_client; | |
44 $this->providers = $providers; | |
45 $this->cacheBackend = $cache_backend; | |
46 $this->useCaches = isset($cache_backend); | |
47 } | |
48 | |
49 /** | |
50 * {@inheritdoc} | |
51 */ | |
52 public function fetchResource($url) { | |
53 $cache_id = "media:oembed_resource:$url"; | |
54 | |
55 $cached = $this->cacheGet($cache_id); | |
56 if ($cached) { | |
57 return $this->createResource($cached->data, $url); | |
58 } | |
59 | |
60 try { | |
61 $response = $this->httpClient->get($url); | |
62 } | |
63 catch (RequestException $e) { | |
64 throw new ResourceException('Could not retrieve the oEmbed resource.', $url, [], $e); | |
65 } | |
66 | |
67 list($format) = $response->getHeader('Content-Type'); | |
68 $content = (string) $response->getBody(); | |
69 | |
70 if (strstr($format, 'text/xml') || strstr($format, 'application/xml')) { | |
71 $data = $this->parseResourceXml($content, $url); | |
72 } | |
73 elseif (strstr($format, 'text/javascript') || strstr($format, 'application/json')) { | |
74 $data = Json::decode($content); | |
75 } | |
76 // If the response is neither XML nor JSON, we are in bat country. | |
77 else { | |
78 throw new ResourceException('The fetched resource did not have a valid Content-Type header.', $url); | |
79 } | |
80 | |
81 $this->cacheSet($cache_id, $data); | |
82 | |
83 return $this->createResource($data, $url); | |
84 } | |
85 | |
86 /** | |
87 * Creates a Resource object from raw resource data. | |
88 * | |
89 * @param array $data | |
90 * The resource data returned by the provider. | |
91 * @param string $url | |
92 * The URL of the resource. | |
93 * | |
94 * @return \Drupal\media\OEmbed\Resource | |
95 * A value object representing the resource. | |
96 * | |
97 * @throws \Drupal\media\OEmbed\ResourceException | |
98 * If the resource cannot be created. | |
99 */ | |
100 protected function createResource(array $data, $url) { | |
101 $data += [ | |
102 'title' => NULL, | |
103 'author_name' => NULL, | |
104 'author_url' => NULL, | |
105 'provider_name' => NULL, | |
106 'cache_age' => NULL, | |
107 'thumbnail_url' => NULL, | |
108 'thumbnail_width' => NULL, | |
109 'thumbnail_height' => NULL, | |
110 'width' => NULL, | |
111 'height' => NULL, | |
112 'url' => NULL, | |
113 'html' => NULL, | |
114 'version' => NULL, | |
115 ]; | |
116 | |
117 if ($data['version'] !== '1.0') { | |
118 throw new ResourceException("Resource version must be '1.0'", $url, $data); | |
119 } | |
120 | |
121 // Prepare the arguments to pass to the factory method. | |
122 $provider = $data['provider_name'] ? $this->providers->get($data['provider_name']) : NULL; | |
123 | |
124 // The Resource object will validate the data we create it with and throw an | |
125 // exception if anything looks wrong. For better debugging, catch those | |
126 // exceptions and wrap them in a more specific and useful exception. | |
127 try { | |
128 switch ($data['type']) { | |
129 case Resource::TYPE_LINK: | |
130 return Resource::link( | |
131 $data['url'], | |
132 $provider, | |
133 $data['title'], | |
134 $data['author_name'], | |
135 $data['author_url'], | |
136 $data['cache_age'], | |
137 $data['thumbnail_url'], | |
138 $data['thumbnail_width'], | |
139 $data['thumbnail_height'] | |
140 ); | |
141 | |
142 case Resource::TYPE_PHOTO: | |
143 return Resource::photo( | |
144 $data['url'], | |
145 $data['width'], | |
146 $data['height'], | |
147 $provider, | |
148 $data['title'], | |
149 $data['author_name'], | |
150 $data['author_url'], | |
151 $data['cache_age'], | |
152 $data['thumbnail_url'], | |
153 $data['thumbnail_width'], | |
154 $data['thumbnail_height'] | |
155 ); | |
156 | |
157 case Resource::TYPE_RICH: | |
158 return Resource::rich( | |
159 $data['html'], | |
160 $data['width'], | |
161 $data['height'], | |
162 $provider, | |
163 $data['title'], | |
164 $data['author_name'], | |
165 $data['author_url'], | |
166 $data['cache_age'], | |
167 $data['thumbnail_url'], | |
168 $data['thumbnail_width'], | |
169 $data['thumbnail_height'] | |
170 ); | |
171 case Resource::TYPE_VIDEO: | |
172 return Resource::video( | |
173 $data['html'], | |
174 $data['width'], | |
175 $data['height'], | |
176 $provider, | |
177 $data['title'], | |
178 $data['author_name'], | |
179 $data['author_url'], | |
180 $data['cache_age'], | |
181 $data['thumbnail_url'], | |
182 $data['thumbnail_width'], | |
183 $data['thumbnail_height'] | |
184 ); | |
185 | |
186 default: | |
187 throw new ResourceException('Unknown resource type: ' . $data['type'], $url, $data); | |
188 } | |
189 } | |
190 catch (\InvalidArgumentException $e) { | |
191 throw new ResourceException($e->getMessage(), $url, $data, $e); | |
192 } | |
193 } | |
194 | |
195 /** | |
196 * Parses XML resource data. | |
197 * | |
198 * @param string $data | |
199 * The raw XML for the resource. | |
200 * @param string $url | |
201 * The resource URL. | |
202 * | |
203 * @return array | |
204 * The parsed resource data. | |
205 * | |
206 * @throws \Drupal\media\OEmbed\ResourceException | |
207 * If the resource data could not be parsed. | |
208 */ | |
209 protected function parseResourceXml($data, $url) { | |
210 // Enable userspace error handling. | |
211 $was_using_internal_errors = libxml_use_internal_errors(TRUE); | |
212 libxml_clear_errors(); | |
213 | |
214 $content = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA); | |
215 // Restore the previous error handling behavior. | |
216 libxml_use_internal_errors($was_using_internal_errors); | |
217 | |
218 $error = libxml_get_last_error(); | |
219 if ($error) { | |
220 libxml_clear_errors(); | |
221 throw new ResourceException($error->message, $url); | |
222 } | |
223 elseif ($content === FALSE) { | |
224 throw new ResourceException('The fetched resource could not be parsed.', $url); | |
225 } | |
226 | |
227 // Convert XML to JSON so that the parsed resource has a consistent array | |
228 // structure, regardless of any XML attributes or quirks of the XML parser. | |
229 $data = Json::encode($content); | |
230 return Json::decode($data); | |
231 } | |
232 | |
233 } |