wp/wp-includes/class-oembed.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 21 48c4eec2b7e6
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
     1 <?php
     1 <?php
     2 /**
     2 /**
     3  * API for fetching the HTML to embed remote content based on a provided URL
     3  * API for fetching the HTML to embed remote content based on a provided URL.
     4  *
     4  *
     5  * Used internally by the WP_Embed class, but is designed to be generic.
     5  * This file is deprecated, use 'wp-includes/class-wp-oembed.php' instead.
     6  *
     6  *
     7  * @link https://codex.wordpress.org/oEmbed oEmbed Codex Article
     7  * @deprecated 5.3.0
     8  * @link http://oembed.com/ oEmbed Homepage
       
     9  *
       
    10  * @package WordPress
     8  * @package WordPress
    11  * @subpackage oEmbed
     9  * @subpackage oEmbed
    12  */
    10  */
    13 
    11 
    14 /**
    12 _deprecated_file( basename( __FILE__ ), '5.3.0', 'wp-includes/class-wp-oembed.php' );
    15  * Core class used to implement oEmbed functionality.
       
    16  *
       
    17  * @since 2.9.0
       
    18  */
       
    19 class WP_oEmbed {
       
    20 
    13 
    21 	/**
    14 /** WP_oEmbed class */
    22 	 * A list of oEmbed providers.
    15 require_once ABSPATH . 'wp-includes/class-wp-oembed.php';
    23 	 *
       
    24 	 * @since 2.9.0
       
    25 	 * @var array
       
    26 	 */
       
    27 	public $providers = array();
       
    28 
       
    29 	/**
       
    30 	 * A list of an early oEmbed providers.
       
    31 	 *
       
    32 	 * @since 4.0.0
       
    33 	 * @var array
       
    34 	 */
       
    35 	public static $early_providers = array();
       
    36 
       
    37 	/**
       
    38 	 * A list of private/protected methods, used for backward compatibility.
       
    39 	 *
       
    40 	 * @since 4.2.0
       
    41 	 * @var array
       
    42 	 */
       
    43 	private $compat_methods = array( '_fetch_with_format', '_parse_json', '_parse_xml', '_parse_xml_body' );
       
    44 
       
    45 	/**
       
    46 	 * Constructor.
       
    47 	 *
       
    48 	 * @since 2.9.0
       
    49 	 */
       
    50 	public function __construct() {
       
    51 		$host      = urlencode( home_url() );
       
    52 		$providers = array(
       
    53 			'#https?://((m|www)\.)?youtube\.com/watch.*#i' => array( 'https://www.youtube.com/oembed', true ),
       
    54 			'#https?://((m|www)\.)?youtube\.com/playlist.*#i' => array( 'https://www.youtube.com/oembed', true ),
       
    55 			'#https?://youtu\.be/.*#i'                     => array( 'https://www.youtube.com/oembed', true ),
       
    56 			'#https?://(.+\.)?vimeo\.com/.*#i'             => array( 'https://vimeo.com/api/oembed.{format}', true ),
       
    57 			'#https?://(www\.)?dailymotion\.com/.*#i'      => array( 'https://www.dailymotion.com/services/oembed', true ),
       
    58 			'#https?://dai\.ly/.*#i'                       => array( 'https://www.dailymotion.com/services/oembed', true ),
       
    59 			'#https?://(www\.)?flickr\.com/.*#i'           => array( 'https://www.flickr.com/services/oembed/', true ),
       
    60 			'#https?://flic\.kr/.*#i'                      => array( 'https://www.flickr.com/services/oembed/', true ),
       
    61 			'#https?://(.+\.)?smugmug\.com/.*#i'           => array( 'https://api.smugmug.com/services/oembed/', true ),
       
    62 			'#https?://(www\.)?hulu\.com/watch/.*#i'       => array( 'http://www.hulu.com/api/oembed.{format}', true ),
       
    63 			'#https?://(www\.)?scribd\.com/(doc|document)/.*#i' => array( 'https://www.scribd.com/services/oembed', true ),
       
    64 			'#https?://wordpress\.tv/.*#i'                 => array( 'https://wordpress.tv/oembed/', true ),
       
    65 			'#https?://(.+\.)?polldaddy\.com/.*#i'         => array( 'https://api.crowdsignal.com/oembed', true ),
       
    66 			'#https?://poll\.fm/.*#i'                      => array( 'https://api.crowdsignal.com/oembed', true ),
       
    67 			'#https?://(.+\.)?survey\.fm/.*#i'             => array( 'https://api.crowdsignal.com/oembed', true ),
       
    68 			'#https?://(www\.)?twitter\.com/\w{1,15}/status(es)?/.*#i' => array( 'https://publish.twitter.com/oembed', true ),
       
    69 			'#https?://(www\.)?twitter\.com/\w{1,15}$#i'   => array( 'https://publish.twitter.com/oembed', true ),
       
    70 			'#https?://(www\.)?twitter\.com/\w{1,15}/likes$#i' => array( 'https://publish.twitter.com/oembed', true ),
       
    71 			'#https?://(www\.)?twitter\.com/\w{1,15}/lists/.*#i' => array( 'https://publish.twitter.com/oembed', true ),
       
    72 			'#https?://(www\.)?twitter\.com/\w{1,15}/timelines/.*#i' => array( 'https://publish.twitter.com/oembed', true ),
       
    73 			'#https?://(www\.)?twitter\.com/i/moments/.*#i' => array( 'https://publish.twitter.com/oembed', true ),
       
    74 			'#https?://(www\.)?soundcloud\.com/.*#i'       => array( 'https://soundcloud.com/oembed', true ),
       
    75 			'#https?://(.+?\.)?slideshare\.net/.*#i'       => array( 'https://www.slideshare.net/api/oembed/2', true ),
       
    76 			'#https?://(www\.)?instagr(\.am|am\.com)/(p|tv)/.*#i' => array( 'https://api.instagram.com/oembed', true ),
       
    77 			'#https?://(open|play)\.spotify\.com/.*#i'     => array( 'https://embed.spotify.com/oembed/', true ),
       
    78 			'#https?://(.+\.)?imgur\.com/.*#i'             => array( 'https://api.imgur.com/oembed', true ),
       
    79 			'#https?://(www\.)?meetu(\.ps|p\.com)/.*#i'    => array( 'https://api.meetup.com/oembed', true ),
       
    80 			'#https?://(www\.)?issuu\.com/.+/docs/.+#i'    => array( 'https://issuu.com/oembed_wp', true ),
       
    81 			'#https?://(www\.)?collegehumor\.com/video/.*#i' => array( 'https://www.collegehumor.com/oembed.{format}', true ),
       
    82 			'#https?://(www\.)?mixcloud\.com/.*#i'         => array( 'https://www.mixcloud.com/oembed', true ),
       
    83 			'#https?://(www\.|embed\.)?ted\.com/talks/.*#i' => array( 'https://www.ted.com/services/v1/oembed.{format}', true ),
       
    84 			'#https?://(www\.)?(animoto|video214)\.com/play/.*#i' => array( 'https://animoto.com/oembeds/create', true ),
       
    85 			'#https?://(.+)\.tumblr\.com/post/.*#i'        => array( 'https://www.tumblr.com/oembed/1.0', true ),
       
    86 			'#https?://(www\.)?kickstarter\.com/projects/.*#i' => array( 'https://www.kickstarter.com/services/oembed', true ),
       
    87 			'#https?://kck\.st/.*#i'                       => array( 'https://www.kickstarter.com/services/oembed', true ),
       
    88 			'#https?://cloudup\.com/.*#i'                  => array( 'https://cloudup.com/oembed', true ),
       
    89 			'#https?://(www\.)?reverbnation\.com/.*#i'     => array( 'https://www.reverbnation.com/oembed', true ),
       
    90 			'#https?://videopress\.com/v/.*#'              => array( 'https://public-api.wordpress.com/oembed/?for=' . $host, true ),
       
    91 			'#https?://(www\.)?reddit\.com/r/[^/]+/comments/.*#i' => array( 'https://www.reddit.com/oembed', true ),
       
    92 			'#https?://(www\.)?speakerdeck\.com/.*#i'      => array( 'https://speakerdeck.com/oembed.{format}', true ),
       
    93 			'#https?://www\.facebook\.com/.*/posts/.*#i'   => array( 'https://www.facebook.com/plugins/post/oembed.json/', true ),
       
    94 			'#https?://www\.facebook\.com/.*/activity/.*#i' => array( 'https://www.facebook.com/plugins/post/oembed.json/', true ),
       
    95 			'#https?://www\.facebook\.com/.*/photos/.*#i'  => array( 'https://www.facebook.com/plugins/post/oembed.json/', true ),
       
    96 			'#https?://www\.facebook\.com/photo(s/|\.php).*#i' => array( 'https://www.facebook.com/plugins/post/oembed.json/', true ),
       
    97 			'#https?://www\.facebook\.com/permalink\.php.*#i' => array( 'https://www.facebook.com/plugins/post/oembed.json/', true ),
       
    98 			'#https?://www\.facebook\.com/media/.*#i'      => array( 'https://www.facebook.com/plugins/post/oembed.json/', true ),
       
    99 			'#https?://www\.facebook\.com/questions/.*#i'  => array( 'https://www.facebook.com/plugins/post/oembed.json/', true ),
       
   100 			'#https?://www\.facebook\.com/notes/.*#i'      => array( 'https://www.facebook.com/plugins/post/oembed.json/', true ),
       
   101 			'#https?://www\.facebook\.com/.*/videos/.*#i'  => array( 'https://www.facebook.com/plugins/video/oembed.json/', true ),
       
   102 			'#https?://www\.facebook\.com/video\.php.*#i'  => array( 'https://www.facebook.com/plugins/video/oembed.json/', true ),
       
   103 			'#https?://(www\.)?screencast\.com/.*#i'       => array( 'https://api.screencast.com/external/oembed', true ),
       
   104 			'#https?://([a-z0-9-]+\.)?amazon\.(com|com\.mx|com\.br|ca)/.*#i' => array( 'https://read.amazon.com/kp/api/oembed', true ),
       
   105 			'#https?://([a-z0-9-]+\.)?amazon\.(co\.uk|de|fr|it|es|in|nl|ru)/.*#i' => array( 'https://read.amazon.co.uk/kp/api/oembed', true ),
       
   106 			'#https?://([a-z0-9-]+\.)?amazon\.(co\.jp|com\.au)/.*#i' => array( 'https://read.amazon.com.au/kp/api/oembed', true ),
       
   107 			'#https?://([a-z0-9-]+\.)?amazon\.cn/.*#i'     => array( 'https://read.amazon.cn/kp/api/oembed', true ),
       
   108 			'#https?://(www\.)?a\.co/.*#i'                 => array( 'https://read.amazon.com/kp/api/oembed', true ),
       
   109 			'#https?://(www\.)?amzn\.to/.*#i'              => array( 'https://read.amazon.com/kp/api/oembed', true ),
       
   110 			'#https?://(www\.)?amzn\.eu/.*#i'              => array( 'https://read.amazon.co.uk/kp/api/oembed', true ),
       
   111 			'#https?://(www\.)?amzn\.in/.*#i'              => array( 'https://read.amazon.in/kp/api/oembed', true ),
       
   112 			'#https?://(www\.)?amzn\.asia/.*#i'            => array( 'https://read.amazon.com.au/kp/api/oembed', true ),
       
   113 			'#https?://(www\.)?z\.cn/.*#i'                 => array( 'https://read.amazon.cn/kp/api/oembed', true ),
       
   114 			'#https?://www\.someecards\.com/.+-cards/.+#i' => array( 'https://www.someecards.com/v2/oembed/', true ),
       
   115 			'#https?://www\.someecards\.com/usercards/viewcard/.+#i' => array( 'https://www.someecards.com/v2/oembed/', true ),
       
   116 			'#https?://some\.ly\/.+#i'                     => array( 'https://www.someecards.com/v2/oembed/', true ),
       
   117 		);
       
   118 
       
   119 		if ( ! empty( self::$early_providers['add'] ) ) {
       
   120 			foreach ( self::$early_providers['add'] as $format => $data ) {
       
   121 				$providers[ $format ] = $data;
       
   122 			}
       
   123 		}
       
   124 
       
   125 		if ( ! empty( self::$early_providers['remove'] ) ) {
       
   126 			foreach ( self::$early_providers['remove'] as $format ) {
       
   127 				unset( $providers[ $format ] );
       
   128 			}
       
   129 		}
       
   130 
       
   131 		self::$early_providers = array();
       
   132 
       
   133 		/**
       
   134 		 * Filters the list of whitelisted oEmbed providers.
       
   135 		 *
       
   136 		 * Since WordPress 4.4, oEmbed discovery is enabled for all users and allows embedding of sanitized
       
   137 		 * iframes. The providers in this list are whitelisted, meaning they are trusted and allowed to
       
   138 		 * embed any content, such as iframes, videos, JavaScript, and arbitrary HTML.
       
   139 		 *
       
   140 		 * Supported providers:
       
   141 		 *
       
   142 		 * |   Provider   |                     Flavor                | Supports HTTPS |  Since  |
       
   143 		 * | ------------ | ----------------------------------------- | :------------: | ------- |
       
   144 		 * | Dailymotion  | dailymotion.com                           |      Yes       | 2.9.0   |
       
   145 		 * | Flickr       | flickr.com                                |      Yes       | 2.9.0   |
       
   146 		 * | Hulu         | hulu.com                                  |      Yes       | 2.9.0   |
       
   147 		 * | Scribd       | scribd.com                                |      Yes       | 2.9.0   |
       
   148 		 * | Vimeo        | vimeo.com                                 |      Yes       | 2.9.0   |
       
   149 		 * | WordPress.tv | wordpress.tv                              |      Yes       | 2.9.0   |
       
   150 		 * | YouTube      | youtube.com/watch                         |      Yes       | 2.9.0   |
       
   151 		 * | Crowdsignal  | polldaddy.com                             |      Yes       | 3.0.0   |
       
   152 		 * | SmugMug      | smugmug.com                               |      Yes       | 3.0.0   |
       
   153 		 * | YouTube      | youtu.be                                  |      Yes       | 3.0.0   |
       
   154 		 * | Twitter      | twitter.com                               |      Yes       | 3.4.0   |
       
   155 		 * | Instagram    | instagram.com                             |      Yes       | 3.5.0   |
       
   156 		 * | Instagram    | instagr.am                                |      Yes       | 3.5.0   |
       
   157 		 * | Slideshare   | slideshare.net                            |      Yes       | 3.5.0   |
       
   158 		 * | SoundCloud   | soundcloud.com                            |      Yes       | 3.5.0   |
       
   159 		 * | Dailymotion  | dai.ly                                    |      Yes       | 3.6.0   |
       
   160 		 * | Flickr       | flic.kr                                   |      Yes       | 3.6.0   |
       
   161 		 * | Spotify      | spotify.com                               |      Yes       | 3.6.0   |
       
   162 		 * | Imgur        | imgur.com                                 |      Yes       | 3.9.0   |
       
   163 		 * | Meetup.com   | meetup.com                                |      Yes       | 3.9.0   |
       
   164 		 * | Meetup.com   | meetu.ps                                  |      Yes       | 3.9.0   |
       
   165 		 * | Animoto      | animoto.com                               |      Yes       | 4.0.0   |
       
   166 		 * | Animoto      | video214.com                              |      Yes       | 4.0.0   |
       
   167 		 * | CollegeHumor | collegehumor.com                          |      Yes       | 4.0.0   |
       
   168 		 * | Issuu        | issuu.com                                 |      Yes       | 4.0.0   |
       
   169 		 * | Mixcloud     | mixcloud.com                              |      Yes       | 4.0.0   |
       
   170 		 * | Crowdsignal  | poll.fm                                   |      Yes       | 4.0.0   |
       
   171 		 * | TED          | ted.com                                   |      Yes       | 4.0.0   |
       
   172 		 * | YouTube      | youtube.com/playlist                      |      Yes       | 4.0.0   |
       
   173 		 * | Tumblr       | tumblr.com                                |      Yes       | 4.2.0   |
       
   174 		 * | Kickstarter  | kickstarter.com                           |      Yes       | 4.2.0   |
       
   175 		 * | Kickstarter  | kck.st                                    |      Yes       | 4.2.0   |
       
   176 		 * | Cloudup      | cloudup.com                               |      Yes       | 4.3.0   |
       
   177 		 * | ReverbNation | reverbnation.com                          |      Yes       | 4.4.0   |
       
   178 		 * | VideoPress   | videopress.com                            |      Yes       | 4.4.0   |
       
   179 		 * | Reddit       | reddit.com                                |      Yes       | 4.4.0   |
       
   180 		 * | Speaker Deck | speakerdeck.com                           |      Yes       | 4.4.0   |
       
   181 		 * | Twitter      | twitter.com/timelines                     |      Yes       | 4.5.0   |
       
   182 		 * | Twitter      | twitter.com/moments                       |      Yes       | 4.5.0   |
       
   183 		 * | Facebook     | facebook.com                              |      Yes       | 4.7.0   |
       
   184 		 * | Twitter      | twitter.com/user                          |      Yes       | 4.7.0   |
       
   185 		 * | Twitter      | twitter.com/likes                         |      Yes       | 4.7.0   |
       
   186 		 * | Twitter      | twitter.com/lists                         |      Yes       | 4.7.0   |
       
   187 		 * | Screencast   | screencast.com                            |      Yes       | 4.8.0   |
       
   188 		 * | Amazon       | amazon.com (com.mx, com.br, ca)           |      Yes       | 4.9.0   |
       
   189 		 * | Amazon       | amazon.de (fr, it, es, in, nl, ru, co.uk) |      Yes       | 4.9.0   |
       
   190 		 * | Amazon       | amazon.co.jp (com.au)                     |      Yes       | 4.9.0   |
       
   191 		 * | Amazon       | amazon.cn                                 |      Yes       | 4.9.0   |
       
   192 		 * | Amazon       | a.co                                      |      Yes       | 4.9.0   |
       
   193 		 * | Amazon       | amzn.to (eu, in, asia)                    |      Yes       | 4.9.0   |
       
   194 		 * | Amazon       | z.cn                                      |      Yes       | 4.9.0   |
       
   195 		 * | Someecards   | someecards.com                            |      Yes       | 4.9.0   |
       
   196 		 * | Someecards   | some.ly                                   |      Yes       | 4.9.0   |
       
   197 		 * | Crowdsignal  | survey.fm                                 |      Yes       | 5.1.0   |
       
   198 		 * | Instagram TV | instagram.com                             |      Yes       | 5.1.0   |
       
   199 		 * | Instagram TV | instagr.am                                |      Yes       | 5.1.0   |
       
   200 		 *
       
   201 		 * No longer supported providers:
       
   202 		 *
       
   203 		 * |   Provider   |        Flavor        | Supports HTTPS |   Since   |  Removed  |
       
   204 		 * | ------------ | -------------------- | :------------: | --------- | --------- |
       
   205 		 * | Qik          | qik.com              |      Yes       | 2.9.0     | 3.9.0     |
       
   206 		 * | Viddler      | viddler.com          |      Yes       | 2.9.0     | 4.0.0     |
       
   207 		 * | Revision3    | revision3.com        |      No        | 2.9.0     | 4.2.0     |
       
   208 		 * | Blip         | blip.tv              |      No        | 2.9.0     | 4.4.0     |
       
   209 		 * | Rdio         | rdio.com             |      Yes       | 3.6.0     | 4.4.1     |
       
   210 		 * | Rdio         | rd.io                |      Yes       | 3.6.0     | 4.4.1     |
       
   211 		 * | Vine         | vine.co              |      Yes       | 4.1.0     | 4.9.0     |
       
   212 		 * | Photobucket  | photobucket.com      |      No        | 2.9.0     | 5.1.0     |
       
   213 		 * | Funny or Die | funnyordie.com       |      Yes       | 3.0.0     | 5.1.0     |
       
   214 		 *
       
   215 		 * @see wp_oembed_add_provider()
       
   216 		 *
       
   217 		 * @since 2.9.0
       
   218 		 *
       
   219 		 * @param array[] $providers An array of arrays containing data about popular oEmbed providers.
       
   220 		 */
       
   221 		$this->providers = apply_filters( 'oembed_providers', $providers );
       
   222 
       
   223 		// Fix any embeds that contain new lines in the middle of the HTML which breaks wpautop().
       
   224 		add_filter( 'oembed_dataparse', array( $this, '_strip_newlines' ), 10, 3 );
       
   225 	}
       
   226 
       
   227 	/**
       
   228 	 * Exposes private/protected methods for backward compatibility.
       
   229 	 *
       
   230 	 * @since 4.0.0
       
   231 	 *
       
   232 	 * @param string   $name      Method to call.
       
   233 	 * @param array    $arguments Arguments to pass when calling.
       
   234 	 * @return mixed|bool Return value of the callback, false otherwise.
       
   235 	 */
       
   236 	public function __call( $name, $arguments ) {
       
   237 		if ( in_array( $name, $this->compat_methods ) ) {
       
   238 			return call_user_func_array( array( $this, $name ), $arguments );
       
   239 		}
       
   240 		return false;
       
   241 	}
       
   242 
       
   243 	/**
       
   244 	 * Takes a URL and returns the corresponding oEmbed provider's URL, if there is one.
       
   245 	 *
       
   246 	 * @since 4.0.0
       
   247 	 *
       
   248 	 * @see WP_oEmbed::discover()
       
   249 	 *
       
   250 	 * @param string        $url  The URL to the content.
       
   251 	 * @param string|array  $args Optional provider arguments.
       
   252 	 * @return false|string False on failure, otherwise the oEmbed provider URL.
       
   253 	 */
       
   254 	public function get_provider( $url, $args = '' ) {
       
   255 		$args = wp_parse_args( $args );
       
   256 
       
   257 		$provider = false;
       
   258 
       
   259 		if ( ! isset( $args['discover'] ) ) {
       
   260 			$args['discover'] = true;
       
   261 		}
       
   262 
       
   263 		foreach ( $this->providers as $matchmask => $data ) {
       
   264 			list( $providerurl, $regex ) = $data;
       
   265 
       
   266 			// Turn the asterisk-type provider URLs into regex
       
   267 			if ( ! $regex ) {
       
   268 				$matchmask = '#' . str_replace( '___wildcard___', '(.+)', preg_quote( str_replace( '*', '___wildcard___', $matchmask ), '#' ) ) . '#i';
       
   269 				$matchmask = preg_replace( '|^#http\\\://|', '#https?\://', $matchmask );
       
   270 			}
       
   271 
       
   272 			if ( preg_match( $matchmask, $url ) ) {
       
   273 				$provider = str_replace( '{format}', 'json', $providerurl ); // JSON is easier to deal with than XML
       
   274 				break;
       
   275 			}
       
   276 		}
       
   277 
       
   278 		if ( ! $provider && $args['discover'] ) {
       
   279 			$provider = $this->discover( $url );
       
   280 		}
       
   281 
       
   282 		return $provider;
       
   283 	}
       
   284 
       
   285 	/**
       
   286 	 * Adds an oEmbed provider.
       
   287 	 *
       
   288 	 * The provider is added just-in-time when wp_oembed_add_provider() is called before
       
   289 	 * the {@see 'plugins_loaded'} hook.
       
   290 	 *
       
   291 	 * The just-in-time addition is for the benefit of the {@see 'oembed_providers'} filter.
       
   292 	 *
       
   293 	 * @since 4.0.0
       
   294 	 *
       
   295 	 * @see wp_oembed_add_provider()
       
   296 	 *
       
   297 	 * @param string $format   Format of URL that this provider can handle. You can use
       
   298 	 *                         asterisks as wildcards.
       
   299 	 * @param string $provider The URL to the oEmbed provider..
       
   300 	 * @param bool   $regex    Optional. Whether the $format parameter is in a regex format.
       
   301 	 *                         Default false.
       
   302 	 */
       
   303 	public static function _add_provider_early( $format, $provider, $regex = false ) {
       
   304 		if ( empty( self::$early_providers['add'] ) ) {
       
   305 			self::$early_providers['add'] = array();
       
   306 		}
       
   307 
       
   308 		self::$early_providers['add'][ $format ] = array( $provider, $regex );
       
   309 	}
       
   310 
       
   311 	/**
       
   312 	 * Removes an oEmbed provider.
       
   313 	 *
       
   314 	 * The provider is removed just-in-time when wp_oembed_remove_provider() is called before
       
   315 	 * the {@see 'plugins_loaded'} hook.
       
   316 	 *
       
   317 	 * The just-in-time removal is for the benefit of the {@see 'oembed_providers'} filter.
       
   318 	 *
       
   319 	 * @since 4.0.0
       
   320 	 *
       
   321 	 * @see wp_oembed_remove_provider()
       
   322 	 *
       
   323 	 * @param string $format The format of URL that this provider can handle. You can use
       
   324 	 *                       asterisks as wildcards.
       
   325 	 */
       
   326 	public static function _remove_provider_early( $format ) {
       
   327 		if ( empty( self::$early_providers['remove'] ) ) {
       
   328 			self::$early_providers['remove'] = array();
       
   329 		}
       
   330 
       
   331 		self::$early_providers['remove'][] = $format;
       
   332 	}
       
   333 
       
   334 	/**
       
   335 	 * Takes a URL and attempts to return the oEmbed data.
       
   336 	 *
       
   337 	 * @see WP_oEmbed::fetch()
       
   338 	 *
       
   339 	 * @since 4.8.0
       
   340 	 *
       
   341 	 * @param string       $url  The URL to the content that should be attempted to be embedded.
       
   342 	 * @param array|string $args Optional. Arguments, usually passed from a shortcode. Default empty.
       
   343 	 * @return false|object False on failure, otherwise the result in the form of an object.
       
   344 	 */
       
   345 	public function get_data( $url, $args = '' ) {
       
   346 		$args = wp_parse_args( $args );
       
   347 
       
   348 		$provider = $this->get_provider( $url, $args );
       
   349 
       
   350 		if ( ! $provider ) {
       
   351 			return false;
       
   352 		}
       
   353 
       
   354 		$data = $this->fetch( $provider, $url, $args );
       
   355 
       
   356 		if ( false === $data ) {
       
   357 			return false;
       
   358 		}
       
   359 
       
   360 		return $data;
       
   361 	}
       
   362 
       
   363 	/**
       
   364 	 * The do-it-all function that takes a URL and attempts to return the HTML.
       
   365 	 *
       
   366 	 * @see WP_oEmbed::fetch()
       
   367 	 * @see WP_oEmbed::data2html()
       
   368 	 *
       
   369 	 * @since 2.9.0
       
   370 	 *
       
   371 	 * @param string       $url  The URL to the content that should be attempted to be embedded.
       
   372 	 * @param array|string $args Optional. Arguments, usually passed from a shortcode. Default empty.
       
   373 	 * @return false|string False on failure, otherwise the UNSANITIZED (and potentially unsafe) HTML that should be used to embed.
       
   374 	 */
       
   375 	public function get_html( $url, $args = '' ) {
       
   376 		/**
       
   377 		 * Filters the oEmbed result before any HTTP requests are made.
       
   378 		 *
       
   379 		 * This allows one to short-circuit the default logic, perhaps by
       
   380 		 * replacing it with a routine that is more optimal for your setup.
       
   381 		 *
       
   382 		 * Passing a non-null value to the filter will effectively short-circuit retrieval,
       
   383 		 * returning the passed value instead.
       
   384 		 *
       
   385 		 * @since 4.5.3
       
   386 		 *
       
   387 		 * @param null|string $result The UNSANITIZED (and potentially unsafe) HTML that should be used to embed. Default null.
       
   388 		 * @param string      $url    The URL to the content that should be attempted to be embedded.
       
   389 		 * @param array       $args   Optional. Arguments, usually passed from a shortcode. Default empty.
       
   390 		 */
       
   391 		$pre = apply_filters( 'pre_oembed_result', null, $url, $args );
       
   392 
       
   393 		if ( null !== $pre ) {
       
   394 			return $pre;
       
   395 		}
       
   396 
       
   397 		$data = $this->get_data( $url, $args );
       
   398 
       
   399 		if ( false === $data ) {
       
   400 			return false;
       
   401 		}
       
   402 
       
   403 		/**
       
   404 		 * Filters the HTML returned by the oEmbed provider.
       
   405 		 *
       
   406 		 * @since 2.9.0
       
   407 		 *
       
   408 		 * @param string|false $data The returned oEmbed HTML (false if unsafe).
       
   409 		 * @param string       $url  URL of the content to be embedded.
       
   410 		 * @param array        $args Optional arguments, usually passed from a shortcode.
       
   411 		 */
       
   412 		return apply_filters( 'oembed_result', $this->data2html( $data, $url ), $url, $args );
       
   413 	}
       
   414 
       
   415 	/**
       
   416 	 * Attempts to discover link tags at the given URL for an oEmbed provider.
       
   417 	 *
       
   418 	 * @since 2.9.0
       
   419 	 *
       
   420 	 * @param string $url The URL that should be inspected for discovery `<link>` tags.
       
   421 	 * @return false|string False on failure, otherwise the oEmbed provider URL.
       
   422 	 */
       
   423 	public function discover( $url ) {
       
   424 		$providers = array();
       
   425 		$args      = array(
       
   426 			'limit_response_size' => 153600, // 150 KB
       
   427 		);
       
   428 
       
   429 		/**
       
   430 		 * Filters oEmbed remote get arguments.
       
   431 		 *
       
   432 		 * @since 4.0.0
       
   433 		 *
       
   434 		 * @see WP_Http::request()
       
   435 		 *
       
   436 		 * @param array  $args oEmbed remote get arguments.
       
   437 		 * @param string $url  URL to be inspected.
       
   438 		 */
       
   439 		$args = apply_filters( 'oembed_remote_get_args', $args, $url );
       
   440 
       
   441 		// Fetch URL content
       
   442 		$request = wp_safe_remote_get( $url, $args );
       
   443 		if ( $html = wp_remote_retrieve_body( $request ) ) {
       
   444 
       
   445 			/**
       
   446 			 * Filters the link types that contain oEmbed provider URLs.
       
   447 			 *
       
   448 			 * @since 2.9.0
       
   449 			 *
       
   450 			 * @param string[] $format Array of oEmbed link types. Accepts 'application/json+oembed',
       
   451 			 *                         'text/xml+oembed', and 'application/xml+oembed' (incorrect,
       
   452 			 *                         used by at least Vimeo).
       
   453 			 */
       
   454 			$linktypes = apply_filters(
       
   455 				'oembed_linktypes',
       
   456 				array(
       
   457 					'application/json+oembed' => 'json',
       
   458 					'text/xml+oembed'         => 'xml',
       
   459 					'application/xml+oembed'  => 'xml',
       
   460 				)
       
   461 			);
       
   462 
       
   463 			// Strip <body>
       
   464 			if ( $html_head_end = stripos( $html, '</head>' ) ) {
       
   465 				$html = substr( $html, 0, $html_head_end );
       
   466 			}
       
   467 
       
   468 			// Do a quick check
       
   469 			$tagfound = false;
       
   470 			foreach ( $linktypes as $linktype => $format ) {
       
   471 				if ( stripos( $html, $linktype ) ) {
       
   472 					$tagfound = true;
       
   473 					break;
       
   474 				}
       
   475 			}
       
   476 
       
   477 			if ( $tagfound && preg_match_all( '#<link([^<>]+)/?>#iU', $html, $links ) ) {
       
   478 				foreach ( $links[1] as $link ) {
       
   479 					$atts = shortcode_parse_atts( $link );
       
   480 
       
   481 					if ( ! empty( $atts['type'] ) && ! empty( $linktypes[ $atts['type'] ] ) && ! empty( $atts['href'] ) ) {
       
   482 						$providers[ $linktypes[ $atts['type'] ] ] = htmlspecialchars_decode( $atts['href'] );
       
   483 
       
   484 						// Stop here if it's JSON (that's all we need)
       
   485 						if ( 'json' == $linktypes[ $atts['type'] ] ) {
       
   486 							break;
       
   487 						}
       
   488 					}
       
   489 				}
       
   490 			}
       
   491 		}
       
   492 
       
   493 		// JSON is preferred to XML
       
   494 		if ( ! empty( $providers['json'] ) ) {
       
   495 			return $providers['json'];
       
   496 		} elseif ( ! empty( $providers['xml'] ) ) {
       
   497 			return $providers['xml'];
       
   498 		} else {
       
   499 			return false;
       
   500 		}
       
   501 	}
       
   502 
       
   503 	/**
       
   504 	 * Connects to a oEmbed provider and returns the result.
       
   505 	 *
       
   506 	 * @since 2.9.0
       
   507 	 *
       
   508 	 * @param string       $provider The URL to the oEmbed provider.
       
   509 	 * @param string       $url      The URL to the content that is desired to be embedded.
       
   510 	 * @param array|string $args     Optional. Arguments, usually passed from a shortcode. Default empty.
       
   511 	 * @return false|object False on failure, otherwise the result in the form of an object.
       
   512 	 */
       
   513 	public function fetch( $provider, $url, $args = '' ) {
       
   514 		$args = wp_parse_args( $args, wp_embed_defaults( $url ) );
       
   515 
       
   516 		$provider = add_query_arg( 'maxwidth', (int) $args['width'], $provider );
       
   517 		$provider = add_query_arg( 'maxheight', (int) $args['height'], $provider );
       
   518 		$provider = add_query_arg( 'url', urlencode( $url ), $provider );
       
   519 		$provider = add_query_arg( 'dnt', 1, $provider );
       
   520 
       
   521 		/**
       
   522 		 * Filters the oEmbed URL to be fetched.
       
   523 		 *
       
   524 		 * @since 2.9.0
       
   525 		 * @since 4.9.0 The `dnt` (Do Not Track) query parameter was added to all oEmbed provider URLs.
       
   526 		 *
       
   527 		 * @param string $provider URL of the oEmbed provider.
       
   528 		 * @param string $url      URL of the content to be embedded.
       
   529 		 * @param array  $args     Optional arguments, usually passed from a shortcode.
       
   530 		 */
       
   531 		$provider = apply_filters( 'oembed_fetch_url', $provider, $url, $args );
       
   532 
       
   533 		foreach ( array( 'json', 'xml' ) as $format ) {
       
   534 			$result = $this->_fetch_with_format( $provider, $format );
       
   535 			if ( is_wp_error( $result ) && 'not-implemented' == $result->get_error_code() ) {
       
   536 				continue;
       
   537 			}
       
   538 			return ( $result && ! is_wp_error( $result ) ) ? $result : false;
       
   539 		}
       
   540 		return false;
       
   541 	}
       
   542 
       
   543 	/**
       
   544 	 * Fetches result from an oEmbed provider for a specific format and complete provider URL
       
   545 	 *
       
   546 	 * @since 3.0.0
       
   547 	 *
       
   548 	 * @param string $provider_url_with_args URL to the provider with full arguments list (url, maxheight, etc.)
       
   549 	 * @param string $format Format to use
       
   550 	 * @return false|object|WP_Error False on failure, otherwise the result in the form of an object.
       
   551 	 */
       
   552 	private function _fetch_with_format( $provider_url_with_args, $format ) {
       
   553 		$provider_url_with_args = add_query_arg( 'format', $format, $provider_url_with_args );
       
   554 
       
   555 		/** This filter is documented in wp-includes/class-oembed.php */
       
   556 		$args = apply_filters( 'oembed_remote_get_args', array(), $provider_url_with_args );
       
   557 
       
   558 		$response = wp_safe_remote_get( $provider_url_with_args, $args );
       
   559 		if ( 501 == wp_remote_retrieve_response_code( $response ) ) {
       
   560 			return new WP_Error( 'not-implemented' );
       
   561 		}
       
   562 		if ( ! $body = wp_remote_retrieve_body( $response ) ) {
       
   563 			return false;
       
   564 		}
       
   565 		$parse_method = "_parse_$format";
       
   566 		return $this->$parse_method( $body );
       
   567 	}
       
   568 
       
   569 	/**
       
   570 	 * Parses a json response body.
       
   571 	 *
       
   572 	 * @since 3.0.0
       
   573 	 *
       
   574 	 * @param string $response_body
       
   575 	 * @return object|false
       
   576 	 */
       
   577 	private function _parse_json( $response_body ) {
       
   578 		$data = json_decode( trim( $response_body ) );
       
   579 		return ( $data && is_object( $data ) ) ? $data : false;
       
   580 	}
       
   581 
       
   582 	/**
       
   583 	 * Parses an XML response body.
       
   584 	 *
       
   585 	 * @since 3.0.0
       
   586 	 *
       
   587 	 * @param string $response_body
       
   588 	 * @return object|false
       
   589 	 */
       
   590 	private function _parse_xml( $response_body ) {
       
   591 		if ( ! function_exists( 'libxml_disable_entity_loader' ) ) {
       
   592 			return false;
       
   593 		}
       
   594 
       
   595 		$loader = libxml_disable_entity_loader( true );
       
   596 		$errors = libxml_use_internal_errors( true );
       
   597 
       
   598 		$return = $this->_parse_xml_body( $response_body );
       
   599 
       
   600 		libxml_use_internal_errors( $errors );
       
   601 		libxml_disable_entity_loader( $loader );
       
   602 
       
   603 		return $return;
       
   604 	}
       
   605 
       
   606 	/**
       
   607 	 * Serves as a helper function for parsing an XML response body.
       
   608 	 *
       
   609 	 * @since 3.6.0
       
   610 	 *
       
   611 	 * @param string $response_body
       
   612 	 * @return stdClass|false
       
   613 	 */
       
   614 	private function _parse_xml_body( $response_body ) {
       
   615 		if ( ! function_exists( 'simplexml_import_dom' ) || ! class_exists( 'DOMDocument', false ) ) {
       
   616 			return false;
       
   617 		}
       
   618 
       
   619 		$dom     = new DOMDocument;
       
   620 		$success = $dom->loadXML( $response_body );
       
   621 		if ( ! $success ) {
       
   622 			return false;
       
   623 		}
       
   624 
       
   625 		if ( isset( $dom->doctype ) ) {
       
   626 			return false;
       
   627 		}
       
   628 
       
   629 		foreach ( $dom->childNodes as $child ) {
       
   630 			if ( XML_DOCUMENT_TYPE_NODE === $child->nodeType ) {
       
   631 				return false;
       
   632 			}
       
   633 		}
       
   634 
       
   635 		$xml = simplexml_import_dom( $dom );
       
   636 		if ( ! $xml ) {
       
   637 			return false;
       
   638 		}
       
   639 
       
   640 		$return = new stdClass;
       
   641 		foreach ( $xml as $key => $value ) {
       
   642 			$return->$key = (string) $value;
       
   643 		}
       
   644 
       
   645 		return $return;
       
   646 	}
       
   647 
       
   648 	/**
       
   649 	 * Converts a data object from WP_oEmbed::fetch() and returns the HTML.
       
   650 	 *
       
   651 	 * @since 2.9.0
       
   652 	 *
       
   653 	 * @param object $data A data object result from an oEmbed provider.
       
   654 	 * @param string $url The URL to the content that is desired to be embedded.
       
   655 	 * @return false|string False on error, otherwise the HTML needed to embed.
       
   656 	 */
       
   657 	public function data2html( $data, $url ) {
       
   658 		if ( ! is_object( $data ) || empty( $data->type ) ) {
       
   659 			return false;
       
   660 		}
       
   661 
       
   662 		$return = false;
       
   663 
       
   664 		switch ( $data->type ) {
       
   665 			case 'photo':
       
   666 				if ( empty( $data->url ) || empty( $data->width ) || empty( $data->height ) ) {
       
   667 					break;
       
   668 				}
       
   669 				if ( ! is_string( $data->url ) || ! is_numeric( $data->width ) || ! is_numeric( $data->height ) ) {
       
   670 					break;
       
   671 				}
       
   672 
       
   673 				$title  = ! empty( $data->title ) && is_string( $data->title ) ? $data->title : '';
       
   674 				$return = '<a href="' . esc_url( $url ) . '"><img src="' . esc_url( $data->url ) . '" alt="' . esc_attr( $title ) . '" width="' . esc_attr( $data->width ) . '" height="' . esc_attr( $data->height ) . '" /></a>';
       
   675 				break;
       
   676 
       
   677 			case 'video':
       
   678 			case 'rich':
       
   679 				if ( ! empty( $data->html ) && is_string( $data->html ) ) {
       
   680 					$return = $data->html;
       
   681 				}
       
   682 				break;
       
   683 
       
   684 			case 'link':
       
   685 				if ( ! empty( $data->title ) && is_string( $data->title ) ) {
       
   686 					$return = '<a href="' . esc_url( $url ) . '">' . esc_html( $data->title ) . '</a>';
       
   687 				}
       
   688 				break;
       
   689 
       
   690 			default:
       
   691 				$return = false;
       
   692 		}
       
   693 
       
   694 		/**
       
   695 		 * Filters the returned oEmbed HTML.
       
   696 		 *
       
   697 		 * Use this filter to add support for custom data types, or to filter the result.
       
   698 		 *
       
   699 		 * @since 2.9.0
       
   700 		 *
       
   701 		 * @param string $return The returned oEmbed HTML.
       
   702 		 * @param object $data   A data object result from an oEmbed provider.
       
   703 		 * @param string $url    The URL of the content to be embedded.
       
   704 		 */
       
   705 		return apply_filters( 'oembed_dataparse', $return, $data, $url );
       
   706 	}
       
   707 
       
   708 	/**
       
   709 	 * Strips any new lines from the HTML.
       
   710 	 *
       
   711 	 * @since 2.9.0 as strip_scribd_newlines()
       
   712 	 * @since 3.0.0
       
   713 	 *
       
   714 	 * @param string $html Existing HTML.
       
   715 	 * @param object $data Data object from WP_oEmbed::data2html()
       
   716 	 * @param string $url The original URL passed to oEmbed.
       
   717 	 * @return string Possibly modified $html
       
   718 	 */
       
   719 	public function _strip_newlines( $html, $data, $url ) {
       
   720 		if ( false === strpos( $html, "\n" ) ) {
       
   721 			return $html;
       
   722 		}
       
   723 
       
   724 		$count     = 1;
       
   725 		$found     = array();
       
   726 		$token     = '__PRE__';
       
   727 		$search    = array( "\t", "\n", "\r", ' ' );
       
   728 		$replace   = array( '__TAB__', '__NL__', '__CR__', '__SPACE__' );
       
   729 		$tokenized = str_replace( $search, $replace, $html );
       
   730 
       
   731 		preg_match_all( '#(<pre[^>]*>.+?</pre>)#i', $tokenized, $matches, PREG_SET_ORDER );
       
   732 		foreach ( $matches as $i => $match ) {
       
   733 			$tag_html  = str_replace( $replace, $search, $match[0] );
       
   734 			$tag_token = $token . $i;
       
   735 
       
   736 			$found[ $tag_token ] = $tag_html;
       
   737 			$html                = str_replace( $tag_html, $tag_token, $html, $count );
       
   738 		}
       
   739 
       
   740 		$replaced = str_replace( $replace, $search, $html );
       
   741 		$stripped = str_replace( array( "\r\n", "\n" ), '', $replaced );
       
   742 		$pre      = array_values( $found );
       
   743 		$tokens   = array_keys( $found );
       
   744 
       
   745 		return str_replace( $tokens, $pre, $stripped );
       
   746 	}
       
   747 }