PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 1.0.94
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v1.0.94
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / app / Services / RemoteUrlParser.php

RemoteUrlParser.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 1.0.94, at app/Services/RemoteUrlParser.php

330 lines 9.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\App\Services;
4
5 class RemoteUrlParser
6 {
7
8 private static $instance;
9
10 /*
11 * Create a method to call the getInfoFromRemoteUrl static method as magic method
12 */
13 public static function parse($url)
14 {
15 if (!self::$instance) {
16 self::$instance = new self();
17 }
18
19 $oEmbed = self::$instance->getOembed($url);
20
21 if ($oEmbed) {
22 return $oEmbed;
23 }
24
25 return self::$instance->getInfoFromRemoteUrl($url);
26 }
27
28 public function getOembed($url)
29 {
30 $supportedDomains = [
31 'youtube.com',
32 'youtu.be',
33 'vimeo.com',
34 'dailymotion.com',
35 'wordpress.tv',
36 'screencast.com',
37 ];
38
39
40 // Using regex to check if the $url is matched with any of the supported domains
41 $pattern = '#https?://(?:www\.)?(' . implode('|', $supportedDomains) . ')/#i';
42 if (!preg_match($pattern, $url)) {
43 return null;
44 }
45
46 $data = (new \WP_oEmbed())->get_data($url);
47
48 if (!$data || is_wp_error($data) || empty($data->provider_name)) {
49 return null;
50 }
51
52 return array_filter([
53 'title' => $data->title,
54 'author_name' => $data->author_name,
55 'type' => 'oembed',
56 'provider' => strtolower($data->provider_name),
57 'content_type' => $data->type,
58 'url' => $url,
59 'html' => $data->html,
60 'image' => $data->thumbnail_url,
61 ]);
62 }
63
64 public function getInfoFromRemoteUrl($url)
65 {
66 $url = untrailingslashit($url);
67
68 if (empty($url)) {
69 return new \WP_Error('rest_invalid_url', __('Invalid URL'), array('status' => 404));
70 }
71
72 $cacheKey = 'fcom_url_details_meta_' . md5($url);
73 $cachedReponse = wp_cache_get($cacheKey, 'fluent-community');
74
75 if ($cachedReponse) {
76 return $cachedReponse;
77 }
78
79 $remote_url_response = $this->getRemoteBody($url);
80 if (is_wp_error($remote_url_response) || empty($remote_url_response)) {
81 return $remote_url_response;
82 }
83 $html_head = $this->getDocumentHead($remote_url_response);
84
85 $title = $this->getTitle($html_head);
86 if (!$title) {
87 return new \WP_Error('rest_invalid_url', __('Invalid URL'), array('status' => 404));
88 }
89
90 $meta_elements = $this->getMetaWithContentElements($html_head);
91 $data = array_filter([
92 'title' => $title,
93 'image' => $this->getImage($meta_elements, $url),
94 'description' => $this->getDescription($meta_elements),
95 'icon' => $this->getIcon($html_head, $url),
96 'type' => 'meta_data',
97 'url' => $url
98 ]);
99
100 wp_cache_set($cacheKey, $data, 'fluent-community', apply_filters('rest_url_details_cache_expiration', HOUR_IN_SECONDS));
101
102 return $data;
103 }
104
105 private function getRemoteBody($url)
106 {
107 $modified_user_agent = 'WP-URLDetails/' . get_bloginfo('version') . ' (+' . get_bloginfo('url') . ')';
108
109 $args = array(
110 'limit_response_size' => 300 * KB_IN_BYTES,
111 'user-agent' => $modified_user_agent,
112 );
113
114 /**
115 * Filters the HTTP request args for URL data retrieval.
116 *
117 * Can be used to adjust response size limit and other WP_Http::request() args.
118 *
119 * @param array $args Arguments used for the HTTP request.
120 * @param string $url The attempted URL.
121 * @since 5.9.0
122 *
123 */
124 $args = apply_filters('rest_url_details_http_request_args', $args, $url);
125
126 $response = wp_safe_remote_get($url, $args);
127
128 if (\WP_Http::OK !== wp_remote_retrieve_response_code($response)) {
129 // Not saving the error response to cache since the error might be temporary.
130 return new \WP_Error(
131 'no_response',
132 __('URL not found. Response returned a non-200 status code for this URL.'),
133 array('status' => \WP_Http::NOT_FOUND)
134 );
135 }
136
137 $remote_body = wp_remote_retrieve_body($response);
138
139 if (empty($remote_body)) {
140 return new \WP_Error(
141 'no_content',
142 __('Unable to retrieve body from response at this URL.'),
143 array('status' => \WP_Http::NOT_FOUND)
144 );
145 }
146
147 return $remote_body;
148 }
149
150 private function getDocumentHead($html)
151 {
152 $head_html = $html;
153
154 // Find the opening `<head>` tag.
155 $head_start = strpos($html, '<head');
156 if (false === $head_start) {
157 // Didn't find it. Return the original HTML.
158 return $html;
159 }
160
161 // Find the closing `</head>` tag.
162 $head_end = strpos($head_html, '</head>');
163 if (false === $head_end) {
164 // Didn't find it. Find the opening `<body>` tag.
165 $head_end = strpos($head_html, '<body');
166
167 // Didn't find it. Return the original HTML.
168 if (false === $head_end) {
169 return $html;
170 }
171 }
172
173 // Extract the HTML from opening tag to the closing tag. Then add the closing tag.
174 $head_html = substr($head_html, $head_start, $head_end);
175 $head_html .= '</head>';
176
177 return $head_html;
178 }
179
180 private function getDescription($meta_elements)
181 {
182 // Bail out if there are no meta elements.
183 if (empty($meta_elements[0])) {
184 return '';
185 }
186
187 $description = $this->getMetadataFromMetaElement(
188 $meta_elements,
189 'name',
190 '(?:description|og:description)'
191 );
192
193 // Bail out if description not found.
194 if ('' === $description) {
195 return '';
196 }
197
198 return $this->prepare_metadata_for_output($description);
199 }
200
201 private function getImage($meta_elements, $url)
202 {
203 $image = $this->getMetadataFromMetaElement(
204 $meta_elements,
205 'property',
206 '(?:og:image|og:image:url)'
207 );
208
209 // Bail out if image not found.
210 if ('' === $image) {
211 return '';
212 }
213
214 // Attempt to convert relative URLs to absolute.
215 $parsed_url = wp_parse_url($url);
216 if (isset($parsed_url['scheme']) && isset($parsed_url['host'])) {
217 $root_url = $parsed_url['scheme'] . '://' . $parsed_url['host'] . '/';
218 $image = \WP_Http::make_absolute_url($image, $root_url);
219 }
220
221 return $image;
222 }
223
224 private function getTitle($html)
225 {
226 if (!$html) {
227 return '';
228 }
229
230 $pattern = '#<title[^>]*>(.*?)<\s*/\s*title>#is';
231 preg_match($pattern, $html, $match_title);
232
233 if (empty($match_title[1]) || !is_string($match_title[1])) {
234 return '';
235 }
236
237 $title = trim($match_title[1]);
238
239 return $this->prepare_metadata_for_output($title);
240 }
241
242 private function getMetaWithContentElements($html)
243 {
244 $pattern = '#<meta\s' .
245 '[^>]*' .
246 'content=(["\']??)(.*)\1' .
247 '[^>]*' .
248 '\/?>#' .
249 'isU';
250
251 preg_match_all($pattern, $html, $elements);
252
253 return $elements;
254 }
255
256 private function prepare_metadata_for_output($metadata)
257 {
258 $metadata = html_entity_decode($metadata, ENT_QUOTES, get_bloginfo('charset'));
259 $metadata = wp_strip_all_tags($metadata);
260 return $metadata;
261 }
262
263 private function getMetadataFromMetaElement($meta_elements, $attr, $attr_value)
264 {
265 // Bail out if there are no meta elements.
266 if (empty($meta_elements[0])) {
267 return '';
268 }
269
270 $metadata = '';
271 $pattern = '#' .
272 $attr . '=([\"\']??)\s*' . $attr_value . '\s*\1' .
273 '#isU';
274
275 foreach ($meta_elements[0] as $index => $element) {
276 preg_match($pattern, $element, $match);
277
278 if (empty($match)) {
279 continue;
280 }
281
282 if (isset($meta_elements[2][$index]) && is_string($meta_elements[2][$index])) {
283 $metadata = trim($meta_elements[2][$index]);
284 }
285
286 break;
287 }
288
289 return $metadata;
290 }
291
292 private function getIcon($html, $url)
293 {
294 // Grab the icon's link element.
295 $pattern = '#<link\s[^>]*rel=(?:[\"\']??)\s*(?:icon|shortcut icon|icon shortcut)\s*(?:[\"\']??)[^>]*\/?>#isU';
296 preg_match($pattern, $html, $element);
297 if (empty($element[0]) || !is_string($element[0])) {
298 return '';
299 }
300 $element = trim($element[0]);
301
302 // Get the icon's href value.
303 $pattern = '#href=([\"\']??)([^\" >]*?)\\1[^>]*#isU';
304 preg_match($pattern, $element, $icon);
305 if (empty($icon[2]) || !is_string($icon[2])) {
306 return '';
307 }
308 $icon = trim($icon[2]);
309
310 // If the icon is a data URL, return it.
311 $parsed_icon = wp_parse_url($icon);
312 if (isset($parsed_icon['scheme']) && 'data' === $parsed_icon['scheme']) {
313 return $icon;
314 }
315
316 // Attempt to convert relative URLs to absolute.
317 if (!is_string($url) || '' === $url) {
318 return $icon;
319 }
320
321 $parsed_url = wp_parse_url($url);
322 if (isset($parsed_url['scheme']) && isset($parsed_url['host'])) {
323 $root_url = $parsed_url['scheme'] . '://' . $parsed_url['host'] . '/';
324 $icon = \WP_Http::make_absolute_url($icon, $root_url);
325 }
326
327 return $icon;
328 }
329 }
330