晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。   林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。   见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝)   既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。   南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。 .
Prv8 Shell
Server : Apache
System : Linux srv.rainic.com 4.18.0-553.47.1.el8_10.x86_64 #1 SMP Wed Apr 2 05:45:37 EDT 2025 x86_64
User : rainic ( 1014)
PHP Version : 7.4.33
Disable Function : exec,passthru,shell_exec,system
Directory :  /home/stando/www/wp-content/plugins/wpseo-video/post-analysis/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : //home/stando/www/wp-content/plugins/wpseo-video/post-analysis/class-analyse-post.php
<?php
/**
 * @package    Internals
 * @since      1.8.0
 * @version    1.8.0
 */

// Avoid direct calls to this file.
if ( ! class_exists( 'WPSEO_Video_Sitemap' ) ) {
	header( 'Status: 403 Forbidden' );
	header( 'HTTP/1.1 403 Forbidden' );
	exit();
}


/**
 *****************************************************************
 * Analyse post content for videos
 */
if ( ! class_exists( 'WPSEO_Video_Analyse_Post' ) ) {

	/**
	 * @package    WordPress\Plugins\Video-seo
	 * @subpackage Internals
	 * @since      1.8.0
	 * @version    1.8.0
	 *
	 * Find video content in posts/pages/cpts.
	 *
	 * @todo       [JRF -> Yoast] This currently stops at the first video (=old behaviour). Is this correct
	 * and as intended ? What about adding the potential secondary videos to the sitemap as well ?
	 * and what about wrapping them in schema.org info in the content ?
	 * This would impact the saving of metadata, how the metadata is presented to the user in the metabox
	 * as the user should be able to edit info on all videos (should they get a choice which is the main
	 * video to use for header info ? ), how the schema.org data is added and how the sitemap is generated.
	 * Adding the 'matched' string to the saved metadata would help with the schema.org data
	 * (str_replace on the correct video)
	 */
	class WPSEO_Video_Analyse_Post {

		/**
		 * Whether or not the DOM extension is enabled.
		 *
		 * @var bool
		 */
		public static $dom_enabled = false;

		/**
		 * Array of supported plugins to take into account when analysing a post.
		 *
		 * Format: key = class name suffix, value = plugin basename.
		 *
		 * {@internal Changing the order of this array will change the priority with which the plugin is treated
		 *            The current order is based on the number of plugin downloads as stated in the
		 *            WP repository per 2014-07-25.}}
		 *
		 * {@internal To add (or remove) support for a plugin:
		 *            - Create a class file in the supported-plugins folder (see other files and template for examples).
		 *            - Add the plugin to the below list.
		 *            - Add the class file to the autoload list in video-seo.php.
		 *            - Add one of more unit test file(s) for the features supported by the plugin.
		 *            - Add the plugin to travis.yml for download via git/svn.}}
		 *
		 * @var array
		 */
		public static $supported_plugins = array(
			'Jetpack'                            => 'jetpack/jetpack.php',
			'Smart_Youtube'                      => 'smart-youtube/smartyoutube.php',
			'Cincopa_Media'                      => 'video-playlist-and-gallery-plugin/wp-media-cincopa.php',
			'JW_Player'                          => 'jw-player-plugin-for-wordpress/jwplayermodule.php',
			'Youtube_Embed_Plus'                 => 'youtube-embed-plus/youtube.php',
			'Tubepress'                          => 'tubepress/tubepress.php',
			'Media_Element_Player'               => 'media-element-html5-video-and-audio-player/mediaelement-js-wp.php',
			'Youtube_Embed'                      => 'youtube-embed/youtube-embed.php',
			'Featured_Video_Plus'                => 'featured-video-plus/featured-video-plus.php',
			'FV_WordPress_Flowplayer'            => 'fv-wordpress-flowplayer/flowplayer.php',
			'WP_Youtube_Lyte'                    => 'wp-youtube-lyte/wp-youtube-lyte.php',
			'WP_Video_Lightbox'                  => 'wp-video-lightbox/wp-video-lightbox.php',
			'Advanced_Responsive_Video_Embedder' => 'advanced-responsive-video-embedder/advanced-responsive-video-embedder.php',
			'Flowplayer5'                        => 'flowplayer5/flowplayer.php',
			'Ustudio'                            => 'ustudio/plugin.php',
		);

		/**
		 * Array of active plugins - subset of the supported plugins.
		 *
		 * Format: key = class name suffix, value = object instance.
		 *
		 * @var array
		 */
		protected static $active_plugins = array();

		/**
		 * Array of supported shortcodes with their handler methods.
		 *
		 * Format: key = shortcode, value = array of handler methods, i.e. if several plugins
		 * use the same shortcode, each handler method will be used in turn.
		 *
		 * @var array
		 */
		protected static $shortcodes = array();

		/**
		 * Array of additional supported post_types with their handler methods.
		 *
		 * Format: key = post_type, value = array of handler methods, i.e. if several plugins
		 * use the same post_type, each handler method will be used in turn.
		 *
		 * @var array
		 */
		protected static $post_types = array();

		/**
		 * Array of additional supported custom post meta fields with their handler methods.
		 *
		 * Format: key = meta_key, value = array of handler methods, i.e. if several plugins
		 * use the same meta_key, each handler method will be used in turn.
		 *
		 * @var array
		 */
		protected static $meta_keys = array();

		/**
		 * Array of alternative protocol schemes to take into account.
		 *
		 * @var array
		 */
		protected static $alt_protocols = array();

		/**
		 * Array of video embeds to take into account.
		 *
		 * @var array
		 */
		protected static $video_autoembeds = array();

		/**
		 * Array of video OEmbeds to take into account.
		 *
		 * @var array
		 */
		protected static $video_oembeds = array();

		/**
		 * The options set for this plugin.
		 *
		 * @var array
		 */
		protected $options = array();

		/**
		 * The video info array.
		 *
		 * @var array
		 */
		protected $vid = array(
			'id'   => null,
			'type' => null,
			'url'  => null,
		);

		/**
		 * The video array with all the data of the previous "fetch", if available.
		 *
		 * @var array
		 */
		protected $old_vid = array();

		/**
		 * The content of the post to analyse.
		 *
		 * @var string
		 */
		protected $content = '';

		/**
		 * The post object for the post to analyse.
		 *
		 * @var object
		 */
		protected $post;

		/**
		 * Use embedly as a fall back method for video detail retrieval?
		 *
		 * @var bool
		 */
		protected static $use_embedly;

		/**
		 * Initialize the class
		 *
		 * @param string $content The content to parse for videos.
		 * @param array  $vid     The video array to update.
		 * @param array  $old_vid The former video array.
		 * @param mixed  $post    The post object or the post id of the post to analyse.
		 *
		 * @return \WPSEO_Video_Analyse_Post
		 */
		public function __construct( $content, $vid, $old_vid = array(), $post = null ) {
			// Set the base properties and deal with alternative protocols.
			$this->options = get_option( 'wpseo_video' );
			$this->vid     = array_merge( $this->vid, $vid );
			$this->old_vid = $old_vid;
			$content       = apply_filters( 'wpseo_video_index_content', $content, $this->vid );

			// Deal with alternative protocols.
			if ( in_array( 'youtube::', self::$alt_protocols, true ) ) {
				// Very specific to the YouTube Embed plugin - maybe should be moved to plugin methods.
				$content = str_replace( 'youtube::', 'http://www.youtube.com/watch?v=', $content );
			}
			$this->content = str_replace( self::$alt_protocols, 'http://', $content );

			// Set up post object if needed.
			if ( ! empty( $post ) && ! is_object( $post ) ) {
				$post = get_post( $post );
			}

			if ( is_object( $post ) ) {
				$this->post = $post;
			}
			elseif ( ! empty( $GLOBALS['post'] ) ) {
				// Default to the current post - @todo probably wrong as it might be a term which is being analysed.
				$this->post = $GLOBALS['post'];
			}

			// Can we use Embedly?
			if ( ! isset( self::$use_embedly ) ) {
				// Check if we have an Embedly api key.
				if ( isset( $this->options['embedly_api_key'] ) && $this->options['embedly_api_key'] !== '' ) {
					self::$use_embedly = true;
				}
				else {
					self::$use_embedly = false;
				}
			}

			$this->analyse();
		}

		/**
		 * Reset statics
		 */
		public static function reset_statics() {
			self::$dom_enabled      = false;
			self::$active_plugins   = array();
			self::$shortcodes       = array();
			self::$post_types       = array();
			self::$meta_keys        = array();
			self::$alt_protocols    = array();
			self::$video_autoembeds = array();
			self::$video_oembeds    = array();
		}

		/**
		 * Set statics
		 */
		public static function set_statics() {
			// Reset just in case this method is called more than once (like when we're testing).
			self::reset_statics();

			if ( extension_loaded( 'dom' ) && class_exists( 'domxpath' ) ) {
				self::$dom_enabled = true;
			}

			/**
			 * Add WP core to 'active plugins' as the first (highest prio) item
			 * Add this plugin as the second
			 */
			self::$active_plugins['wp_core']  = new WPSEO_Video_Support_Core();
			self::$active_plugins['videoseo'] = new WPSEO_Video_Plugin_Yoast_Videoseo();

			/*
			 * @todo It might be better if we can figure out if the plugin is installed rather than active
			 * Also - this will give issues with plugins in the mu-plugins directory as is_plugin_active()
			 * incorrectly returns false (should be fixed in WP4 ?)
			 */
			if ( ! function_exists( 'is_plugin_active' ) ) {
				require_once ABSPATH . 'wp-admin/includes/plugin.php';
			}
			foreach ( self::$supported_plugins as $name => $plugin_basenames ) {
				$plugin_basenames = (array) $plugin_basenames;
				foreach ( $plugin_basenames as $plugin_basename ) {
					if ( is_plugin_active( $plugin_basename ) ) {
						$classname                     = 'WPSEO_Video_Plugin_' . $name;
						self::$active_plugins[ $name ] = new $classname();
						break;
					}
				}
			}
			unset( $name, $plugin_basenames, $plugin_basename, $classname );

			// Add the plugin features for all active plugins.
			if ( is_array( self::$active_plugins ) && self::$active_plugins !== array() ) {
				foreach ( self::$active_plugins as $name => $instance ) {

					// Add known shortcodes.
					$shortcodes = $instance->get_shortcodes();
					if ( is_array( $shortcodes ) && $shortcodes !== array() ) {
						foreach ( $shortcodes as $sc ) {
							self::$shortcodes[ $sc ][] = array( $instance, 'get_info_from_shortcode' );
						}
					}
					unset( $shortcodes, $sc );

					// Add known additional post_types.
					$post_types = $instance->get_post_types();
					if ( is_array( $post_types ) && $post_types !== array() ) {
						foreach ( $post_types as $pt ) {
							self::$post_types[ $pt ][] = array( $instance, 'get_info_for_post_type' );
						}
					}
					unset( $post_types, $pt );

					// Add known additional meta_keys.
					$meta_keys = $instance->get_meta_keys();
					if ( is_array( $meta_keys ) && $meta_keys !== array() ) {
						foreach ( $meta_keys as $key ) {
							self::$meta_keys[ $key ][] = array( $instance, 'get_info_from_post_meta' );
						}
					}
					unset( $meta_keys, $key );


					// Add alternative protocols.
					$alt_protocols = $instance->get_alt_protocols();
					if ( is_array( $alt_protocols ) && $alt_protocols !== array() ) {
						self::$alt_protocols = array_unique( array_merge( self::$alt_protocols, $alt_protocols ) );
					}
					unset( $alt_protocols );


					// Add autoembed information.
					$video_autoembeds = $instance->get_video_autoembeds();
					if ( is_array( $video_autoembeds ) && $video_autoembeds !== array() ) {
						/*
						 * {@internal Merge order reversed, if there is a handler name conflict between plugins,
						 * defer to the more popular plugin which will have been added first.}}
						 */
						self::$video_autoembeds = array_unique( array_merge( $video_autoembeds, self::$video_autoembeds ) );
					}
					unset( $video_autoembeds );


					// Add oembed information.
					$video_oembeds = $instance->get_video_oembeds();
					if ( is_array( $video_oembeds ) && $video_oembeds !== array() ) {
						/*
						 * {@internal Merge order reversed, if there is a handler name conflict between plugins,
						 * defer to the more popular plugin which will have been added first.}}
						 */
						self::$video_oembeds = array_unique( array_merge( $video_oembeds, self::$video_oembeds ) );
					}
					unset( $video_oembeds );

				}
				unset( $name, $instance );
			}
		}

		/**
		 * Get the video info
		 *
		 * @return array|string        Return video array or 'none'
		 */
		public function get_vid_info() {
			if ( isset( $this->vid['content_loc'] ) || isset( $this->vid['player_loc'] ) ) {
				$this->normalize_values();
				$vid = apply_filters( 'wpseo_video_' . $this->vid['type'] . '_details', $this->vid );

				return array_filter( $vid );
			}
			else {
				return 'none';
			}
		}

		/**
		 * Make sure all duration, view_count, height and width values are integers
		 */
		protected function normalize_values() {
			$keys = array(
				'duration',
				'height',
				'view_count',
				'width',
			);
			foreach ( $keys as $key ) {
				if ( isset( $this->vid[ $key ] ) ) {
					$this->vid[ $key ] = absint( round( $this->vid[ $key ] ) );
				}
			}
		}

		/**
		 * Analyse the post for video content
		 */
		protected function analyse() {

			$methods = array(
				'get_video_from_post_type',
				'get_video_from_post_meta',
				'get_video_from_attachment',
				'get_video_from_shortcode',
				'get_video_from_auto_embeds',

				'get_video_through_old_methods',
			);

			foreach ( $methods as $method ) {
				if ( is_callable( array( $this, $method ) ) ) {
					$vid = call_user_func( array( $this, $method ) );

					// Check for video.
					if ( $vid !== array() && ( isset( $vid['content_loc'] ) || isset( $vid['player_loc'] ) ) ) {
						$this->vid = array_merge( $this->vid, $vid );
						break;
					}
				}
			}
		}

		/**
		 * Check if the current post type is a video post type and if we can find usable info through it
		 */
		protected function get_video_from_post_type() {
			$vid = array();

			if ( ( is_array( self::$post_types ) && self::$post_types !== array() ) && ( is_object( $this->post ) && isset( self::$post_types[ $this->post->post_type ] ) ) ) {
				foreach ( self::$post_types[ $this->post->post_type ] as $function ) {
					if ( is_callable( $function ) ) {
						$vid = call_user_func( $function, $this->post->ID, $this->post->post_type, $this->post );
						if ( is_array( $vid ) && $vid !== array() ) {
							$vid = $this->get_video_details( $vid );
							if ( ! empty( $vid['player_loc'] ) || ! empty( $vid['content_loc'] ) ) {
								// Stop on the first function which delivers results.
								break;
							}
							else {
								// Reset $vid if no usable info was found.
								$vid = array();
							}
						}
					}
				}
			}

			return $vid;
		}

		/**
		 * Check if any custom fields are video fields and if they contain usable info
		 */
		protected function get_video_from_post_meta() {
			$vid = array();

			if ( is_array( self::$meta_keys ) && self::$meta_keys !== array() && ! empty( $this->post->ID ) ) {

				foreach ( self::$meta_keys as $key => $callables ) {
					$meta_values = $this->get_normalized_meta_values( $key, $this->post->ID );

					if ( is_array( $meta_values ) && $meta_values !== array() ) {
						foreach ( $meta_values as $single_meta_value ) {

							foreach ( $callables as $function ) {
								if ( is_callable( $function ) ) {

									$vid = call_user_func( $function, $single_meta_value, $key, $this->post->ID );

									if ( is_array( $vid ) && $vid !== array() ) {
										$vid = $this->get_video_details( $vid );
										if ( ! empty( $vid['player_loc'] ) || ! empty( $vid['content_loc'] ) ) {
											// Stop on the first function which deliveres results.
											unset( $vid['__add_to_content'] );
											break 3;
										}
										elseif ( ! empty( $vid['__add_to_content'] ) ) {
											$this->content = $vid['__add_to_content'] . "\n" . $this->content;
											$vid           = array();
										}
										else {
											// Reset $vid if no usable info was found.
											$vid = array();
										}
									}
								}
							}
						}
					}
				}
			}

			return $vid;
		}

		/**
		 * Get post meta values to analyse for video content
		 *
		 * @param  string $key     Meta key to get the values for.
		 * @param  int    $post_id Post to get the values for.
		 *
		 * @return array  Single dimensional array with already entity normalized potentially usable meta values.
		 */
		protected function get_normalized_meta_values( $key, $post_id ) {

			$real_values = array();
			$meta_values = get_post_custom_values( $key, $post_id );

			if ( is_array( $meta_values ) && $meta_values !== array() ) {
				foreach ( $meta_values as $meta_value ) {
					$meta_value = maybe_unserialize( $meta_value );

					if ( is_scalar( $meta_value ) && ! empty( $meta_value ) ) {
						$real_values[] = $meta_value;
					}
					elseif ( is_array( $meta_value ) && $meta_value !== array() ) {
						foreach ( $meta_value as $value ) {
							if ( is_scalar( $value ) && ! empty( $value ) ) {
								$real_values[] = $value;
							}
							elseif ( is_array( $value ) && ! empty( $value[0] ) && is_scalar( $value[0] ) ) {
								/*
								 * Ignore deeper meta values which are multi-dim arrays as we really
								 * don't know what we need from it them
								 */
								$real_values[] = $value[0];
							}
						}
					}
				}
			}
			unset( $meta_values, $meta_value, $value );

			/*
			 * Silly, silly themes _encode_ the value of the post meta field. Yeah it's ridiculous.
			 * But this fixes it.
			 *
			 * ^ Helpful comment, thanks!
			 */
			$real_values = array_map( array( $this, 'normalize_entities' ), $real_values );
			$real_values = array_map( 'trim', $real_values );

			// Remove empties.
			$real_values = array_filter( $real_values );

			return $real_values;
		}

		/**
		 * Get all video attachments and see if we can find one we can use
		 */
		protected function get_video_from_attachment() {
			$vid = array();

			if ( ! empty( $this->post->ID ) ) {
				$media = get_attached_media( 'video', $this->post->ID );
				if ( is_array( $media ) && $media !== array() ) {
					foreach ( $media as $video ) {
						$vid['type']          = 'localfile';
						$vid['maybe_local']   = true;
						$vid['attachment_id'] = $video->ID;
						$vid['url']           = $video->guid;

						$vid = $this->get_video_details( $vid );
						if ( ! empty( $vid['player_loc'] ) || ! empty( $vid['content_loc'] ) ) {
							// Stop on the first video which delivers results (i.e. has a usable extension).
							break;
						}
						else {
							// Reset $vid if no usable info was found.
							$vid = array();
						}
					}
				}
			}

			return $vid;
		}

		/**
		 * Get all the shortcodes, check for any video shortcodes and see if we can parse them to useable info
		 */
		protected function get_video_from_shortcode() {
			$vid = array();

			if ( false !== strpos( $this->content, '[' ) && is_array( self::$shortcodes ) && self::$shortcodes !== array() ) {

				$old_shortcode_tags        = $GLOBALS['shortcode_tags'];
				$GLOBALS['shortcode_tags'] = self::$shortcodes; // WPCS: override ok.

				/**
				 * 1 - An extra [ to allow for escaping shortcodes with double [[]]
				 * 2 - The shortcode name
				 * 3 - The shortcode argument list
				 * 4 - The self closing /
				 * 5 - The content of a shortcode when it wraps some content.
				 * 6 - An extra ] to allow for escaping shortcodes with double [[]]
				 */
				if ( preg_match_all( '/' . get_shortcode_regex() . '/s', $this->content, $matches, PREG_SET_ORDER ) ) {

					foreach ( $matches as $match ) {
						// No need to do anything is it's an escaped shortcode.
						if ( $match[1] !== '[' && $match[6] !== ']' ) {
							$full       = $match[0];
							$tag        = trim( $match[2] );
							$sc_content = $match[5];
							$atts       = shortcode_parse_atts( $match[3] );

							// Handle WordPress.com shortcode format.
							if ( isset( $atts[0] ) && $sc_content === '' ) {
								$atts       = $this->fix_sc_attributes( $atts );
								$sc_content = trim( $atts[0] );
								unset( $atts[0] );
							}

							$sc_content = $this->normalize_entities( $sc_content );
							if ( is_array( $atts ) && $atts !== array() ) {
								$atts = array_map( array( $this, 'normalize_entities' ), $atts );
							}

							$thumb = '';
							if ( isset( $atts['image'] ) && ( is_string( $atts['image'] ) && $atts['images'] !== '' ) ) {
								$thumb = $atts['image'];
							}

							foreach ( self::$shortcodes[ $tag ] as $function ) {
								if ( is_callable( $function ) ) {
									$vid = call_user_func( $function, $full, $tag, $atts, $sc_content );
									if ( is_array( $vid ) && $vid !== array() ) {
										if ( ! isset( $vid['thumbnail_loc'] ) && $thumb !== '' ) {
											$vid['thumbnail_loc'] = $thumb;
										}

										$vid = $this->get_video_details( $vid );
										if ( ! empty( $vid['player_loc'] ) || ! empty( $vid['content_loc'] ) ) {
											// Stop on the first function which delivers results.
											break 2;
										}
										elseif ( ! empty( $vid['__add_to_content'] ) ) {
											$this->content = $vid['__add_to_content'] . "\n" . $this->content;
											$vid           = array();
										}
										else {
											// Reset $vid if no usable info was found.
											$vid = array();
										}
									}
								}
							}
						}
					}
				}

				$GLOBALS['shortcode_tags'] = $old_shortcode_tags; // WPCS: override ok.
			}

			return $vid;
		}

		/**
		 * Grab all urls which are on their own line and check if any are registered video urls
		 * and if so, grab usable info
		 */
		protected function get_video_from_auto_embeds() {
			$vid = array();

			/*
			 * Get all the embeddable urls
			 * Use the same regex as in WP_Embed::autoembed()
			 */
			if ( preg_match_all( '`^(?:\s*)(https?://[^\s<>"]+)(?:\s*)$`im', $this->content, $matches, PREG_PATTERN_ORDER ) ) {
				// Only interested in the real url matches.
				$urls = $matches[1];

				foreach ( $urls as $url ) {
					// Follow WP.
					$url = str_replace( '&amp;', '&', $url );

					if ( ! empty( $GLOBALS['wp_embed']->handlers ) && is_array( $GLOBALS['wp_embed']->handlers ) ) {
						// Go through the embed handlers.
						foreach ( $GLOBALS['wp_embed']->handlers as $handler_array ) {
							foreach ( $handler_array as $name => $details ) {
								if ( isset( self::$video_autoembeds[ $name ] ) && preg_match( $details['regex'], $url ) ) {
									$vid['url'] = $url;
									if ( self::$video_autoembeds[ $name ] !== '' ) {
										$vid['type'] = self::$video_autoembeds[ $name ];
									}

									$vid = $this->get_video_details( $vid );
									if ( ! empty( $vid['player_loc'] ) || ! empty( $vid['content_loc'] ) ) {
										// Stop on the first function which delivers results.
										break 3;
									}
									else {
										// Reset $vid if no usable info was found.
										$vid = array();
									}
								}
							}
							unset( $name, $details );
						}
						unset( $handler_array );
					}

					/*
					 * Go through the Oembed handlers
					 */
					$oembed      = _wp_oembed_get_object();
					$providerurl = $oembed->get_provider( $url, array( 'discover' => false ) );
					if ( ( is_string( $providerurl ) && $providerurl !== '' ) && ! empty( self::$video_oembeds ) ) {
						foreach ( self::$video_oembeds as $partial_url => $service ) {
							if ( stripos( $providerurl, $partial_url ) !== false ) {
								$vid['url'] = $url;
								if ( $service !== '' ) {
									$vid['type'] = $service;
								}

								$vid = $this->get_video_details( $vid );
								if ( ! empty( $vid['player_loc'] ) || ! empty( $vid['content_loc'] ) ) {
									// Stop on the first function which delivers results.
									break 2;
								}
								else {
									// Reset $vid if no usable info was found.
									$vid = array();
								}
							}
						}
					}
				}
			}

			return $vid;
		}

		/**
		 * Parse the content of a post or term description.
		 *
		 * {@internal Stripped version of the old function.}}
		 *
		 * @since    1.3
		 *
		 * @return array $vid
		 */
		protected function get_video_through_old_methods() {
			$content = $this->content;
			$vid     = array();

			if ( preg_match( '`(<video.*</video>)`s', $content, $html5vid ) ) {

				if ( preg_match( '`src=([\'"])(.*?)\.(' . WPSEO_Video_Sitemap::$video_ext_pattern . ')\1`', $html5vid[1], $content_loc ) ) {
					$vid['content_loc'] = $content_loc[2] . '.' . $content_loc[3];
					$vid['maybe_local'] = true;

					if ( preg_match( '`poster=([\'"])([^\'"\s]+)\1`', $html5vid[1], $thumbnail_loc ) ) {
						$vid['thumbnail_loc'] = $thumbnail_loc[2];
					}

					$vid['type'] = 'html5vid';

					return $this->get_video_details( $vid );
				}
			}

			$vid = $this->get_wistia_video_through_old_methods( $content );

			if ( isset( $vid['content_loc'] ) || isset( $vid['player_loc'] ) ) {
				return $vid;
			}
			else {
				// Reset vid.
				$vid = array();
			}


			$oembed = $this->grab_embeddable_urls_xpath( $content );
			if ( is_array( $oembed ) && $oembed !== array() ) {

				foreach ( $oembed as $url ) {
					$vid['url'] = $url;
					$vid        = $this->get_video_details( $vid );

					if ( isset( $vid['content_loc'] ) || isset( $vid['player_loc'] ) ) {
						return $vid;
					}
					else {
						// Reset vid.
						$vid = array();
					}
				}
			}
			unset( $oembed );


			$oembed = $this->grab_embeddable_urls( $content );
			if ( is_array( $oembed ) && $oembed !== array() ) {

				foreach ( $oembed as $url ) {
					$vid['url'] = $url;
					$vid        = $this->get_video_details( $vid );

					if ( isset( $vid['content_loc'] ) || isset( $vid['player_loc'] ) ) {
						return $vid;
					}
					else {
						// Reset vid.
						$vid = array();
					}
				}
			}
			unset( $oembed );


			return $vid;
		}

		/**
		 * Analyse post content for typical Wistia embed codes.
		 *
		 * @link https://wistia.com/doc/embedding
		 *
		 * @since 3.9
		 *
		 * @param string $content Post content.
		 *
		 * @return array Video info array or empty array if no wistia video was matched.
		 */
		protected function get_wistia_video_through_old_methods( $content ) {
			$vid = array();

			if ( preg_match( '`<(?:div|span)(?: [a-z]+=\S+)* class=(?:[\'"])wistia_embed wistia_async_([^\'"\s]+)`', $content, $matches ) ) {
				$vid['id']   = $matches[1];
				$vid['type'] = 'wistia';
				$vid         = $this->get_video_details( $vid );
			}
			elseif ( preg_match( '`<div id=([\'"])wistia_([^\'"\s]+)\1 class=([\'"])wistia_embed[^\'"]*\3`', $content, $matches ) ) {
				$vid['id']   = $matches[2];
				$vid['type'] = 'wistia';
				$vid         = $this->get_video_details( $vid );
			}
			elseif ( preg_match( '`<a(?:.*?)href="(?:http[s]?:)?//fast\.wistia\.(?:com|net)/embed/iframe/([^\?]+)\?`', $content, $matches ) ) {
				$vid['id']   = $matches[1];
				$vid['type'] = 'wistia';
				$vid         = $this->get_video_details( $vid );
			}

			return $vid;
		}

		/**
		 * Checks whether there are oembed URLs in the post that should be included in the video sitemap.
		 *
		 * {@internal Look at WP native function `get_media_embedded_in_content( $content, $types = null )`.}}
		 *
		 * @since    0.1
		 *
		 * @param string $content the content of the post.
		 *
		 * @return array|boolean returns array $urls with type of video as array key and video URL as content, or false on negative
		 */
		protected function grab_embeddable_urls( $content ) {
			$options      = $this->options;
			$evs_location = get_option( 'evs_location' );

			// Catch both the single line embeds as well as the embeds using the [embed] shortcode.
			preg_match_all( '`\[embed(?:[^\]]+)?\](http[s]?://[^\s"]+)\s*\[/embed\]`im', $content, $matches );
			preg_match_all( '`^\s*(?:<p>)?(http[s]?://[^\s"]+)\s*$`im', $content, $matches2 );

			$matched_urls = array();
			if ( isset( $matches[1] ) && is_array( $matches[1] ) ) {
				$matched_urls = array_merge( $matched_urls, $matches[1] );
			}
			if ( isset( $matches2[1] ) && is_array( $matches2[1] ) ) {
				$matched_urls = array_merge( $matched_urls, $matches2[1] );
			}

			if ( preg_match_all( '`(<iframe.*</iframe>)`s', $content, $iframes, PREG_SET_ORDER ) ) {
				foreach ( $iframes as $iframe ) {
					if ( preg_match( '`src=([\'"])([^\s\'"]+)\1`', $iframe[1], $iframesrc ) ) {
						$matched_urls[] = $iframesrc[2];
					}
				}
			}

			if ( preg_match_all( '`(<object.*</object>)`s', $content, $objects, PREG_SET_ORDER ) ) {
				foreach ( $objects as $object ) {
					if ( preg_match( '`<param name=([\'"])src\1 value=([\'"])([^\s\'"]+)\2`', $object[1], $srcmatch ) ) {
						$matched_urls[] = $srcmatch[3];
					}
					elseif ( preg_match( '`<param name=([\'"])movie\1 value=([\'"])([^\s\'"]+)\2`', $object[1], $moviematch ) ) {
						$matched_urls[] = $moviematch[3];
					}
				}
			}

			if ( preg_match( '`<a href=([\'"])(http[s]?://(?:www\.)?(?:youtube|vimeo)\.com/[^\s\'"]*)\1 rel=([\'"])wp-video-lightbox\3`', $content, $matches ) ) {
				$matched_urls[] = $matches[2];
			}

			if ( preg_match( '`<a class=([\'"])youtubepop\1 href=([\'"])(http[s]?://(?:www\.)?(?:youtube|vimeo)\.com/[^\s\'"]*)\2>`', $content, $matches ) ) {
				$matched_urls[] = $matches[3];
			}

			$wistia_info = array( 'domain' => 'wistia.com' );
			if ( $options['wistia_domain'] !== '' ) {
				$wistia_info = $this->parse_url( $options['wistia_domain'] );
			}

			$evs_info = $this->parse_url( $evs_location );
			if ( ! isset( $evs_info ) || ! isset( $evs_info['domain'] ) ) {
				$evs_info = array( 'domain' => 'easyvideosuite.com' );
			}


			if ( count( $matched_urls ) > 0 ) {
				$urls = array();

				foreach ( $matched_urls as $match ) {
					$url_info = $this->parse_url( $match );
					if ( ! isset( $url_info['domain'] ) ) {
						continue;
					}

					switch ( $url_info['domain'] ) {
						case 'brightcove.com':
							if ( preg_match( '`<param name="flashVars" value="playerID=(\d+)`', $content, $bcmatch ) ) {
								$urls['brightcove'] = $bcmatch[1];
							}

							if ( preg_match( '`<param name="flashVars" value="videoId=(\d+)`', $content, $bcmatch ) ) {
								$urls[] = $bcmatch[1];
							}
							break;

						case '23video.com':
						case 'animoto.com':
						case 'video214.com':
						case 'archive.org':
						case 'blip.tv':
						case 'blip.com':
						case 'cincopa.com':
						case 'collegehumor.com':
						case 'dailymotion.com':
						case 'dai.ly':
						case 'easyvideosuite.com':
						case 'embed.ly':
						case 'embedly.com':
						case 'flickr.com':
						case 'flic.kr':
						case 'ifilm.com':
						case 'funnyordie.com':
						case 'hulu.com':
						case 'metacafe.com':
						case 'muzu.tv':
						case 'revision3.com':
						case 'screencast.com':
						case 'screenr.com':
						case 'snotr.com':
						case 'spike.com':
						case 'ted.com':
						case 'ted.org':
						case 'ustudio.com':
						case 'veoh.com':
						case 'viddler.com':
						case 'videojug.com':
						case 'vidyard.com':
						case 'vimeo.com':
						case 'vine.co':
						case $wistia_info['domain']:
						case 'wistia.com':
						case 'wistia.net':
						case 'wi.st':
						case 'wordpress.tv':
						case 'youtu.be':
						case 'youtube.com':
						case 'youtube-nocookie.com':
						case $evs_info['domain']:
							$urls[] = $match;
							break;
					}
				}

				if ( count( $urls ) > 0 ) {
					return $urls;
				}
				else {
					return false;
				}
			}
			else {
				return false;
			}
		}

		/**
		 * Checks whether there are oembed URLs in the post that should be included in the video sitemap.
		 * Uses DOMDocument and XPath to parse the content for urls instead of preg matches.
		 *
		 * @since 1.5.4.4
		 *
		 * @param string $content the content of the post.
		 *
		 * @return array|boolean returns array $urls with type of video as array key and video URL as content, or false on negative
		 */
		protected function grab_embeddable_urls_xpath( $content ) {
			if ( ( ! is_string( $content ) || trim( $content ) === '' ) || self::$dom_enabled === false ) {
				return false;
			}

			$dom = new DOMDocument();
			@$dom->loadHTML( $content );
			$xpath = new DOMXPath( $dom );

			$matched_urls = array();

			// For object embeds (i.e screencast.com).
			$objects = $xpath->query( '//object/param[@name="movie"] | //object/param[@name="src"]' );
			if ( is_object( $objects ) && $objects->length > 0 ) {
				foreach ( $objects as $object ) {
					$value          = $object->getAttribute( 'value' );
					$matched_urls[] = $value;
				}
			}
			unset( $objects, $object, $value );

			// For iframe embeds (i.e. vidyard.com).
			$iframes = $xpath->query( '//iframe' );
			if ( is_object( $iframes ) && $iframes->length > 0 ) {
				foreach ( $iframes as $iframe ) {
					$src            = $iframe->getAttribute( 'src' );
					$matched_urls[] = $src;
				}
			}
			unset( $iframes, $iframe, $src );

			// Specific check for vidyard embed with javascript and lightbox.
			$script = $xpath->query( '//script[contains(@src,"play.vidyard.com")]' );
			if ( is_object( $script ) && $script->length > 0 ) {
				foreach ( $script as $element ) {
					$src            = $element->getAttribute( 'src' );
					$matched_urls[] = $src;
				}
			}
			unset( $script, $element, $src );

			// Specific check for cincopa embed via javascript.
			$script = $xpath->query( '//script/text()[contains(.,"cp_load_widget")]' );
			if ( is_object( $script ) && $script->length > 0 ) {
				foreach ( $script as $element ) {
					// Remove CDATA.
					$src            = preg_replace( '`//\s*?<!\[CDATA\[\s*|\s*//\s*\]\]>`', '', $element->wholeText );
					$src            = 'http://cincopa.com?' . $src;
					$matched_urls[] = $src;
				}
			}
			unset( $script, $element, $src );

			// Specific check for brightcove.
			$script = $xpath->query( '//object/param[contains(@value,"brightcove.com")]/following-sibling::param[@name="flashVars"]' );
			if ( is_object( $script ) && $script->length > 0 ) {
				foreach ( $script as $element ) {
					$src            = $element->getAttribute( 'value' );
					$src            = 'http://brightcove.com?' . $src;
					$matched_urls[] = $src;
				}
			}
			unset( $script, $element, $src );

			// Specific check for screenr.
			$script = $xpath->query( '//object/param[contains(@value,"screenr.com")]/following-sibling::param[@name="flashvars"]' );
			if ( is_object( $script ) && $script->length > 0 ) {
				foreach ( $script as $element ) {
					$flashvars = $element->getAttribute( 'value' );
					if ( preg_match( '`<iframe src=(&quot;|["\'])(.+?)\1`', $flashvars, $match ) ) {
						$matched_urls[] = $match[2];
					}
				}
			}
			unset( $script, $element, $flashvars );

			// Specific check for veoh.
			$script = $xpath->query( '//object[@name="veohFlashPlayer"]/param[@name="movie"]' );
			if ( is_object( $script ) && $script->length > 0 ) {
				foreach ( $script as $element ) {
					$value = $element->getAttribute( 'value' );
					$value = self::wp_parse_url( $value );
					parse_str( $value['query'], $query );
					if ( ! empty( $query['permalinkId'] ) ) {
						$matched_urls[] = 'http://www.veoh.com/watch/' . $query['permalinkId'];
					}
				}
			}
			unset( $script, $element, $value, $query );

			// Specific check for 23video.
			$link = $xpath->query( '//a[contains(@href,"23video.com")]/img[contains(@src,"23video.com")]/following-sibling::div/..' );
			if ( is_object( $link ) && $link->length > 0 ) {
				foreach ( $link as $element ) {
					$matched_urls[] = $element->getAttribute( 'href' );
				}
			}
			unset( $link, $element );

			if ( count( $matched_urls ) > 0 ) {
				$urls = array();

				foreach ( $matched_urls as $match ) {
					$url_info = $this->parse_url( $match );
					if ( ! isset( $url_info['domain'] ) ) {
						continue;
					}

					switch ( $url_info['domain'] ) {
						/*
						 * work around for screencast.com b/c there's no connection between url and the embed code
						 * @todo JRF -> this ought to be changed so that the $vid['url'] value is a normal value and we add another key for passing the content
						 */
						case 'screencast.com':
							$urls['screencast']['url']   = $match;
							$urls['screencast']['embed'] = $content;
							break;

						case '23video.com':
						case 'animoto.com':
						case 'video214.com':
						case 'brightcove.com':
						case 'cincopa.com':
						case 'screenr.com':
						case 'veoh.com':
						case 'vidyard.com':
							$urls[] = $match;
							break;
					}
				}

				if ( count( $urls ) > 0 ) {
					return $urls;
				}
				else {
					return false;
				}
			}
			else {
				return false;
			}
		}

		/**
		 * Retrieve video details
		 *
		 * @since      1.7.0
		 * @see        WPSEO_Video_Details
		 *
		 * @param  array $vid A potential video array with all the data.
		 *
		 * @todo       - Should be changed to visibility protected, but that would cause fatal
		 * errors with the fall-back for deprecated methods. Let's wait a few versions.
		 *
		 * @return array $vid
		 */
		public function get_video_details( $vid ) {
			$vid = $this->verify_service_type( $vid );

			// Make sure we don't lose an updated title, description or publication date.
			$vid = array_merge( $this->vid, $vid );

			$class = 'unknown';
			if ( isset( $vid['type'] ) ) {
				$class = 'WPSEO_Video_Details_' . ucfirst( $vid['type'] );
			}

			if ( class_exists( $class ) ) {
				$video = new $class( $vid, $this->old_vid );
				$vid   = $video->get_details();
			}
			elseif ( isset( $vid['maybe_local'] ) && $vid['maybe_local'] === true ) {
				$video = new WPSEO_Video_Details_Localfile( $vid, $this->old_vid );
				$vid   = $video->get_details();
			}

			// Alternatively try to get details via embedly.
			if ( ( empty( $vid['content_loc'] ) && empty( $vid['player_loc'] ) ) && ( self::$use_embedly === true && WPSEO_Video_Details_Embedly::$functional !== false ) && ( ! empty( $vid['url'] ) ) ) {
				$video = new WPSEO_Video_Details_Embedly( $vid, $this->old_vid );
				$vid   = $video->get_details();

			}

			return $vid;
		}

		/**
		 * Verify the service type based on the url - work around user error and shortcodes without type indication.
		 * Also: check if the url might be local to use the local detail retrieval as fall back.
		 *
		 * @todo Add Videopress domains to the switches
		 *
		 * @param  array $vid A potential video array with all the data.
		 *
		 * @return array $vid
		 */
		protected function verify_service_type( $vid ) {
			static $site_host;
			static $site_domain;
			static $network_host;
			static $network_domain;
			static $wistia_host;
			static $evs_domain;

			$type = '';

			// Get the url we're going to use.
			$url = '';
			if ( isset( $vid['url'] ) && ( is_string( $vid['url'] ) && $vid['url'] !== '' ) ) {
				$url = $vid['url'];
			}
			// (Temporary) Work around for screencast - @todo - get rid of the array in url.
			elseif ( isset( $vid['url'] ) && ( is_array( $vid['url'] ) && isset( $vid['url']['url'] ) && $vid['url']['url'] !== '' ) ) {
				$url = $vid['url']['url'];
			}


			/* Test the url against the known domains */
			if ( $url !== '' ) {

				// Set the local domain statics if not done before.
				if ( ! isset( $site_host ) && ! isset( $site_domain ) ) {
					$site        = $this->parse_url( site_url() );
					$site_host   = $site['host'];
					$site_domain = $site['domain'];
					unset( $site );
				}

				if ( ! isset( $network_host ) && ! isset( $network_domain ) ) {
					$network_host   = '';
					$network_domain = '';
					if ( is_multisite() ) {
						$network        = $this->parse_url( network_site_url() );
						$network_host   = $network['host'];
						$network_domain = $network['domain'];
						unset( $network );
					}
				}


				// Ok, we have a url, let's see if it's typed properly.
				$parsed_url = $this->parse_url( $url );

				// Only go through all the switches if we need to - prevents overhead in 80% of the cases.
				if ( ! isset( $vid['type'] ) || $parsed_url['domainname'] !== $vid['type'] ) {

					// Set the rest of the statics if not done before.
					if ( ! isset( $wistia_host ) ) {
						$wistia_host = '';
						if ( $this->options['wistia_domain'] !== '' ) {
							$wistia_host = 'http://' . $this->options['wistia_domain'];
							$wistia_host = $this->parse_url( $wistia_host, 'host' );
						}
					}

					// @todo Verify what the value of evs_location could be to make sure this will work.
					if ( ! isset( $evs_domain ) ) {
						$evs_domain   = 'easyvideosuite.com';
						$evs_location = get_option( 'evs_location' );
						if ( is_string( $evs_location ) && $evs_location !== '' ) {
							$evs_domain = $this->parse_url( $evs_location, 'domain' );
						}
						unset( $evs_location );
					}


					// Full hostname: www.test.com.
					switch ( $parsed_url['host'] ) {
						case $wistia_host:
							$type = 'wistia';
							break;

						case 'v.wordpress.com':
							$type = 'wordpresstv';
							break;
					}

					/*
					 * Only domain: 'test' in www.test.com
					 * Used for domains which have a wide range of tld registrations, think youtube.nl, youtube.de etc
					 */
					if ( $type === '' ) {
						switch ( $parsed_url['domainname'] ) {
							case 'youtube':
								$type = $parsed_url['domainname'];
								break;
						}
					}

					/* Domain: 'test.com' in www.test.com */
					if ( $type === '' ) {
						switch ( $parsed_url['domain'] ) {
							case 'dai.ly':
								$type = 'dailymotion';
								break;

							case 'flic.kr':
								$type = 'flickr';
								break;

							case 'wi.st':
								$type = 'wistia';
								break;

							case 'youtu.be':
							case 'youtube-nocookie.com':
								$type = 'youtube';
								break;

							case $evs_domain:
							case 'easyvideosuite.com':
								$type = 'evs';
								break;

							case 'video214.com':
								$type = 'animoto';
								break;

							case 'ifilm.com':
								$type = 'spike';
								break;

							case 'archive.org':
							case 'embed.ly':
							case 'muzu.tv':
							case 'wordpress.tv':
								$type = str_replace( '.', '', $parsed_url['domain'] );
								break;

							case '23video.com':
							case 'animoto.com':
							case 'blip.tv':
							case 'blip.com':
							case 'brightcove.com':
							case 'cincopa.com':
							case 'collegehumor.com':
							case 'dailymotion.com':
							case 'embedly.com':
							case 'flickr.com':
							case 'funnyordie.com':
							case 'hulu.com':
							case 'metacafe.com':
							case 'revision3.com':
							case 'screencast.com':
							case 'screenr.com':
							case 'snotr.com':
							case 'spike.com':
							case 'ted.com':
							case 'ted.org':
							case 'ustudio.com':
							case 'veoh.com':
							case 'viddler.com':
							case 'videojug.com':
							case 'vidyard.com':
							case 'vimeo.com':
							case 'vine.co':
							case 'wistia.com':
							case 'wistia.net':
								$type = $parsed_url['domainname'];
								break;
						}
					}
				}

				// Add the 'maybe_local' key if potentially needed.
				if ( ( $parsed_url['host'] === $network_host || $parsed_url['host'] === $site_host ) || ( $parsed_url['domain'] === $site_domain || $parsed_url['domain'] === $network_domain ) ) {
					$vid['maybe_local'] = true;
				}
			}

			if ( $type !== '' ) {
				$vid['type'] = $type;
			}
			/* If we're using the old type, make sure it's not in a non-usable format */
			elseif ( isset( $vid['type'] ) ) {
				$vid['type'] = $this->correct_faulty_service_types( $vid['type'] );
			}

			return $vid;
		}

		/**
		 *    If we have a service type make sure it's not in a non-usable format
		 *
		 * @param  string $type Service type.
		 *
		 * @return string
		 */
		private function correct_faulty_service_types( $type ) {
			if ( is_string( $type ) && $type !== '' ) {
				switch ( $type ) {
					case 'archive.org':
						$type = 'archiveorg';
						break;

					case 'blip.tv':
					case 'bliptv':
						$type = 'blip';
						break;

					case 'muzu.tv':
					case 'muzu':
						$type = 'muzutv';
						break;

					case 'wordpress.tv':
						$type = 'wordpresstv';
						break;
				}
			}

			return $type;
		}

		/**
		 * Fix no-name attributes, i.e. [video=http://.....] syntax
		 * Inspired by Viper video Quicktags
		 *
		 * @param  array $atts Shortcode attributes.
		 *
		 * @return array
		 */
		protected function fix_sc_attributes( $atts = array() ) {
			if ( isset( $atts[0] ) && ! empty( $atts[0] ) ) {
				$atts[0] = ltrim( $atts[0], '="\'' );
				$atts[0] = rtrim( $atts[0], '"\'' );
			}

			return $atts;
		}

		/**
		 * Html entity decoding for shortcode attributes and post meta values
		 * - Will first change invalid entities to valid ones - &#000058 -> &#58;
		 * - Then change named ones to numeric ones
		 * - Then decode them all to their normal characters
		 * - And remove any surrounding whitespace
		 *
		 * @param string $string Arbitrary string.
		 *
		 * @return string
		 */
		protected function normalize_entities( $string ) {
			return trim( wp_kses_decode_entities( ent2ncr( wp_kses_normalize_entities( $string ) ) ) );
		}

		/**
		 * Parse a url to its components in a largely cross-PHP consistent manner.
		 *
		 * {@internal The WP version was introduced in WP 4.4.0 and will started
		 * supporting the second argument "component" in WP 4.7.0.}}
		 *
		 * @link https://developer.wordpress.org/reference/functions/wp_parse_url/
		 * @link http://php.net/manual/en/function.parse-url.php
		 * @link https://core.trac.wordpress.org/ticket/36356
		 *
		 * @since 3.8.0
		 * @since 6.3.0 Now always falls through to the WP function as the minimum
		 *              required WP version is now > 4.6.
		 *
		 * @param string $url       The url to parse.
		 * @param int    $component One of the predefined PHP url component constants.
		 *
		 * @return array|string|int|null|false False for seriously malformed urls.
		 *                                     Array of components if no specific
		 *                                     component was requested.
		 *                                     String if a component was requested and
		 *                                     available in the parsed url.
		 *                                     Int if the requested component was 'port'.
		 *                                     Null if the requested component was not
		 *                                     in the url.
		 */
		public static function wp_parse_url( $url, $component = -1 ) {
			return wp_parse_url( $url, $component );
		}

		/**
		 * Parse a URL and find the host name and more.
		 *
		 * {@internal Used to be regex based, but the regex was buggy in a few places, most notably it
		 * failed on:
		 * - protocol independent urls
		 * - arguments in array syntax
		 * - query starting with & not ?
		 * This version deals with all those cases too and is compatible with the expected array return elements
		 * from both the old function as well as the php native parse_url function.}}
		 *
		 * @since    1.1
		 *
		 * @param string     $url       The URL to parse.
		 * @param string|int $component (optional) The specific component to return, either the component
		 *                              key or one of the PHP Native PHP_URL_* constants.
		 *
		 * @return array|string  An array of url parts or the string value of one specific part which could
		 *                       be an empty string
		 */
		public static function parse_url( $url, $component = -1 ) {
			$defaults = array(
				'scheme'     => '',
				'user'       => '',
				'login'      => '',
				'pass'       => '',
				'host'       => '',
				'subdomain'  => '',
				'domain'     => '',
				'domainname' => '',
				'extension'  => '',
				'port'       => '',
				'path'       => '',
				'file'       => '',
				'query'      => '',
				'arg'        => '',
				'fragment'   => '',
				'anchor'     => '',
			);

			if ( strpos( $url, '//' ) === 0 ) {
				// Work around php pre5.4.17 bug for protocol independent urls.
				$url = 'http:' . $url;
			}

			$parsed_url = $defaults;
			if ( strpos( $url, '/' ) !== 0 ) {
				// This function is not meant for relative urls.
				$parsed_url = self::wp_parse_url( $url );
				if ( is_array( $parsed_url ) && $parsed_url !== array() ) {
					$parsed_url = array_merge( $defaults, $parsed_url );

					if ( $parsed_url['host'] !== '' ) {
						$host                     = explode( '.', $parsed_url['host'] );
						$parsed_url['extension']  = array_pop( $host );
						$parsed_url['domainname'] = array_pop( $host );
						$parsed_url['domain']     = $parsed_url['domainname'] . '.' . $parsed_url['extension'];
						$parsed_url['subdomain']  = implode( '.', $host );
					}
					if ( $parsed_url['path'] !== '' && strrpos( $parsed_url['path'], '/' ) !== ( strlen( $parsed_url['path'] ) - 1 ) ) {
						$file               = explode( '/', $parsed_url['path'] );
						$parsed_url['file'] = array_pop( $file );

						if ( strpos( $parsed_url['file'], '&' ) !== false && $parsed_url['query'] === '' ) {
							$parsed_url['query'] = substr( self::stristr( $parsed_url['file'], '&' ), 1 );
							$parsed_url['file']  = self::stristr( $parsed_url['file'], '&', true );
							$parsed_url['path']  = str_replace( '&' . $parsed_url['query'], '', $parsed_url['path'] );
						}
					}

					// Compatibility with the array keys of the previously used regex based function.
					$parsed_url['login']  = $parsed_url['user'];
					$parsed_url['arg']    = $parsed_url['query'];
					$parsed_url['anchor'] = $parsed_url['fragment'];
				}
				else {
					$parsed_url = $defaults;
				}
			}

			// Maybe translate component constract to key name.
			if ( $component !== -1 && ! is_string( $component ) ) {
				$component = self::translate_php_url_constant_to_key( $component );
			}

			if ( is_string( $component ) ) {
				if ( isset( $parsed_url[ $component ] ) ) {
					return $parsed_url[ $component ];
				}
				else {
					return '';
				}
			}
			else {
				return $parsed_url;
			}
		}

		/**
		 * Translate a PHP_URL_* constant to the named array keys we use
		 *
		 * @param  int $constant PHP_URL_* constant.
		 *
		 * @return string|bool   The named key or false
		 */
		private static function translate_php_url_constant_to_key( $constant ) {
			$translation = array(
				0 => 'scheme',
				1 => 'host',
				2 => 'port',
				3 => 'user',
				4 => 'pass',
				5 => 'path',
				6 => 'query',
				7 => 'fragment',
			);

			if ( isset( $translation[ $constant ] ) ) {
				return $translation[ $constant ];
			}
			else {
				return false;
			}
		}

		/**
		 * Strstr PHP 5.2 compatibility
		 *
		 * @link http://php.net/strstr
		 *
		 * @param string $haystack      Text to search.
		 * @param mixed  $needle        Needle to find.
		 * @param bool   $before_needle Before needle.
		 *
		 * @return string|bool Returns the matched substring or false if needle is not found
		 */
		public static function strstr( $haystack, $needle, $before_needle = false ) {
			if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
				return strstr( $haystack, $needle, $before_needle );
			}
			else {
				if ( $before_needle === false ) {
					return strstr( $haystack, $needle );
				}
				else {
					$pos = strpos( $haystack, $needle );
					if ( $pos !== false ) {
						return substr( $haystack, 0, $pos );
					}
					else {
						return false;
					}
				}
			}
		}

		/**
		 * Stristr PHP 5.2 compatibility
		 *
		 * @link http://php.net/stristr
		 *
		 * @param string $haystack      Text to search.
		 * @param mixed  $needle        Needle to find.
		 * @param bool   $before_needle Before needle.
		 *
		 * @return string|bool Returns the matched substring or false if needle is not found
		 */
		public static function stristr( $haystack, $needle, $before_needle = false ) {
			if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) ) {
				return stristr( $haystack, $needle, $before_needle );
			}
			else {
				if ( $before_needle === false ) {
					return stristr( $haystack, $needle );
				}
				else {
					$pos = stripos( $haystack, $needle );
					if ( $pos !== false ) {
						return substr( $haystack, 0, $pos );
					}
					else {
						return false;
					}
				}
			}
		}
	} /* End of class */


	/**
	 * Setup the class statics on file load
	 */
	WPSEO_Video_Analyse_Post::set_statics();

} /* End of class-exists wrapper */

haha - 2025