Plugin Directory

source: featured-video-plus/trunk/php/class-oembed.php @ 1457131

Last change on this file since 1457131 was 1457131, checked in by a.hoereth, 8 years ago

Publishing version 2.2.3

File size: 13.3 KB
Line 
1<?php
2/**
3 * Class providing additional functionality to WordPress' native oEmbed
4 * functionality.
5 *
6 * @link http://codex.wordpress.org/oEmbed
7 * @link http://oembed.com/ oEmbed Homepage
8 * @link https://github.com/WordPress/WordPress/tree/master/wp-includes/class-oembed.php
9 *
10 * @package Featured Video Plus
11 * @subpackage oEmbed
12 * @since 2.0.0
13 */
14class FVP_oEmbed {
15        private $oembed;
16
17        public function __construct() {
18                // Does not extend oEmbed in order to not initialize it a second time.
19                require_once( ABSPATH . '/' . WPINC . '/class-oembed.php' );
20                $this->oembed = _wp_oembed_get_object();
21
22                add_filter(
23                        'oembed_fetch_url', array( $this, 'additional_arguments' ), 10, 3
24                );
25        }
26
27
28        /**
29         * Call methods from 'oembed' class if they don't exist here.
30         *
31         * @param  {string} $method
32         * @param  {array}  $args
33         * @return {}               Whatever the other method returns.
34         */
35        public function __call( $method, $args ) {
36                return call_user_func_array( array( $this->oembed, $method ), $args );
37        }
38
39
40        /**
41         * Utilizes the WordPress oembed class for fetching the oembed info object.
42         *
43         * @see   http://oembed.com/
44         * @since 2.0.0
45         */
46        public function request( $url ) {
47                // fetch the oEmbed data with some arbitrary big size to get the biggest
48                // thumbnail possible.
49                $raw = $this->oembed->fetch(
50                        $this->get_provider( $url ),
51                        $url,
52                        array(
53                                'width'  => 4096,
54                                'height' => 4096,
55                        )
56                );
57
58                return ! empty( $raw ) ? $raw : false;
59        }
60
61
62        /**
63         * The do-it-all function that takes a URL and attempts to return the HTML.
64         *
65         * @see WP_oEmbed::get_html()
66         *
67         * @param {string} $url The URL to the content that should be embedded.
68         * @param {array}  $args Optional arguments. Usually passed from a shortcode.
69         * @return {false/string} False on failure, other the embed HTML.
70         */
71        public function get_html( $url, $args = array(), $provider = null ) {
72                if ( $provider ) {
73                        $args = $this->filter_legal_args( $provider, $args );
74                }
75
76                $html = $this->oembed->get_html( $url, $args );
77
78                if ( empty( $provider ) ) {
79                        return $html;
80                }
81
82                // Some providers do not provide it's player API to oEmbed requests,
83                // therefore the plugin needs to manually interfere with their iframe's
84                // source URL..
85                switch ( $provider ) {
86
87                        // YouTube.com
88                        case 'youtube':
89                                // Add `origin` paramter.
90                                $args['origin'] = urlencode( get_bloginfo( 'url' ) );
91
92                                // The loop parameter does not work with single videos if there is no
93                                // playlist defined. Workaround: Set playlist to video itself.
94                                // @see https://developers.google.com/youtube/player_parameters#loop-supported-players
95                                if ( ! empty( $args['loop'] ) && $args['loop'] ) {
96                                        $args['playlist'] = $this->get_youtube_video_id( $url );
97                                }
98
99                                // Remove fullscreen button manually because the YouTube API
100                                // does not care about `&fs=0`.
101                                if ( array_key_exists( 'fs', $args ) && $args['fs'] == 0 ) {
102                                        $html = str_replace( 'allowfullscreen', '', $html );
103                                }
104
105                                // We strip the 'feature=oembed' from the parameters because it breaks
106                                // some parameters.
107                                $hook = '?feature=oembed';
108                                $html = str_replace( $hook, '', $html );
109                                break;
110
111                        // DailyMotion.com
112                        case 'dailymotion':
113                                $args = $this->translate_time_arg( $args );
114                                break;
115                }
116
117                if ( ! empty( $args ) ) {
118                        $pattern = "/src=([\"'])([^\"']*)[\"']/";
119                        preg_match( $pattern, $html, $match );
120                        if ( ! empty( $match[1] ) && ! empty( $match[2] ) ) {
121                                $code = $this->clean_url( $match[2] );
122                                $replace = sprintf( 'src=$1%s$1', add_query_arg( $args, $code ) );
123                                $html = preg_replace( $pattern, $replace, $html );
124                        }
125                }
126
127                return $html;
128        }
129
130
131        /**
132         * Cleans up ? and & in urls such that before all & there is a single ?
133         * with none following.
134         *
135         * @see wordpress.org/support/topic/fix-for-wordpress-442-for-youtube-video-error
136         *
137         * @param {string} $urls
138         * @return {string}
139         */
140        public function clean_url($url) {
141                $url = preg_replace( '/\?/', '&', $url, 1);
142                return preg_replace( '/&/',  '?', $url, 1);
143        }
144
145
146        /**
147         * Enable additional parameters for oEmbed requests from inside the plugin.
148         * The plugin only allows a limited set of parameters.
149         *
150         * @see    https://core.trac.wordpress.org/ticket/16996#comment:18
151         * @param  {string} $provider The oEmbed provider (as URL)
152         * @param  {string} $url      The oEmbed request URL
153         * @param  {assoc}  $args     The additional parameters for the provider
154         * @return {string}           $provider with added parameters.
155         */
156        public function additional_arguments( $provider, $url, $args ) {
157                unset(
158                        $args['width'],
159                        $args['height'],
160                        $args['discover']
161                );
162
163                return add_query_arg( $args, $provider );;
164        }
165
166
167        /**
168         * Extracts the arguments (query and fragment) from a given URL and filters
169         * them to only contain arguments legal for the given provider.
170         *
171         * @since  2.0.0
172         *
173         * @param  {string} $url           oEmbed request URL
174         * @param  {string} $provider      Provider name (optional)
175         * @param  {bool}   $filter_legals If the retrieved arguments should be
176         *                                 filtered to only contain legals
177         * @return {assoc}  Associative array containing the arguments as key value
178         *                  pairs
179         */
180        public function get_args( $url, $provider = null, $filter_legals = true ) {
181                $args = $this->parse_url_args( $url );
182
183                if ( $filter_legals ) {
184                        $provider = empty( $provider ) ?
185                                $this->get_provider_name( $url ) :
186                                $provider;
187
188                        $args = $this->filter_legal_args( $provider, $args );
189                }
190
191                return $args;
192        }
193
194
195        /**
196         * Extracts the provider name in lowercase from a given URL.
197         *
198         * @since  2.0.0
199         * @param  {string} $url oEmbed request URL
200         * @return {string}      Provider name
201         */
202        public function get_provider_name( $url ) {
203                $host = parse_url( $url, PHP_URL_HOST );
204
205                $tlds_set = array(
206                        'com',
207                        'net',
208                        'tv',
209                );
210
211                $tlds = '(?:' . implode( ')|(?:', $tlds_set ) . ')';
212                $pattern = '/(?:www\.)?(.*)\.' . $tlds  . '/';
213
214                preg_match( $pattern , $host, $match );
215
216                if ( ! empty( $match[1] ) ) {
217                        return strtolower( $match[1] );
218                }
219
220                return false;
221        }
222
223
224        /**
225         * Takes a URL and returns the corresponding oEmbed provider's URL, if there
226         * is one.
227         *
228         * Backport from WordPress 4.0.0 to make this method available for earlier
229         * WordPress releases.
230         * @see https://github.com/WordPress/WordPress/blob/ed4aafa6929b36dc1d06708831a7cef258c16b54/wp-includes/class-oembed.php#L188-L226
231         *
232         * @param string        $url  The URL to the content.
233         * @param string|array  $args Optional provider arguments.
234         * @return false|string False on failure, otherwise the oEmbed provider URL.
235         */
236        public function get_provider( $url, $args = '' ) {
237                // If the WordPress native oembed class has the get_provider function,
238                // use that one.
239                if ( method_exists( $this->oembed, 'get_provider' ) ) {
240                        return $this->oembed->get_provider( $url, $args );
241                }
242
243                $args = wp_parse_args( $args );
244
245                $provider = false;
246                if ( ! isset( $args['discover'] ) ) {
247                        $args['discover'] = true;
248                }
249
250                foreach ( $this->oembed->providers AS $matchmask => $data ) {
251                        list( $providerurl, $regex ) = $data;
252
253                        // Turn the asterisk-type provider URLs into regex
254                        if ( ! $regex ) {
255                                $matchmask = '#' . str_replace(
256                                        '___wildcard___',
257                                        '(.+)',
258                                        preg_quote( str_replace( '*', '___wildcard___', $matchmask ), '#' )
259                                ) . '#i';
260                                $matchmask = preg_replace(
261                                        '|^#http\\\://|', '#https?\://', $matchmask
262                                );
263                        }
264
265                        if ( preg_match( $matchmask, $url ) ) {
266                                // JSON is easier to deal with than XML
267                                $provider = str_replace( '{format}', 'json', $providerurl );
268                                break;
269                        }
270                }
271
272                if ( ! $provider && $args['discover'] ) {
273                        $provider = $this->oembed->discover( $url );
274                }
275
276                return $provider;
277        }
278
279
280        /**
281         * Get video id from url.
282         *
283         * @param  {string} $url
284         * @return {string/bool} Video ID or false on error.
285         */
286        public function get_video_id( $url ) {
287                if ( empty( $url ) ) {
288                        return false;
289                }
290
291                $provider = $this->get_provider_name( $url );
292                if ( empty( $provider ) ) {
293                        return false;
294                }
295
296                switch ( $provider ) {
297        ��               case 'dailymotion':
298                                return strtok( basename( $url ), '_' );
299                                break;
300                }
301
302                return false;
303        }
304
305
306        /**
307         * Get video thumbnail url by provider and video id.
308         *
309         * @param  {string} $provider
310         * @param  {string} $id
311         * @return {string/bool} Video URL or false on error.
312         */
313        public function get_thumbnail_url( $provider, $id ) {
314                static $thumbnail_apis = array(
315                        'dailymotion' =>
316                                'https://api.dailymotion.com/video/%s?fields=thumbnail_url,poster_url',
317                );
318
319                if ( empty( $provider ) || empty( $id ) ) {
320                        return false;
321                }
322
323                $result = @file_get_contents( sprintf( $thumbnail_apis[$provider], $id ) );
324                if ( ! empty( $result ) ) {
325                        switch ( $provider ) {
326                                case 'dailymotion':
327                                        $data = json_decode( $result, true );
328                                        return ! empty( $data['thumbnail_url'] ) ?
329                                                $data['thumbnail_url'] : $data['poster_url'];
330                                        break;
331                        }
332                }
333
334                return false;
335        }
336
337
338        /**
339         * Only keeps key value pairs of the source $array if their keys are listed
340         * in the $filter array.
341         *
342         * @since  2.0.0
343         *
344         * @param  {assoc} $array  Associative array to filter
345         * @param  {array} $filter Enumerated array containing the legal keys to keep
346         * @return {assoc} Filtered array
347         */
348        private function filter_legal_args( $provider, $args ) {
349                $legals = array(
350
351                        // YouTube.com
352                        // https://developers.google.com/youtube/player_parameters
353                        'youtube' => array(
354                                'autohide',
355                                'autoplay',
356                                'cc_load_policy',
357                                'color',
358                                'controls',
359                                'disablekb',
360                                'enablejsapi',
361                                'end',
362                                'fs',
363                                'hl',
364                                'iv_load_policy',
365                                'list',
366                                'listType',
367                                'loop',
368                                'modestbranding',
369                                'origin',
370                                'playerapiid',
371                                'playlist',
372                                'playsinline',
373                                'rel',
374                                'showinfo',
375                                'start',
376                                'theme',
377                        ),
378
379                        // Vimeo.com
380                        // http://developer.vimeo.com/apis/oembed
381                        'vimeo' => array(
382                                'byline',
383                                'title',
384                                'portrait',
385                                'color',
386                                'autoplay',
387                                'autopause',
388                                'loop',
389                                'api',
390                                'player_id',
391                        ),
392
393                        // DailyMotion.com
394                        // http://www.dailymotion.com/doc/api/player.html
395                        'dailymotion' => array(
396                                'wmode',
397                                'autoplay',
398                                'api',
399                                'background',
400                                'chromeless',
401                                'controls',
402                                'foreground',
403                                'highlight',
404                                'html',
405                                'id',
406                                'info',
407                                'logo',
408                                'network',
409                                'quality',
410                                'related',
411                                'startscreen',
412                                'start',
413                                't',
414                                'syndication',
415                        ),
416
417                        // SoundCloud.com
418                        // https://developers.soundcloud.com/docs/oembed
419                        'soundcloud' => array(
420                                'auto_play',
421                                'auto_play' => 'autoplay', // map autoplay to auto_play
422                                'color',
423                                'show_comments',
424                        ),
425                );
426
427                $result = array();
428
429                if ( ! empty( $legals[ $provider ] ) ) {
430                        foreach ( $legals[ $provider ] AS $key => $value ) {
431                                if ( array_key_exists( $value, $args ) &&
432                                     ! is_null( $args[ $value ] )
433                                ) {
434                                        $key = is_numeric( $key ) ? $value : $key;
435                                        $result[ $key ] = urlencode( $args[ $value ] );
436                                }
437                        }
438                }
439
440                return $result;
441        }
442
443
444        /**
445         * Function used for retrieving query (?..&..) and fragment (#..) arguments
446         * of a given URL.
447         *
448         * @see    http://php.net/manual/en/function.parse-url.php
449         * @see    http://php.net/manual/en/function.parse-str.php
450         * @since  2.0.0
451         *
452         * @param  {string} $url the URL to parse for arguments
453         * @return array containing query and fragment arguments
454         */
455        private function parse_url_args( $url ) {
456                // parse query
457                $query = parse_url( $url, PHP_URL_QUERY );
458                $query_args = array();
459                parse_str( $query, $query_args );
460
461                // parse fragment
462                $fragment = parse_url( $url, PHP_URL_FRAGMENT );
463                $fragment_args = array();
464                parse_str( $fragment, $fragment_args );
465
466                // merge query and fragment args
467                $args = array_merge(
468                        $query_args,
469                        $fragment_args
470                );
471
472                return $args;
473        }
474
475
476        /**
477         * Calculates the amount of seconds depicted by a string structured like one
478         * of the following possibilities:
479         *   ##m##s
480         *   ##m
481         *   ##s
482         *   ##
483         *
484         * @since  2.0.0
485         *
486         * @param  {string} $t
487         * @return {int}    seconds
488         */
489        private function handle_m_s_string( $t ) {
490                $seconds = 0;
491
492                preg_match( '/(\d+)m/', $t, $m );
493                if ( ! empty( $m[1] ) ) {
494                        $seconds += $m[1] * 60;
495                }
496
497                preg_match( '/(\d+)s/', $t, $s );
498                if ( ! empty( $s[1] ) ) {
499                        $seconds += $s[1];
500                }
501
502                if ( empty( $m[1] ) && empty( $s[1] ) ) {
503                        $seconds += intval( $t );
504                }
505
506                return $seconds;
507        }
508
509
510        /**
511         * Translates a source time parameter (mostly given as 't' in the
512         * fragment (#..) of an URL in seconds to a destination parameter.
513         *
514         * Note: The source parameter overwrites the destination parameter!
515         *
516         * @since  2.0.0
517         *
518         * @param  {array}  $parameters Array of parameters, containing $src
519         * @param  {string} $src        Key of the source parameter
520         * @param  {string} $dst        Key of the destination parameter
521         * @return {array}  Updated $parameters array
522         */
523        private function translate_time_arg( $args, $src = 't', $dst = 'start' ) {
524                if ( ! empty( $args[ $src ] ) ) {
525                        $t = $this->handle_m_s_string( $args[ $src ] );
526
527                        unset( $args[ $src ] );
528
529                        if ( $t ) {
530                                $args[ $dst ] = $t;
531                        }
532                }
533
534                return $args;
535        }
536
537
538        /**
539         * Extract the YouTube Video ID from an URL.
540         *
541         * @see http://stackoverflow.com/a/6382259/1447384
542         *
543         * @param  {string} $url
544         * @return {string} YouTube ID
545         */
546        private function get_youtube_video_id( $url ) {
547                $pattern =
548                        '/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)'.
549                        '|youtu\.be\/)([^"&?\/ ]{11})/i';
550
551                if ( preg_match( $pattern, $url, $match ) ) {
552                        return $match[1];
553                }
554
555                return false;
556        }
557}
Note: See TracBrowser for help on using the repository browser.