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 }