WP_Embed::unregister_handler()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * API for easily embedding rich media such as videos and images into content.
4
 *
5
 * @package WordPress
6
 * @subpackage Embed
7
 * @since 2.9.0
8
 */
9
class WP_Embed {
10
	public $handlers = array();
11
	public $post_ID;
12
	public $usecache = true;
13
	public $linkifunknown = true;
14
	public $last_attr = array();
15
	public $last_url = '';
16
17
	/**
18
	 * When a URL cannot be embedded, return false instead of returning a link
19
	 * or the URL.
20
	 *
21
	 * Bypasses the {@see 'embed_maybe_make_link'} filter.
22
	 *
23
	 * @access public
24
	 * @var bool
25
	 */
26
	public $return_false_on_fail = false;
27
28
	/**
29
	 * Constructor
30
	 */
31
	public function __construct() {
32
		// Hack to get the [embed] shortcode to run before wpautop()
33
		add_filter( 'the_content', array( $this, 'run_shortcode' ), 8 );
34
35
		// Shortcode placeholder for strip_shortcodes()
36
		add_shortcode( 'embed', '__return_false' );
37
38
		// Attempts to embed all URLs in a post
39
		add_filter( 'the_content', array( $this, 'autoembed' ), 8 );
40
41
		// After a post is saved, cache oEmbed items via Ajax
42
		add_action( 'edit_form_advanced', array( $this, 'maybe_run_ajax_cache' ) );
43
		add_action( 'edit_page_form', array( $this, 'maybe_run_ajax_cache' ) );
44
	}
45
46
	/**
47
	 * Process the [embed] shortcode.
48
	 *
49
	 * Since the [embed] shortcode needs to be run earlier than other shortcodes,
50
	 * this function removes all existing shortcodes, registers the [embed] shortcode,
51
	 * calls do_shortcode(), and then re-registers the old shortcodes.
52
	 *
53
	 * @global array $shortcode_tags
54
	 *
55
	 * @param string $content Content to parse
56
	 * @return string Content with shortcode parsed
57
	 */
58
	public function run_shortcode( $content ) {
59
		global $shortcode_tags;
60
61
		// Back up current registered shortcodes and clear them all out
62
		$orig_shortcode_tags = $shortcode_tags;
63
		remove_all_shortcodes();
64
65
		add_shortcode( 'embed', array( $this, 'shortcode' ) );
66
67
		// Do the shortcode (only the [embed] one is registered)
68
		$content = do_shortcode( $content, true );
69
70
		// Put the original shortcodes back
71
		$shortcode_tags = $orig_shortcode_tags;
72
73
		return $content;
74
	}
75
76
	/**
77
	 * If a post/page was saved, then output JavaScript to make
78
	 * an Ajax request that will call WP_Embed::cache_oembed().
79
	 */
80
	public function maybe_run_ajax_cache() {
81
		$post = get_post();
82
83
		if ( ! $post || empty( $_GET['message'] ) )
84
			return;
85
86
?>
87
<script type="text/javascript">
88
	jQuery(document).ready(function($){
89
		$.get("<?php echo admin_url( 'admin-ajax.php?action=oembed-cache&post=' . $post->ID, 'relative' ); ?>");
90
	});
91
</script>
92
<?php
93
	}
94
95
	/**
96
	 * Registers an embed handler.
97
	 *
98
	 * Do not use this function directly, use wp_embed_register_handler() instead.
99
	 *
100
	 * This function should probably also only be used for sites that do not support oEmbed.
101
	 *
102
	 * @param string $id An internal ID/name for the handler. Needs to be unique.
103
	 * @param string $regex The regex that will be used to see if this handler should be used for a URL.
104
	 * @param callable $callback The callback function that will be called if the regex is matched.
105
	 * @param int $priority Optional. Used to specify the order in which the registered handlers will be tested (default: 10). Lower numbers correspond with earlier testing, and handlers with the same priority are tested in the order in which they were added to the action.
106
	 */
107
	public function register_handler( $id, $regex, $callback, $priority = 10 ) {
108
		$this->handlers[$priority][$id] = array(
109
			'regex'    => $regex,
110
			'callback' => $callback,
111
		);
112
	}
113
114
	/**
115
	 * Unregisters a previously-registered embed handler.
116
	 *
117
	 * Do not use this function directly, use wp_embed_unregister_handler() instead.
118
	 *
119
	 * @param string $id The handler ID that should be removed.
120
	 * @param int $priority Optional. The priority of the handler to be removed (default: 10).
121
	 */
122
	public function unregister_handler( $id, $priority = 10 ) {
123
		unset( $this->handlers[ $priority ][ $id ] );
124
	}
125
126
	/**
127
	 * The do_shortcode() callback function.
128
	 *
129
	 * Attempts to convert a URL into embed HTML. Starts by checking the URL against the regex of
130
	 * the registered embed handlers. If none of the regex matches and it's enabled, then the URL
131
	 * will be given to the WP_oEmbed class.
132
	 *
133
	 * @param array $attr {
134
	 *     Shortcode attributes. Optional.
135
	 *
136
	 *     @type int $width  Width of the embed in pixels.
137
	 *     @type int $height Height of the embed in pixels.
138
	 * }
139
	 * @param string $url The URL attempting to be embedded.
140
	 * @return string|false The embed HTML on success, otherwise the original URL.
141
	 *                      `->maybe_make_link()` can return false on failure.
142
	 */
143
	public function shortcode( $attr, $url = '' ) {
144
		$post = get_post();
145
146
		if ( empty( $url ) && ! empty( $attr['src'] ) ) {
147
			$url = $attr['src'];
148
		}
149
150
		$this->last_url = $url;
151
152
		if ( empty( $url ) ) {
153
			$this->last_attr = $attr;
154
			return '';
155
		}
156
157
		$rawattr = $attr;
158
		$attr = wp_parse_args( $attr, wp_embed_defaults( $url ) );
159
160
		$this->last_attr = $attr;
161
162
		// kses converts & into &amp; and we need to undo this
163
		// See https://core.trac.wordpress.org/ticket/11311
164
		$url = str_replace( '&amp;', '&', $url );
165
166
		// Look for known internal handlers
167
		ksort( $this->handlers );
168
		foreach ( $this->handlers as $priority => $handlers ) {
169
			foreach ( $handlers as $id => $handler ) {
170
				if ( preg_match( $handler['regex'], $url, $matches ) && is_callable( $handler['callback'] ) ) {
171
					if ( false !== $return = call_user_func( $handler['callback'], $matches, $attr, $url, $rawattr ) )
172
						/**
173
						 * Filters the returned embed handler.
174
						 *
175
						 * @since 2.9.0
176
						 *
177
						 * @see WP_Embed::shortcode()
178
						 *
179
						 * @param mixed  $return The shortcode callback function to call.
180
						 * @param string $url    The attempted embed URL.
181
						 * @param array  $attr   An array of shortcode attributes.
182
						 */
183
						return apply_filters( 'embed_handler_html', $return, $url, $attr );
184
				}
185
			}
186
		}
187
188
		$post_ID = ( ! empty( $post->ID ) ) ? $post->ID : null;
189
		if ( ! empty( $this->post_ID ) ) // Potentially set by WP_Embed::cache_oembed()
190
			$post_ID = $this->post_ID;
191
192
		// Unknown URL format. Let oEmbed have a go.
193
		if ( $post_ID ) {
194
195
			// Check for a cached result (stored in the post meta)
196
			$key_suffix = md5( $url . serialize( $attr ) );
197
			$cachekey = '_oembed_' . $key_suffix;
198
			$cachekey_time = '_oembed_time_' . $key_suffix;
199
200
			/**
201
			 * Filters the oEmbed TTL value (time to live).
202
			 *
203
			 * @since 4.0.0
204
			 *
205
			 * @param int    $time    Time to live (in seconds).
206
			 * @param string $url     The attempted embed URL.
207
			 * @param array  $attr    An array of shortcode attributes.
208
			 * @param int    $post_ID Post ID.
209
			 */
210
			$ttl = apply_filters( 'oembed_ttl', DAY_IN_SECONDS, $url, $attr, $post_ID );
211
212
			$cache = get_post_meta( $post_ID, $cachekey, true );
213
			$cache_time = get_post_meta( $post_ID, $cachekey_time, true );
214
215
			if ( ! $cache_time ) {
216
				$cache_time = 0;
217
			}
218
219
			$cached_recently = ( time() - $cache_time ) < $ttl;
220
221
			if ( $this->usecache || $cached_recently ) {
222
				// Failures are cached. Serve one if we're using the cache.
223
				if ( '{{unknown}}' === $cache )
224
					return $this->maybe_make_link( $url );
225
226
				if ( ! empty( $cache ) ) {
227
					/**
228
					 * Filters the cached oEmbed HTML.
229
					 *
230
					 * @since 2.9.0
231
					 *
232
					 * @see WP_Embed::shortcode()
233
					 *
234
					 * @param mixed  $cache   The cached HTML result, stored in post meta.
235
					 * @param string $url     The attempted embed URL.
236
					 * @param array  $attr    An array of shortcode attributes.
237
					 * @param int    $post_ID Post ID.
238
					 */
239
					return apply_filters( 'embed_oembed_html', $cache, $url, $attr, $post_ID );
240
				}
241
			}
242
243
			/**
244
			 * Filters whether to inspect the given URL for discoverable link tags.
245
			 *
246
			 * @since 2.9.0
247
			 * @since 4.4.0 The default value changed to true.
248
			 *
249
			 * @see WP_oEmbed::discover()
250
			 *
251
			 * @param bool $enable Whether to enable `<link>` tag discovery. Default true.
252
			 */
253
			$attr['discover'] = ( apply_filters( 'embed_oembed_discover', true ) );
254
255
			// Use oEmbed to get the HTML
256
			$html = wp_oembed_get( $url, $attr );
257
258
			// Maybe cache the result
259
			if ( $html ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $html of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
260
				update_post_meta( $post_ID, $cachekey, $html );
261
				update_post_meta( $post_ID, $cachekey_time, time() );
262
			} elseif ( ! $cache ) {
263
				update_post_meta( $post_ID, $cachekey, '{{unknown}}' );
264
			}
265
266
			// If there was a result, return it
267
			if ( $html ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $html of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
268
				/** This filter is documented in wp-includes/class-wp-embed.php */
269
				return apply_filters( 'embed_oembed_html', $html, $url, $attr, $post_ID );
270
			}
271
		}
272
273
		// Still unknown
274
		return $this->maybe_make_link( $url );
275
	}
276
277
	/**
278
	 * Delete all oEmbed caches. Unused by core as of 4.0.0.
279
	 *
280
	 * @param int $post_ID Post ID to delete the caches for.
281
	 */
282
	public function delete_oembed_caches( $post_ID ) {
283
		$post_metas = get_post_custom_keys( $post_ID );
284
		if ( empty($post_metas) )
285
			return;
286
287
		foreach ( $post_metas as $post_meta_key ) {
288
			if ( '_oembed_' == substr( $post_meta_key, 0, 8 ) )
289
				delete_post_meta( $post_ID, $post_meta_key );
290
		}
291
	}
292
293
	/**
294
	 * Triggers a caching of all oEmbed results.
295
	 *
296
	 * @param int $post_ID Post ID to do the caching for.
297
	 */
298
	public function cache_oembed( $post_ID ) {
299
		$post = get_post( $post_ID );
300
301
		$post_types = get_post_types( array( 'show_ui' => true ) );
302
		/**
303
		 * Filters the array of post types to cache oEmbed results for.
304
		 *
305
		 * @since 2.9.0
306
		 *
307
		 * @param array $post_types Array of post types to cache oEmbed results for. Defaults to post types with `show_ui` set to true.
308
		 */
309
		if ( empty( $post->ID ) || ! in_array( $post->post_type, apply_filters( 'embed_cache_oembed_types', $post_types ) ) ){
310
			return;
311
		}
312
313
		// Trigger a caching
314
		if ( ! empty( $post->post_content ) ) {
315
			$this->post_ID = $post->ID;
316
			$this->usecache = false;
317
318
			$content = $this->run_shortcode( $post->post_content );
319
			$this->autoembed( $content );
320
321
			$this->usecache = true;
322
		}
323
	}
324
325
	/**
326
	 * Passes any unlinked URLs that are on their own line to WP_Embed::shortcode() for potential embedding.
327
	 *
328
	 * @see WP_Embed::autoembed_callback()
329
	 *
330
	 * @param string $content The content to be searched.
331
	 * @return string Potentially modified $content.
332
	 */
333
	public function autoembed( $content ) {
334
		// Replace line breaks from all HTML elements with placeholders.
335
		$content = wp_replace_in_html_tags( $content, array( "\n" => '<!-- wp-line-break -->' ) );
336
337
		if ( preg_match( '#(^|\s|>)https?://#i', $content ) ) {
338
			// Find URLs on their own line.
339
			$content = preg_replace_callback( '|^(\s*)(https?://[^\s<>"]+)(\s*)$|im', array( $this, 'autoembed_callback' ), $content );
340
			// Find URLs in their own paragraph.
341
			$content = preg_replace_callback( '|(<p(?: [^>]*)?>\s*)(https?://[^\s<>"]+)(\s*<\/p>)|i', array( $this, 'autoembed_callback' ), $content );
342
		}
343
344
		// Put the line breaks back.
345
		return str_replace( '<!-- wp-line-break -->', "\n", $content );
346
	}
347
348
	/**
349
	 * Callback function for WP_Embed::autoembed().
350
	 *
351
	 * @param array $match A regex match array.
352
	 * @return string The embed HTML on success, otherwise the original URL.
353
	 */
354
	public function autoembed_callback( $match ) {
355
		$oldval = $this->linkifunknown;
356
		$this->linkifunknown = false;
357
		$return = $this->shortcode( array(), $match[2] );
358
		$this->linkifunknown = $oldval;
359
360
		return $match[1] . $return . $match[3];
361
	}
362
363
	/**
364
	 * Conditionally makes a hyperlink based on an internal class variable.
365
	 *
366
	 * @param string $url URL to potentially be linked.
367
	 * @return false|string Linked URL or the original URL. False if 'return_false_on_fail' is true.
368
	 */
369
	public function maybe_make_link( $url ) {
370
		if ( $this->return_false_on_fail ) {
371
			return false;
372
		}
373
374
		$output = ( $this->linkifunknown ) ? '<a href="' . esc_url($url) . '">' . esc_html($url) . '</a>' : $url;
375
376
		/**
377
		 * Filters the returned, maybe-linked embed URL.
378
		 *
379
		 * @since 2.9.0
380
		 *
381
		 * @param string $output The linked or original URL.
382
		 * @param string $url    The original URL.
383
		 */
384
		return apply_filters( 'embed_maybe_make_link', $output, $url );
385
	}
386
}
387