getOembed($url); if ($oEmbed) { return $oEmbed; } return self::$instance->getInfoFromRemoteUrl($url); } public function getOembed($url) { $data = (new \WP_oEmbed())->get_data($url); if (!$data || is_wp_error($data) || empty($data->provider_name)) { return null; } $data = (array)$data; return array_filter([ 'title' => Arr::get($data, 'title'), 'author_name' => Arr::get($data, 'author_name'), 'type' => 'oembed', 'provider' => strtolower(Arr::get($data, 'provider_name')), 'content_type' => Arr::get($data, 'type'), 'url' => $url, 'html' => Arr::get($data, 'html'), 'image' => Arr::get($data, 'thumbnail_url'), ]); } public function getInfoFromRemoteUrl($url) { $url = untrailingslashit($url); if (empty($url)) { return new \WP_Error('rest_invalid_url', __('Invalid URL', 'fluent-community'), array('status' => 404)); } $cacheKey = 'fcom_url_details_meta_' . md5($url); $cachedReponse = wp_cache_get($cacheKey, 'fluent-community'); if ($cachedReponse) { return $cachedReponse; } $remote_url_response = $this->getRemoteBody($url); if (is_wp_error($remote_url_response) || empty($remote_url_response)) { return $remote_url_response; } $html_head = $this->getDocumentHead($remote_url_response); $title = $this->getTitle($html_head); if (!$title) { return new \WP_Error('rest_invalid_url', __('Invalid URL'), array('status' => 404)); } $meta_elements = $this->getMetaWithContentElements($html_head); $data = array_filter([ 'title' => $title, 'image' => $this->getImage($meta_elements, $url), 'description' => $this->getDescription($meta_elements), 'icon' => $this->getIcon($html_head, $url), 'type' => 'meta_data', 'url' => $url ]); wp_cache_set($cacheKey, $data, 'fluent-community', apply_filters('rest_url_details_cache_expiration', HOUR_IN_SECONDS)); return $data; } private function getRemoteBody($url) { $modified_user_agent = 'WP-URLDetails/' . get_bloginfo('version') . ' (+' . get_bloginfo('url') . ')'; $args = array( 'limit_response_size' => 300 * KB_IN_BYTES, 'user-agent' => $modified_user_agent, ); /** * Filters the HTTP request args for URL data retrieval. * * Can be used to adjust response size limit and other WP_Http::request() args. * * @param array $args Arguments used for the HTTP request. * @param string $url The attempted URL. * @since 5.9.0 * */ $args = apply_filters('rest_url_details_http_request_args', $args, $url); $response = wp_safe_remote_get($url, $args); if (\WP_Http::OK !== wp_remote_retrieve_response_code($response)) { // Not saving the error response to cache since the error might be temporary. return new \WP_Error( 'no_response', __('URL not found. Response returned a non-200 status code for this URL.'), array('status' => \WP_Http::NOT_FOUND) ); } $remote_body = wp_remote_retrieve_body($response); if (empty($remote_body)) { return new \WP_Error( 'no_content', __('Unable to retrieve body from response at this URL.'), array('status' => \WP_Http::NOT_FOUND) ); } return $remote_body; } private function getDocumentHead($html) { $head_html = $html; // Find the opening `` tag. $head_start = strpos($html, '` tag. $head_end = strpos($head_html, ''); if (false === $head_end) { // Didn't find it. Find the opening `` tag. $head_end = strpos($head_html, 'getMetadataFromMetaElement( $meta_elements, 'name', '(?:description|og:description)' ); // Bail out if description not found. if ('' === $description) { return ''; } return $this->prepare_metadata_for_output($description); } private function getImage($meta_elements, $url) { $image = $this->getMetadataFromMetaElement( $meta_elements, 'property', '(?:og:image|og:image:url)' ); // Bail out if image not found. if ('' === $image) { return ''; } // Attempt to convert relative URLs to absolute. $parsed_url = wp_parse_url($url); if (isset($parsed_url['scheme']) && isset($parsed_url['host'])) { $root_url = $parsed_url['scheme'] . '://' . $parsed_url['host'] . '/'; $image = \WP_Http::make_absolute_url($image, $root_url); } if (!$image) { return $image; } return sanitize_url(html_entity_decode($image, ENT_QUOTES | ENT_HTML5, 'UTF-8')); } private function getTitle($html) { if (!$html) { return ''; } $pattern = '#]*>(.*?)<\s*/\s*title>#is'; preg_match($pattern, $html, $match_title); if (empty($match_title[1]) || !is_string($match_title[1])) { return ''; } $title = trim($match_title[1]); return $this->prepare_metadata_for_output($title); } private function getMetaWithContentElements($html) { $pattern = '#]*' . 'content=(["\']??)(.*)\1' . '[^>]*' . '\/?>#' . 'isU'; preg_match_all($pattern, $html, $elements); return $elements; } private function prepare_metadata_for_output($metadata) { $metadata = html_entity_decode($metadata, ENT_QUOTES, get_bloginfo('charset')); $metadata = wp_strip_all_tags($metadata); return $metadata; } private function getMetadataFromMetaElement($meta_elements, $attr, $attr_value) { // Bail out if there are no meta elements. if (empty($meta_elements[0])) { return ''; } $metadata = ''; $pattern = '#' . $attr . '=([\"\']??)\s*' . $attr_value . '\s*\1' . '#isU'; foreach ($meta_elements[0] as $index => $element) { preg_match($pattern, $element, $match); if (empty($match)) { continue; } if (isset($meta_elements[2][$index]) && is_string($meta_elements[2][$index])) { $metadata = trim($meta_elements[2][$index]); } break; } return $metadata; } private function getIcon($html, $url) { // Grab the icon's link element. $pattern = '#]*rel=(?:[\"\']??)\s*(?:icon|shortcut icon|icon shortcut)\s*(?:[\"\']??)[^>]*\/?>#isU'; preg_match($pattern, $html, $element); if (empty($element[0]) || !is_string($element[0])) { return ''; } $element = trim($element[0]); // Get the icon's href value. $pattern = '#href=([\"\']??)([^\" >]*?)\\1[^>]*#isU'; preg_match($pattern, $element, $icon); if (empty($icon[2]) || !is_string($icon[2])) { return ''; } $icon = trim($icon[2]); // If the icon is a data URL, return it. $parsed_icon = wp_parse_url($icon); if (isset($parsed_icon['scheme']) && 'data' === $parsed_icon['scheme']) { return $icon; } // Attempt to convert relative URLs to absolute. if (!is_string($url) || '' === $url) { return $icon; } $parsed_url = wp_parse_url($url); if (isset($parsed_url['scheme']) && isset($parsed_url['host'])) { $root_url = $parsed_url['scheme'] . '://' . $parsed_url['host'] . '/'; $icon = \WP_Http::make_absolute_url($icon, $root_url); } return $icon; } }