Chris@17
|
1 <?php
|
Chris@17
|
2
|
Chris@17
|
3 namespace Drupal\media\OEmbed;
|
Chris@17
|
4
|
Chris@17
|
5 use Drupal\Component\Utility\Html;
|
Chris@17
|
6 use Drupal\Component\Utility\UrlHelper;
|
Chris@17
|
7 use Drupal\Core\Cache\CacheBackendInterface;
|
Chris@17
|
8 use Drupal\Core\Cache\UseCacheBackendTrait;
|
Chris@17
|
9 use Drupal\Core\Extension\ModuleHandlerInterface;
|
Chris@17
|
10 use GuzzleHttp\ClientInterface;
|
Chris@17
|
11 use GuzzleHttp\Exception\RequestException;
|
Chris@17
|
12
|
Chris@17
|
13 /**
|
Chris@17
|
14 * Converts oEmbed media URLs into endpoint-specific resource URLs.
|
Chris@17
|
15 */
|
Chris@17
|
16 class UrlResolver implements UrlResolverInterface {
|
Chris@17
|
17
|
Chris@17
|
18 use UseCacheBackendTrait;
|
Chris@17
|
19
|
Chris@17
|
20 /**
|
Chris@17
|
21 * The HTTP client.
|
Chris@17
|
22 *
|
Chris@17
|
23 * @var \GuzzleHttp\Client
|
Chris@17
|
24 */
|
Chris@17
|
25 protected $httpClient;
|
Chris@17
|
26
|
Chris@17
|
27 /**
|
Chris@17
|
28 * The OEmbed provider repository service.
|
Chris@17
|
29 *
|
Chris@17
|
30 * @var \Drupal\media\OEmbed\ProviderRepositoryInterface
|
Chris@17
|
31 */
|
Chris@17
|
32 protected $providers;
|
Chris@17
|
33
|
Chris@17
|
34 /**
|
Chris@17
|
35 * The OEmbed resource fetcher service.
|
Chris@17
|
36 *
|
Chris@17
|
37 * @var \Drupal\media\OEmbed\ResourceFetcherInterface
|
Chris@17
|
38 */
|
Chris@17
|
39 protected $resourceFetcher;
|
Chris@17
|
40
|
Chris@17
|
41 /**
|
Chris@17
|
42 * The module handler service.
|
Chris@17
|
43 *
|
Chris@17
|
44 * @var \Drupal\Core\Extension\ModuleHandlerInterface
|
Chris@17
|
45 */
|
Chris@17
|
46 protected $moduleHandler;
|
Chris@17
|
47
|
Chris@17
|
48 /**
|
Chris@17
|
49 * Static cache of discovered oEmbed resource URLs, keyed by canonical URL.
|
Chris@17
|
50 *
|
Chris@17
|
51 * A discovered resource URL is the actual endpoint URL for a specific media
|
Chris@17
|
52 * object, fetched from its canonical URL.
|
Chris@17
|
53 *
|
Chris@17
|
54 * @var string[]
|
Chris@17
|
55 */
|
Chris@17
|
56 protected $urlCache = [];
|
Chris@17
|
57
|
Chris@17
|
58 /**
|
Chris@17
|
59 * Constructs a UrlResolver object.
|
Chris@17
|
60 *
|
Chris@17
|
61 * @param \Drupal\media\OEmbed\ProviderRepositoryInterface $providers
|
Chris@17
|
62 * The oEmbed provider repository service.
|
Chris@17
|
63 * @param \Drupal\media\OEmbed\ResourceFetcherInterface $resource_fetcher
|
Chris@17
|
64 * The OEmbed resource fetcher service.
|
Chris@17
|
65 * @param \GuzzleHttp\ClientInterface $http_client
|
Chris@17
|
66 * The HTTP client.
|
Chris@17
|
67 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
Chris@17
|
68 * The module handler service.
|
Chris@17
|
69 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
Chris@17
|
70 * (optional) The cache backend.
|
Chris@17
|
71 */
|
Chris@17
|
72 public function __construct(ProviderRepositoryInterface $providers, ResourceFetcherInterface $resource_fetcher, ClientInterface $http_client, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend = NULL) {
|
Chris@17
|
73 $this->providers = $providers;
|
Chris@17
|
74 $this->resourceFetcher = $resource_fetcher;
|
Chris@17
|
75 $this->httpClient = $http_client;
|
Chris@17
|
76 $this->moduleHandler = $module_handler;
|
Chris@17
|
77 $this->cacheBackend = $cache_backend;
|
Chris@17
|
78 $this->useCaches = isset($cache_backend);
|
Chris@17
|
79 }
|
Chris@17
|
80
|
Chris@17
|
81 /**
|
Chris@17
|
82 * Runs oEmbed discovery and returns the endpoint URL if successful.
|
Chris@17
|
83 *
|
Chris@17
|
84 * @param string $url
|
Chris@17
|
85 * The resource's URL.
|
Chris@17
|
86 *
|
Chris@17
|
87 * @return string|bool
|
Chris@17
|
88 * URL of the oEmbed endpoint, or FALSE if the discovery was unsuccessful.
|
Chris@17
|
89 *
|
Chris@17
|
90 * @throws \Drupal\media\OEmbed\ResourceException
|
Chris@17
|
91 * If the resource cannot be retrieved.
|
Chris@17
|
92 */
|
Chris@17
|
93 protected function discoverResourceUrl($url) {
|
Chris@17
|
94 try {
|
Chris@17
|
95 $response = $this->httpClient->get($url);
|
Chris@17
|
96 }
|
Chris@17
|
97 catch (RequestException $e) {
|
Chris@17
|
98 throw new ResourceException('Could not fetch oEmbed resource.', $url, [], $e);
|
Chris@17
|
99 }
|
Chris@17
|
100
|
Chris@17
|
101 $document = Html::load((string) $response->getBody());
|
Chris@17
|
102 $xpath = new \DOMXpath($document);
|
Chris@17
|
103
|
Chris@17
|
104 return $this->findUrl($xpath, 'json') ?: $this->findUrl($xpath, 'xml');
|
Chris@17
|
105 }
|
Chris@17
|
106
|
Chris@17
|
107 /**
|
Chris@17
|
108 * Tries to find the oEmbed URL in a DOM.
|
Chris@17
|
109 *
|
Chris@17
|
110 * @param \DOMXPath $xpath
|
Chris@17
|
111 * Page HTML as DOMXPath.
|
Chris@17
|
112 * @param string $format
|
Chris@17
|
113 * Format of oEmbed resource. Possible values are 'json' and 'xml'.
|
Chris@17
|
114 *
|
Chris@17
|
115 * @return bool|string
|
Chris@17
|
116 * A URL to an oEmbed resource or FALSE if not found.
|
Chris@17
|
117 */
|
Chris@17
|
118 protected function findUrl(\DOMXPath $xpath, $format) {
|
Chris@17
|
119 $result = $xpath->query("//link[@type='application/$format+oembed']");
|
Chris@17
|
120 return $result->length ? $result->item(0)->getAttribute('href') : FALSE;
|
Chris@17
|
121 }
|
Chris@17
|
122
|
Chris@17
|
123 /**
|
Chris@17
|
124 * {@inheritdoc}
|
Chris@17
|
125 */
|
Chris@17
|
126 public function getProviderByUrl($url) {
|
Chris@17
|
127 // Check the URL against every scheme of every endpoint of every provider
|
Chris@17
|
128 // until we find a match.
|
Chris@17
|
129 foreach ($this->providers->getAll() as $provider_name => $provider_info) {
|
Chris@17
|
130 foreach ($provider_info->getEndpoints() as $endpoint) {
|
Chris@17
|
131 if ($endpoint->matchUrl($url)) {
|
Chris@17
|
132 return $provider_info;
|
Chris@17
|
133 }
|
Chris@17
|
134 }
|
Chris@17
|
135 }
|
Chris@17
|
136
|
Chris@17
|
137 $resource_url = $this->discoverResourceUrl($url);
|
Chris@17
|
138 if ($resource_url) {
|
Chris@17
|
139 return $this->resourceFetcher->fetchResource($resource_url)->getProvider();
|
Chris@17
|
140 }
|
Chris@17
|
141
|
Chris@17
|
142 throw new ResourceException('No matching provider found.', $url);
|
Chris@17
|
143 }
|
Chris@17
|
144
|
Chris@17
|
145 /**
|
Chris@17
|
146 * {@inheritdoc}
|
Chris@17
|
147 */
|
Chris@17
|
148 public function getResourceUrl($url, $max_width = NULL, $max_height = NULL) {
|
Chris@17
|
149 // Try to get the resource URL from the static cache.
|
Chris@17
|
150 if (isset($this->urlCache[$url])) {
|
Chris@17
|
151 return $this->urlCache[$url];
|
Chris@17
|
152 }
|
Chris@17
|
153
|
Chris@17
|
154 // Try to get the resource URL from the persistent cache.
|
Chris@17
|
155 $cache_id = "media:oembed_resource_url:$url:$max_width:$max_height";
|
Chris@17
|
156
|
Chris@17
|
157 $cached = $this->cacheGet($cache_id);
|
Chris@17
|
158 if ($cached) {
|
Chris@17
|
159 $this->urlCache[$url] = $cached->data;
|
Chris@17
|
160 return $this->urlCache[$url];
|
Chris@17
|
161 }
|
Chris@17
|
162
|
Chris@17
|
163 $provider = $this->getProviderByUrl($url);
|
Chris@17
|
164 $endpoints = $provider->getEndpoints();
|
Chris@17
|
165 $endpoint = reset($endpoints);
|
Chris@17
|
166 $resource_url = $endpoint->buildResourceUrl($url);
|
Chris@17
|
167
|
Chris@17
|
168 $parsed_url = UrlHelper::parse($resource_url);
|
Chris@17
|
169 if ($max_width) {
|
Chris@17
|
170 $parsed_url['query']['maxwidth'] = $max_width;
|
Chris@17
|
171 }
|
Chris@17
|
172 if ($max_height) {
|
Chris@17
|
173 $parsed_url['query']['maxheight'] = $max_height;
|
Chris@17
|
174 }
|
Chris@17
|
175 // Let other modules alter the resource URL, because some oEmbed providers
|
Chris@17
|
176 // provide extra parameters in the query string. For example, Instagram also
|
Chris@17
|
177 // supports the 'omitscript' parameter.
|
Chris@17
|
178 $this->moduleHandler->alter('oembed_resource_url', $parsed_url, $provider);
|
Chris@17
|
179 $resource_url = $parsed_url['path'] . '?' . UrlHelper::buildQuery($parsed_url['query']);
|
Chris@17
|
180
|
Chris@17
|
181 $this->urlCache[$url] = $resource_url;
|
Chris@17
|
182 $this->cacheSet($cache_id, $resource_url);
|
Chris@17
|
183
|
Chris@17
|
184 return $resource_url;
|
Chris@17
|
185 }
|
Chris@17
|
186
|
Chris@17
|
187 }
|