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