Make WordPress Core

source: trunk/src/wp-includes/feed.php @ 42343

Last change on this file since 42343 was 42343, checked in by pento, 4 years ago

Code is Poetry.
WordPress' code just... wasn't.
This is now dealt with.

Props jrf, pento, netweb, GaryJ, jdgrimes, westonruter, Greg Sherwood from PHPCS, and everyone who's ever contributed to WPCS and PHPCS.
Fixes #41057.

  • Property svn:eol-style set to native
File size: 19.3 KB
Line 
1<?php
2/**
3 * WordPress Feed API
4 *
5 * Many of the functions used in here belong in The Loop, or The Loop for the
6 * Feeds.
7 *
8 * @package WordPress
9 * @subpackage Feed
10 * @since 2.1.0
11 */
12
13/**
14 * RSS container for the bloginfo function.
15 *
16 * You can retrieve anything that you can using the get_bloginfo() function.
17 * Everything will be stripped of tags and characters converted, when the values
18 * are retrieved for use in the feeds.
19 *
20 * @since 1.5.1
21 * @see get_bloginfo() For the list of possible values to display.
22 *
23 * @param string $show See get_bloginfo() for possible values.
24 * @return string
25 */
26function get_bloginfo_rss( $show = '' ) {
27        $info = strip_tags( get_bloginfo( $show ) );
28        /**
29         * Filters the bloginfo for use in RSS feeds.
30         *
31         * @since 2.2.0
32         *
33         * @see convert_chars()
34         * @see get_bloginfo()
35         *
36         * @param string $info Converted string value of the blog information.
37         * @param string $show The type of blog information to retrieve.
38         */
39        return apply_filters( 'get_bloginfo_rss', convert_chars( $info ), $show );
40}
41
42/**
43 * Display RSS container for the bloginfo function.
44 *
45 * You can retrieve anything that you can using the get_bloginfo() function.
46 * Everything will be stripped of tags and characters converted, when the values
47 * are retrieved for use in the feeds.
48 *
49 * @since 0.71
50 * @see get_bloginfo() For the list of possible values to display.
51 *
52 * @param string $show See get_bloginfo() for possible values.
53 */
54function bloginfo_rss( $show = '' ) {
55        /**
56         * Filters the bloginfo for display in RSS feeds.
57         *
58         * @since 2.1.0
59         *
60         * @see get_bloginfo()
61         *
62         * @param string $rss_container RSS container for the blog information.
63         * @param string $show          The type of blog information to retrieve.
64         */
65        echo apply_filters( 'bloginfo_rss', get_bloginfo_rss( $show ), $show );
66}
67
68/**
69 * Retrieve the default feed.
70 *
71 * The default feed is 'rss2', unless a plugin changes it through the
72 * {@see 'default_feed'} filter.
73 *
74 * @since 2.5.0
75 *
76 * @return string Default feed, or for example 'rss2', 'atom', etc.
77 */
78function get_default_feed() {
79        /**
80         * Filters the default feed type.
81         *
82         * @since 2.5.0
83         *
84         * @param string $feed_type Type of default feed. Possible values include 'rss2', 'atom'.
85         *                          Default 'rss2'.
86         */
87        $default_feed = apply_filters( 'default_feed', 'rss2' );
88        return 'rss' == $default_feed ? 'rss2' : $default_feed;
89}
90
91/**
92 * Retrieve the blog title for the feed title.
93 *
94 * @since 2.2.0
95 * @since 4.4.0 The optional `$sep` parameter was deprecated and renamed to `$deprecated`.
96 *
97 * @param string $deprecated Unused..
98 * @return string The document title.
99 */
100function get_wp_title_rss( $deprecated = '&#8211;' ) {
101        if ( '&#8211;' !== $deprecated ) {
102                /* translators: %s: 'document_title_separator' filter name */
103                _deprecated_argument( __FUNCTION__, '4.4.0', sprintf( __( 'Use the %s filter instead.' ), '<code>document_title_separator</code>' ) );
104        }
105
106        /**
107         * Filters the blog title for use as the feed title.
108         *
109         * @since 2.2.0
110         * @since 4.4.0 The `$sep` parameter was deprecated and renamed to `$deprecated`.
111         *
112         * @param string $title      The current blog title.
113         * @param string $deprecated Unused.
114         */
115        return apply_filters( 'get_wp_title_rss', wp_get_document_title(), $deprecated );
116}
117
118/**
119 * Display the blog title for display of the feed title.
120 *
121 * @since 2.2.0
122 * @since 4.4.0 The optional `$sep` parameter was deprecated and renamed to `$deprecated`.
123 *
124 * @param string $deprecated Unused.
125 */
126function wp_title_rss( $deprecated = '&#8211;' ) {
127        if ( '&#8211;' !== $deprecated ) {
128                /* translators: %s: 'document_title_separator' filter name */
129                _deprecated_argument( __FUNCTION__, '4.4.0', sprintf( __( 'Use the %s filter instead.' ), '<code>document_title_separator</code>' ) );
130        }
131
132        /**
133         * Filters the blog title for display of the feed title.
134         *
135         * @since 2.2.0
136         * @since 4.4.0 The `$sep` parameter was deprecated and renamed to `$deprecated`.
137         *
138         * @see get_wp_title_rss()
139         *
140         * @param string $wp_title_rss The current blog title.
141         * @param string $deprecated   Unused.
142         */
143        echo apply_filters( 'wp_title_rss', get_wp_title_rss(), $deprecated );
144}
145
146/**
147 * Retrieve the current post title for the feed.
148 *
149 * @since 2.0.0
150 *
151 * @return string Current post title.
152 */
153function get_the_title_rss() {
154        $title = get_the_title();
155
156        /**
157         * Filters the post title for use in a feed.
158         *
159         * @since 1.2.0
160         *
161         * @param string $title The current post title.
162         */
163        $title = apply_filters( 'the_title_rss', $title );
164        return $title;
165}
166
167/**
168 * Display the post title in the feed.
169 *
170 * @since 0.71
171 */
172function the_title_rss() {
173        echo get_the_title_rss();
174}
175
176/**
177 * Retrieve the post content for feeds.
178 *
179 * @since 2.9.0
180 * @see get_the_content()
181 *
182 * @param string $feed_type The type of feed. rss2 | atom | rss | rdf
183 * @return string The filtered content.
184 */
185function get_the_content_feed( $feed_type = null ) {
186        if ( ! $feed_type ) {
187                $feed_type = get_default_feed();
188        }
189
190        /** This filter is documented in wp-includes/post-template.php */
191        $content = apply_filters( 'the_content', get_the_content() );
192        $content = str_replace( ']]>', ']]&gt;', $content );
193        /**
194         * Filters the post content for use in feeds.
195         *
196         * @since 2.9.0
197         *
198         * @param string $content   The current post content.
199         * @param string $feed_type Type of feed. Possible values include 'rss2', 'atom'.
200         *                          Default 'rss2'.
201         */
202        return apply_filters( 'the_content_feed', $content, $feed_type );
203}
204
205/**
206 * Display the post content for feeds.
207 *
208 * @since 2.9.0
209 *
210 * @param string $feed_type The type of feed. rss2 | atom | rss | rdf
211 */
212function the_content_feed( $feed_type = null ) {
213        echo get_the_content_feed( $feed_type );
214}
215
216/**
217 * Display the post excerpt for the feed.
218 *
219 * @since 0.71
220 */
221function the_excerpt_rss() {
222        $output = get_the_excerpt();
223        /**
224         * Filters the post excerpt for a feed.
225         *
226         * @since 1.2.0
227         *
228         * @param string $output The current post excerpt.
229         */
230        echo apply_filters( 'the_excerpt_rss', $output );
231}
232
233/**
234 * Display the permalink to the post for use in feeds.
235 *
236 * @since 2.3.0
237 */
238function the_permalink_rss() {
239        /**
240         * Filters the permalink to the post for use in feeds.
241         *
242         * @since 2.3.0
243         *
244         * @param string $post_permalink The current post permalink.
245         */
246        echo esc_url( apply_filters( 'the_permalink_rss', get_permalink() ) );
247}
248
249/**
250 * Outputs the link to the comments for the current post in an xml safe way
251 *
252 * @since 3.0.0
253 * @return none
254 */
255function comments_link_feed() {
256        /**
257         * Filters the comments permalink for the current post.
258         *
259         * @since 3.6.0
260         *
261         * @param string $comment_permalink The current comment permalink with
262         *                                  '#comments' appended.
263         */
264        echo esc_url( apply_filters( 'comments_link_feed', get_comments_link() ) );
265}
266
267/**
268 * Display the feed GUID for the current comment.
269 *
270 * @since 2.5.0
271 *
272 * @param int|WP_Comment $comment_id Optional comment object or id. Defaults to global comment object.
273 */
274function comment_guid( $comment_id = null ) {
275        echo esc_url( get_comment_guid( $comment_id ) );
276}
277
278/**
279 * Retrieve the feed GUID for the current comment.
280 *
281 * @since 2.5.0
282 *
283 * @param int|WP_Comment $comment_id Optional comment object or id. Defaults to global comment object.
284 * @return false|string false on failure or guid for comment on success.
285 */
286function get_comment_guid( $comment_id = null ) {
287        $comment = get_comment( $comment_id );
288
289        if ( ! is_object( $comment ) ) {
290                return false;
291        }
292
293        return get_the_guid( $comment->comment_post_ID ) . '#comment-' . $comment->comment_ID;
294}
295
296/**
297 * Display the link to the comments.
298 *
299 * @since 1.5.0
300 * @since 4.4.0 Introduced the `$comment` argument.
301 *
302 * @param int|WP_Comment $comment Optional. Comment object or id. Defaults to global comment object.
303 */
304function comment_link( $comment = null ) {
305        /**
306         * Filters the current comment's permalink.
307         *
308         * @since 3.6.0
309         *
310         * @see get_comment_link()
311         *
312         * @param string $comment_permalink The current comment permalink.
313         */
314        echo esc_url( apply_filters( 'comment_link', get_comment_link( $comment ) ) );
315}
316
317/**
318 * Retrieve the current comment author for use in the feeds.
319 *
320 * @since 2.0.0
321 *
322 * @return string Comment Author
323 */
324function get_comment_author_rss() {
325        /**
326         * Filters the current comment author for use in a feed.
327         *
328         * @since 1.5.0
329         *
330         * @see get_comment_author()
331         *
332         * @param string $comment_author The current comment author.
333         */
334        return apply_filters( 'comment_author_rss', get_comment_author() );
335}
336
337/**
338 * Display the current comment author in the feed.
339 *
340 * @since 1.0.0
341 */
342function comment_author_rss() {
343        echo get_comment_author_rss();
344}
345
346/**
347 * Display the current comment content for use in the feeds.
348 *
349 * @since 1.0.0
350 */
351function comment_text_rss() {
352        $comment_text = get_comment_text();
353        /**
354         * Filters the current comment content for use in a feed.
355         *
356         * @since 1.5.0
357         *
358         * @param string $comment_text The content of the current comment.
359         */
360        $comment_text = apply_filters( 'comment_text_rss', $comment_text );
361        echo $comment_text;
362}
363
364/**
365 * Retrieve all of the post categories, formatted for use in feeds.
366 *
367 * All of the categories for the current post in the feed loop, will be
368 * retrieved and have feed markup added, so that they can easily be added to the
369 * RSS2, Atom, or RSS1 and RSS0.91 RDF feeds.
370 *
371 * @since 2.1.0
372 *
373 * @param string $type Optional, default is the type returned by get_default_feed().
374 * @return string All of the post categories for displaying in the feed.
375 */
376function get_the_category_rss( $type = null ) {
377        if ( empty( $type ) ) {
378                $type = get_default_feed();
379        }
380        $categories = get_the_category();
381        $tags       = get_the_tags();
382        $the_list   = '';
383        $cat_names  = array();
384
385        $filter = 'rss';
386        if ( 'atom' == $type ) {
387                $filter = 'raw';
388        }
389
390        if ( ! empty( $categories ) ) {
391                foreach ( (array) $categories as $category ) {
392                        $cat_names[] = sanitize_term_field( 'name', $category->name, $category->term_id, 'category', $filter );
393                }
394        }
395
396        if ( ! empty( $tags ) ) {
397                foreach ( (array) $tags as $tag ) {
398                        $cat_names[] = sanitize_term_field( 'name', $tag->name, $tag->term_id, 'post_tag', $filter );
399                }
400        }
401
402        $cat_names = array_unique( $cat_names );
403
404        foreach ( $cat_names as $cat_name ) {
405                if ( 'rdf' == $type ) {
406                        $the_list .= "\t\t<dc:subject><![CDATA[$cat_name]]></dc:subject>\n";
407                } elseif ( 'atom' == $type ) {
408                        $the_list .= sprintf( '<category scheme="%1$s" term="%2$s" />', esc_attr( get_bloginfo_rss( 'url' ) ), esc_attr( $cat_name ) );
409                } else {
410                        $the_list .= "\t\t<category><![CDATA[" . @html_entity_decode( $cat_name, ENT_COMPAT, get_option( 'blog_charset' ) ) . "]]></category>\n";
411                }
412        }
413
414        /**
415         * Filters all of the post categories for display in a feed.
416         *
417         * @since 1.2.0
418         *
419         * @param string $the_list All of the RSS post categories.
420         * @param string $type     Type of feed. Possible values include 'rss2', 'atom'.
421         *                         Default 'rss2'.
422         */
423        return apply_filters( 'the_category_rss', $the_list, $type );
424}
425
426/**
427 * Display the post categories in the feed.
428 *
429 * @since 0.71
430 * @see get_the_category_rss() For better explanation.
431 *
432 * @param string $type Optional, default is the type returned by get_default_feed().
433 */
434function the_category_rss( $type = null ) {
435        echo get_the_category_rss( $type );
436}
437
438/**
439 * Display the HTML type based on the blog setting.
440 *
441 * The two possible values are either 'xhtml' or 'html'.
442 *
443 * @since 2.2.0
444 */
445function html_type_rss() {
446        $type = get_bloginfo( 'html_type' );
447        if ( strpos( $type, 'xhtml' ) !== false ) {
448                $type = 'xhtml';
449        } else {
450                $type = 'html';
451        }
452        echo $type;
453}
454
455/**
456 * Display the rss enclosure for the current post.
457 *
458 * Uses the global $post to check whether the post requires a password and if
459 * the user has the password for the post. If not then it will return before
460 * displaying.
461 *
462 * Also uses the function get_post_custom() to get the post's 'enclosure'
463 * metadata field and parses the value to display the enclosure(s). The
464 * enclosure(s) consist of enclosure HTML tag(s) with a URI and other
465 * attributes.
466 *
467 * @since 1.5.0
468 */
469function rss_enclosure() {
470        if ( post_password_required() ) {
471                return;
472        }
473
474        foreach ( (array) get_post_custom() as $key => $val ) {
475                if ( $key == 'enclosure' ) {
476                        foreach ( (array) $val as $enc ) {
477                                $enclosure = explode( "\n", $enc );
478
479                                // only get the first element, e.g. audio/mpeg from 'audio/mpeg mpga mp2 mp3'
480                                $t    = preg_split( '/[ \t]/', trim( $enclosure[2] ) );
481                                $type = $t[0];
482
483                                /**
484                                 * Filters the RSS enclosure HTML link tag for the current post.
485                                 *
486                                 * @since 2.2.0
487                                 *
488                                 * @param string $html_link_tag The HTML link tag with a URI and other attributes.
489                                 */
490                                echo apply_filters( 'rss_enclosure', '<enclosure url="' . esc_url( trim( $enclosure[0] ) ) . '" length="' . absint( trim( $enclosure[1] ) ) . '" type="' . esc_attr( $type ) . '" />' . "\n" );
491                        }
492                }
493        }
494}
495
496/**
497 * Display the atom enclosure for the current post.
498 *
499 * Uses the global $post to check whether the post requires a password and if
500 * the user has the password for the post. If not then it will return before
501 * displaying.
502 *
503 * Also uses the function get_post_custom() to get the post's 'enclosure'
504 * metadata field and parses the value to display the enclosure(s). The
505 * enclosure(s) consist of link HTML tag(s) with a URI and other attributes.
506 *
507 * @since 2.2.0
508 */
509function atom_enclosure() {
510        if ( post_password_required() ) {
511                return;
512        }
513
514        foreach ( (array) get_post_custom() as $key => $val ) {
515                if ( $key == 'enclosure' ) {
516                        foreach ( (array) $val as $enc ) {
517                                $enclosure = explode( "\n", $enc );
518                                /**
519                                 * Filters the atom enclosure HTML link tag for the current post.
520                                 *
521                                 * @since 2.2.0
522                                 *
523                                 * @param string $html_link_tag The HTML link tag with a URI and other attributes.
524                                 */
525                                echo apply_filters( 'atom_enclosure', '<link href="' . esc_url( trim( $enclosure[0] ) ) . '" rel="enclosure" length="' . absint( trim( $enclosure[1] ) ) . '" type="' . esc_attr( trim( $enclosure[2] ) ) . '" />' . "\n" );
526                        }
527                }
528        }
529}
530
531/**
532 * Determine the type of a string of data with the data formatted.
533 *
534 * Tell whether the type is text, html, or xhtml, per RFC 4287 section 3.1.
535 *
536 * In the case of WordPress, text is defined as containing no markup,
537 * xhtml is defined as "well formed", and html as tag soup (i.e., the rest).
538 *
539 * Container div tags are added to xhtml values, per section 3.1.1.3.
540 *
541 * @link http://www.atomenabled.org/developers/syndication/atom-format-spec.php#rfc.section.3.1
542 *
543 * @since 2.5.0
544 *
545 * @param string $data Input string
546 * @return array array(type, value)
547 */
548function prep_atom_text_construct( $data ) {
549        if ( strpos( $data, '<' ) === false && strpos( $data, '&' ) === false ) {
550                return array( 'text', $data );
551        }
552
553        if ( ! function_exists( 'xml_parser_create' ) ) {
554                trigger_error( __( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." ) );
555
556                return array( 'html', "<![CDATA[$data]]>" );
557        }
558
559        $parser = xml_parser_create();
560        xml_parse( $parser, '<div>' . $data . '</div>', true );
561        $code = xml_get_error_code( $parser );
562        xml_parser_free( $parser );
563
564        if ( ! $code ) {
565                if ( strpos( $data, '<' ) === false ) {
566                        return array( 'text', $data );
567                } else {
568                        $data = "<div xmlns='http://www.w3.org/1999/xhtml'>$data</div>";
569                        return array( 'xhtml', $data );
570                }
571        }
572
573        if ( strpos( $data, ']]>' ) === false ) {
574                return array( 'html', "<![CDATA[$data]]>" );
575        } else {
576                return array( 'html', htmlspecialchars( $data ) );
577        }
578}
579
580/**
581 * Displays Site Icon in atom feeds.
582 *
583 * @since 4.3.0
584 *
585 * @see get_site_icon_url()
586 */
587function atom_site_icon() {
588        $url = get_site_icon_url( 32 );
589        if ( $url ) {
590                echo "<icon>$url</icon>\n";
591        }
592}
593
594/**
595 * Displays Site Icon in RSS2.
596 *
597 * @since 4.3.0
598 */
599function rss2_site_icon() {
600        $rss_title = get_wp_title_rss();
601        if ( empty( $rss_title ) ) {
602                $rss_title = get_bloginfo_rss( 'name' );
603        }
604
605        $url = get_site_icon_url( 32 );
606        if ( $url ) {
607                echo '
608<image>
609        <url>' . convert_chars( $url ) . '</url>
610        <title>' . $rss_title . '</title>
611        <link>' . get_bloginfo_rss( 'url' ) . '</link>
612        <width>32</width>
613        <height>32</height>
614</image> ' . "\n";
615        }
616}
617
618/**
619 * Display the link for the currently displayed feed in a XSS safe way.
620 *
621 * Generate a correct link for the atom:self element.
622 *
623 * @since 2.5.0
624 */
625function self_link() {
626        $host = @parse_url( home_url() );
627        /**
628         * Filters the current feed URL.
629         *
630         * @since 3.6.0
631         *
632         * @see set_url_scheme()
633         * @see wp_unslash()
634         *
635         * @param string $feed_link The link for the feed with set URL scheme.
636         */
637        echo esc_url( apply_filters( 'self_link', set_url_scheme( 'http://' . $host['host'] . wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) );
638}
639
640/**
641 * Return the content type for specified feed type.
642 *
643 * @since 2.8.0
644 *
645 * @param string $type Type of feed. Possible values include 'rss', rss2', 'atom', and 'rdf'.
646 */
647function feed_content_type( $type = '' ) {
648        if ( empty( $type ) ) {
649                $type = get_default_feed();
650        }
651
652        $types = array(
653                'rss'      => 'application/rss+xml',
654                'rss2'     => 'application/rss+xml',
655                'rss-http' => 'text/xml',
656                'atom'     => 'application/atom+xml',
657                'rdf'      => 'application/rdf+xml',
658        );
659
660        $content_type = ( ! empty( $types[ $type ] ) ) ? $types[ $type ] : 'application/octet-stream';
661
662        /**
663         * Filters the content type for a specific feed type.
664         *
665         * @since 2.8.0
666         *
667         * @param string $content_type Content type indicating the type of data that a feed contains.
668         * @param string $type         Type of feed. Possible values include 'rss', rss2', 'atom', and 'rdf'.
669         */
670        return apply_filters( 'feed_content_type', $content_type, $type );
671}
672
673/**
674 * Build SimplePie object based on RSS or Atom feed from URL.
675 *
676 * @since 2.8.0
677 *
678 * @param mixed $url URL of feed to retrieve. If an array of URLs, the feeds are merged
679 * using SimplePie's multifeed feature.
680 * See also {@link ​http://simplepie.org/wiki/faq/typical_multifeed_gotchas}
681 *
682 * @return WP_Error|SimplePie WP_Error object on failure or SimplePie object on success
683 */
684function fetch_feed( $url ) {
685        if ( ! class_exists( 'SimplePie', false ) ) {
686                require_once( ABSPATH . WPINC . '/class-simplepie.php' );
687        }
688
689        require_once( ABSPATH . WPINC . '/class-wp-feed-cache.php' );
690        require_once( ABSPATH . WPINC . '/class-wp-feed-cache-transient.php' );
691        require_once( ABSPATH . WPINC . '/class-wp-simplepie-file.php' );
692        require_once( ABSPATH . WPINC . '/class-wp-simplepie-sanitize-kses.php' );
693
694        $feed = new SimplePie();
695
696        $feed->set_sanitize_class( 'WP_SimplePie_Sanitize_KSES' );
697        // We must manually overwrite $feed->sanitize because SimplePie's
698        // constructor sets it before we have a chance to set the sanitization class
699        $feed->sanitize = new WP_SimplePie_Sanitize_KSES();
700
701        $feed->set_cache_class( 'WP_Feed_Cache' );
702        $feed->set_file_class( 'WP_SimplePie_File' );
703
704        $feed->set_feed_url( $url );
705        /** This filter is documented in wp-includes/class-wp-feed-cache-transient.php */
706        $feed->set_cache_duration( apply_filters( 'wp_feed_cache_transient_lifetime', 12 * HOUR_IN_SECONDS, $url ) );
707        /**
708         * Fires just before processing the SimplePie feed object.
709         *
710         * @since 3.0.0
711         *
712         * @param object $feed SimplePie feed object (passed by reference).
713         * @param mixed  $url  URL of feed to retrieve. If an array of URLs, the feeds are merged.
714         */
715        do_action_ref_array( 'wp_feed_options', array( &$feed, $url ) );
716        $feed->init();
717        $feed->set_output_encoding( get_option( 'blog_charset' ) );
718
719        if ( $feed->error() ) {
720                return new WP_Error( 'simplepie-error', $feed->error() );
721        }
722
723        return $feed;
724}
Note: See TracBrowser for help on using the repository browser.