| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 * Retrieves 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 * 22 * @see get_bloginfo() For the list of possible values to display. 23 * 24 * @param string $show See get_bloginfo() for possible values. 25 * @return string 26 */ 27 function get_bloginfo_rss( $show = '' ) { 28 $info = strip_tags( get_bloginfo( $show ) ); 29 /** 30 * Filters the bloginfo for use in RSS feeds. 31 * 32 * @since 2.2.0 33 * 34 * @see convert_chars() 35 * @see get_bloginfo() 36 * 37 * @param string $info Converted string value of the blog information. 38 * @param string $show The type of blog information to retrieve. 39 */ 40 return apply_filters( 'get_bloginfo_rss', convert_chars( $info ), $show ); 41 } 42 43 /** 44 * Displays RSS container for the bloginfo function. 45 * 46 * You can retrieve anything that you can using the get_bloginfo() function. 47 * Everything will be stripped of tags and characters converted, when the values 48 * are retrieved for use in the feeds. 49 * 50 * @since 0.71 51 * 52 * @see get_bloginfo() For the list of possible values to display. 53 * 54 * @param string $show See get_bloginfo() for possible values. 55 */ 56 function bloginfo_rss( $show = '' ) { 57 /** 58 * Filters the bloginfo for display in RSS feeds. 59 * 60 * @since 2.1.0 61 * 62 * @see get_bloginfo() 63 * 64 * @param string $rss_container RSS container for the blog information. 65 * @param string $show The type of blog information to retrieve. 66 */ 67 echo apply_filters( 'bloginfo_rss', get_bloginfo_rss( $show ), $show ); 68 } 69 70 /** 71 * Retrieves the default feed. 72 * 73 * The default feed is 'rss2', unless a plugin changes it through the 74 * {@see 'default_feed'} filter. 75 * 76 * @since 2.5.0 77 * 78 * @return string Default feed, or for example 'rss2', 'atom', etc. 79 */ 80 function get_default_feed() { 81 /** 82 * Filters the default feed type. 83 * 84 * @since 2.5.0 85 * 86 * @param string $feed_type Type of default feed. Possible values include 'rss2', 'atom'. 87 * Default 'rss2'. 88 */ 89 $default_feed = apply_filters( 'default_feed', 'rss2' ); 90 91 return ( 'rss' === $default_feed ) ? 'rss2' : $default_feed; 92 } 93 94 /** 95 * Retrieves the blog title for the feed title. 96 * 97 * @since 2.2.0 98 * @since 4.4.0 The optional `$sep` parameter was deprecated and renamed to `$deprecated`. 99 * 100 * @param string $deprecated Unused. 101 * @return string The document title. 102 */ 103 function get_wp_title_rss( $deprecated = '–' ) { 104 if ( '–' !== $deprecated ) { 105 /* translators: %s: 'document_title_separator' filter name. */ 106 _deprecated_argument( __FUNCTION__, '4.4.0', sprintf( __( 'Use the %s filter instead.' ), '<code>document_title_separator</code>' ) ); 107 } 108 109 /** 110 * Filters the blog title for use as the feed title. 111 * 112 * @since 2.2.0 113 * @since 4.4.0 The `$sep` parameter was deprecated and renamed to `$deprecated`. 114 * 115 * @param string $title The current blog title. 116 * @param string $deprecated Unused. 117 */ 118 return apply_filters( 'get_wp_title_rss', wp_get_document_title(), $deprecated ); 119 } 120 121 /** 122 * Displays the blog title for display of the feed title. 123 * 124 * @since 2.2.0 125 * @since 4.4.0 The optional `$sep` parameter was deprecated and renamed to `$deprecated`. 126 * 127 * @param string $deprecated Unused. 128 */ 129 function wp_title_rss( $deprecated = '–' ) { 130 if ( '–' !== $deprecated ) { 131 /* translators: %s: 'document_title_separator' filter name. */ 132 _deprecated_argument( __FUNCTION__, '4.4.0', sprintf( __( 'Use the %s filter instead.' ), '<code>document_title_separator</code>' ) ); 133 } 134 135 /** 136 * Filters the blog title for display of the feed title. 137 * 138 * @since 2.2.0 139 * @since 4.4.0 The `$sep` parameter was deprecated and renamed to `$deprecated`. 140 * 141 * @see get_wp_title_rss() 142 * 143 * @param string $wp_title_rss The current blog title. 144 * @param string $deprecated Unused. 145 */ 146 echo apply_filters( 'wp_title_rss', get_wp_title_rss(), $deprecated ); 147 } 148 149 /** 150 * Retrieves the current post title for the feed. 151 * 152 * @since 2.0.0 153 * @since 6.6.0 Added the `$post` parameter. 154 * 155 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. 156 * @return string Current post title. 157 */ 158 function get_the_title_rss( $post = 0 ) { 159 $title = get_the_title( $post ); 160 161 /** 162 * Filters the post title for use in a feed. 163 * 164 * @since 1.2.0 165 * 166 * @param string $title The current post title. 167 */ 168 return apply_filters( 'the_title_rss', $title ); 169 } 170 171 /** 172 * Displays the post title in the feed. 173 * 174 * @since 0.71 175 */ 176 function the_title_rss() { 177 echo get_the_title_rss(); 178 } 179 180 /** 181 * Retrieves the post content for feeds. 182 * 183 * @since 2.9.0 184 * 185 * @see get_the_content() 186 * 187 * @param string $feed_type The type of feed. rss2 | atom | rss | rdf 188 * @return string The filtered content. 189 */ 190 function get_the_content_feed( $feed_type = null ) { 191 if ( ! $feed_type ) { 192 $feed_type = get_default_feed(); 193 } 194 195 /** This filter is documented in wp-includes/post-template.php */ 196 $content = apply_filters( 'the_content', get_the_content() ); 197 $content = str_replace( ']]>', ']]>', $content ); 198 199 /** 200 * Filters the post content for use in feeds. 201 * 202 * @since 2.9.0 203 * 204 * @param string $content The current post content. 205 * @param string $feed_type Type of feed. Possible values include 'rss2', 'atom'. 206 * Default 'rss2'. 207 */ 208 return apply_filters( 'the_content_feed', $content, $feed_type ); 209 } 210 211 /** 212 * Displays the post content for feeds. 213 * 214 * @since 2.9.0 215 * 216 * @param string $feed_type The type of feed. rss2 | atom | rss | rdf 217 */ 218 function the_content_feed( $feed_type = null ) { 219 echo get_the_content_feed( $feed_type ); 220 } 221 222 /** 223 * Displays the post excerpt for the feed. 224 * 225 * @since 0.71 226 */ 227 function the_excerpt_rss() { 228 $output = get_the_excerpt(); 229 /** 230 * Filters the post excerpt for a feed. 231 * 232 * @since 1.2.0 233 * 234 * @param string $output The current post excerpt. 235 */ 236 echo apply_filters( 'the_excerpt_rss', $output ); 237 } 238 239 /** 240 * Displays the permalink to the post for use in feeds. 241 * 242 * @since 2.3.0 243 */ 244 function the_permalink_rss() { 245 /** 246 * Filters the permalink to the post for use in feeds. 247 * 248 * @since 2.3.0 249 * 250 * @param string $post_permalink The current post permalink. 251 */ 252 echo esc_url( apply_filters( 'the_permalink_rss', get_permalink() ) ); 253 } 254 255 /** 256 * Outputs the link to the comments for the current post in an XML safe way. 257 * 258 * @since 3.0.0 259 */ 260 function comments_link_feed() { 261 /** 262 * Filters the comments permalink for the current post. 263 * 264 * @since 3.6.0 265 * 266 * @param string $comment_permalink The current comment permalink with 267 * '#comments' appended. 268 */ 269 echo esc_url( apply_filters( 'comments_link_feed', get_comments_link() ) ); 270 } 271 272 /** 273 * Displays the feed GUID for the current comment. 274 * 275 * @since 2.5.0 276 * 277 * @param int|WP_Comment $comment_id Optional comment object or ID. Defaults to global comment object. 278 */ 279 function comment_guid( $comment_id = null ) { 280 echo esc_url( get_comment_guid( $comment_id ) ); 281 } 282 283 /** 284 * Retrieves the feed GUID for the current comment. 285 * 286 * @since 2.5.0 287 * 288 * @param int|WP_Comment $comment_id Optional comment object or ID. Defaults to global comment object. 289 * @return string|false GUID for comment on success, false on failure. 290 */ 291 function get_comment_guid( $comment_id = null ) { 292 $comment = get_comment( $comment_id ); 293 294 if ( ! is_object( $comment ) ) { 295 return false; 296 } 297 298 return get_the_guid( $comment->comment_post_ID ) . '#comment-' . $comment->comment_ID; 299 } 300 301 /** 302 * Displays the link to the comments. 303 * 304 * @since 1.5.0 305 * @since 4.4.0 Introduced the `$comment` argument. 306 * 307 * @param int|WP_Comment $comment Optional. Comment object or ID. Defaults to global comment object. 308 */ 309 function comment_link( $comment = null ) { 310 /** 311 * Filters the current comment's permalink. 312 * 313 * @since 3.6.0 314 * 315 * @see get_comment_link() 316 * 317 * @param string $comment_permalink The current comment permalink. 318 */ 319 echo esc_url( apply_filters( 'comment_link', get_comment_link( $comment ) ) ); 320 } 321 322 /** 323 * Retrieves the current comment author for use in the feeds. 324 * 325 * @since 2.0.0 326 * 327 * @return string Comment Author. 328 */ 329 function get_comment_author_rss() { 330 /** 331 * Filters the current comment author for use in a feed. 332 * 333 * @since 1.5.0 334 * 335 * @see get_comment_author() 336 * 337 * @param string $comment_author The current comment author. 338 */ 339 return apply_filters( 'comment_author_rss', get_comment_author() ); 340 } 341 342 /** 343 * Displays the current comment author in the feed. 344 * 345 * @since 1.0.0 346 */ 347 function comment_author_rss() { 348 echo get_comment_author_rss(); 349 } 350 351 /** 352 * Displays the current comment content for use in the feeds. 353 * 354 * @since 1.0.0 355 */ 356 function comment_text_rss() { 357 $comment_text = get_comment_text(); 358 /** 359 * Filters the current comment content for use in a feed. 360 * 361 * @since 1.5.0 362 * 363 * @param string $comment_text The content of the current comment. 364 */ 365 $comment_text = apply_filters( 'comment_text_rss', $comment_text ); 366 echo $comment_text; 367 } 368 369 /** 370 * Retrieves all of the post categories, formatted for use in feeds. 371 * 372 * All of the categories for the current post in the feed loop, will be 373 * retrieved and have feed markup added, so that they can easily be added to the 374 * RSS2, Atom, or RSS1 and RSS0.91 RDF feeds. 375 * 376 * @since 2.1.0 377 * 378 * @param string $type Optional, default is the type returned by get_default_feed(). 379 * @return string All of the post categories for displaying in the feed. 380 */ 381 function get_the_category_rss( $type = null ) { 382 if ( empty( $type ) ) { 383 $type = get_default_feed(); 384 } 385 $categories = get_the_category(); 386 $tags = get_the_tags(); 387 $the_list = ''; 388 $cat_names = array(); 389 390 $filter = 'rss'; 391 if ( 'atom' === $type ) { 392 $filter = 'raw'; 393 } 394 395 if ( ! empty( $categories ) ) { 396 foreach ( (array) $categories as $category ) { 397 $cat_names[] = sanitize_term_field( 'name', $category->name, $category->term_id, 'category', $filter ); 398 } 399 } 400 401 if ( ! empty( $tags ) ) { 402 foreach ( (array) $tags as $tag ) { 403 $cat_names[] = sanitize_term_field( 'name', $tag->name, $tag->term_id, 'post_tag', $filter ); 404 } 405 } 406 407 $cat_names = array_unique( $cat_names ); 408 409 $atom_scheme = ''; 410 $blog_charset = ''; 411 412 if ( $cat_names ) { 413 if ( 'atom' === $type ) { 414 $atom_scheme = get_bloginfo_rss( 'url' ); 415 } elseif ( 'rdf' !== $type ) { 416 $blog_charset = get_option( 'blog_charset' ); 417 } 418 } 419 420 foreach ( $cat_names as $cat_name ) { 421 if ( 'rdf' === $type ) { 422 $the_list .= "\t\t<dc:subject><![CDATA[$cat_name]]></dc:subject>\n"; 423 } elseif ( 'atom' === $type ) { 424 $the_list .= sprintf( '<category scheme="%1$s" term="%2$s" />', esc_attr( $atom_scheme ), esc_attr( $cat_name ) ); 425 } else { 426 $the_list .= "\t\t<category><![CDATA[" . html_entity_decode( $cat_name, ENT_COMPAT, $blog_charset ) . "]]></category>\n"; 427 } 428 } 429 430 /** 431 * Filters all of the post categories for display in a feed. 432 * 433 * @since 1.2.0 434 * 435 * @param string $the_list All of the RSS post categories. 436 * @param string $type Type of feed. Possible values include 'rss2', 'atom'. 437 * Default 'rss2'. 438 */ 439 return apply_filters( 'the_category_rss', $the_list, $type ); 440 } 441 442 /** 443 * Displays the post categories in the feed. 444 * 445 * @since 0.71 446 * 447 * @see get_the_category_rss() For better explanation. 448 * 449 * @param string $type Optional, default is the type returned by get_default_feed(). 450 */ 451 function the_category_rss( $type = null ) { 452 echo get_the_category_rss( $type ); 453 } 454 455 /** 456 * Displays the HTML type based on the blog setting. 457 * 458 * The two possible values are either 'xhtml' or 'html'. 459 * 460 * @since 2.2.0 461 */ 462 function html_type_rss() { 463 $type = get_bloginfo( 'html_type' ); 464 if ( str_contains( $type, 'xhtml' ) ) { 465 $type = 'xhtml'; 466 } else { 467 $type = 'html'; 468 } 469 echo $type; 470 } 471 472 /** 473 * Displays the rss enclosure for the current post. 474 * 475 * Uses the global $post to check whether the post requires a password and if 476 * the user has the password for the post. If not then it will return before 477 * displaying. 478 * 479 * Also uses the function get_post_custom() to get the post's 'enclosure' 480 * metadata field and parses the value to display the enclosure(s). The 481 * enclosure(s) consist of enclosure HTML tag(s) with a URI and other 482 * attributes. 483 * 484 * @since 1.5.0 485 */ 486 function rss_enclosure() { 487 if ( post_password_required() ) { 488 return; 489 } 490 491 foreach ( (array) get_post_custom() as $key => $val ) { 492 if ( 'enclosure' === $key ) { 493 foreach ( (array) $val as $enc ) { 494 $enclosure = explode( "\n", $enc ); 495 496 if ( count( $enclosure ) < 3 ) { 497 continue; 498 } 499 500 // Only get the first element, e.g. 'audio/mpeg' from 'audio/mpeg mpga mp2 mp3'. 501 $t = preg_split( '/[ \t]/', trim( $enclosure[2] ) ); 502 $type = $t[0]; 503 504 /** 505 * Filters the RSS enclosure HTML link tag for the current post. 506 * 507 * @since 2.2.0 508 * 509 * @param string $html_link_tag The HTML link tag with a URI and other attributes. 510 */ 511 echo apply_filters( 'rss_enclosure', '<enclosure url="' . esc_url( trim( $enclosure[0] ) ) . '" length="' . absint( trim( $enclosure[1] ) ) . '" type="' . esc_attr( $type ) . '" />' . "\n" ); 512 } 513 } 514 } 515 } 516 517 /** 518 * Displays the atom enclosure for the current post. 519 * 520 * Uses the global $post to check whether the post requires a password and if 521 * the user has the password for the post. If not then it will return before 522 * displaying. 523 * 524 * Also uses the function get_post_custom() to get the post's 'enclosure' 525 * metadata field and parses the value to display the enclosure(s). The 526 * enclosure(s) consist of link HTML tag(s) with a URI and other attributes. 527 * 528 * @since 2.2.0 529 */ 530 function atom_enclosure() { 531 if ( post_password_required() ) { 532 return; 533 } 534 535 foreach ( (array) get_post_custom() as $key => $val ) { 536 if ( 'enclosure' === $key ) { 537 foreach ( (array) $val as $enc ) { 538 $enclosure = explode( "\n", $enc ); 539 540 $url = ''; 541 $type = ''; 542 $length = 0; 543 544 $mimes = get_allowed_mime_types(); 545 546 // Parse URL. 547 if ( isset( $enclosure[0] ) && is_string( $enclosure[0] ) ) { 548 $url = trim( $enclosure[0] ); 549 } 550 551 // Parse length and type. 552 for ( $i = 1; $i <= 2; $i++ ) { 553 if ( isset( $enclosure[ $i ] ) ) { 554 if ( is_numeric( $enclosure[ $i ] ) ) { 555 $length = trim( $enclosure[ $i ] ); 556 } elseif ( in_array( $enclosure[ $i ], $mimes, true ) ) { 557 $type = trim( $enclosure[ $i ] ); 558 } 559 } 560 } 561 562 $html_link_tag = sprintf( 563 "<link href=\"%s\" rel=\"enclosure\" length=\"%d\" type=\"%s\" />\n", 564 esc_url( $url ), 565 esc_attr( $length ), 566 esc_attr( $type ) 567 ); 568 569 /** 570 * Filters the atom enclosure HTML link tag for the current post. 571 * 572 * @since 2.2.0 573 * 574 * @param string $html_link_tag The HTML link tag with a URI and other attributes. 575 */ 576 echo apply_filters( 'atom_enclosure', $html_link_tag ); 577 } 578 } 579 } 580 } 581 582 /** 583 * Determines the type of a string of data with the data formatted. 584 * 585 * Tell whether the type is text, HTML, or XHTML, per RFC 4287 section 3.1. 586 * 587 * In the case of WordPress, text is defined as containing no markup, 588 * XHTML is defined as "well formed", and HTML as tag soup (i.e., the rest). 589 * 590 * Container div tags are added to XHTML values, per section 3.1.1.3. 591 * 592 * @link http://www.atomenabled.org/developers/syndication/atom-format-spec.php#rfc.section.3.1 593 * 594 * @since 2.5.0 595 * 596 * @param string $data Input string. 597 * @return array array(type, value) 598 */ 599 function prep_atom_text_construct( $data ) { 600 if ( ! str_contains( $data, '<' ) && ! str_contains( $data, '&' ) ) { 601 return array( 'text', $data ); 602 } 603 604 if ( ! function_exists( 'xml_parser_create' ) ) { 605 wp_trigger_error( '', __( "PHP's XML extension is not available. Please contact your hosting provider to enable PHP's XML extension." ) ); 606 607 return array( 'html', "<![CDATA[$data]]>" ); 608 } 609 610 $parser = xml_parser_create(); 611 xml_parse( $parser, '<div>' . $data . '</div>', true ); 612 $code = xml_get_error_code( $parser ); 613 614 if ( PHP_VERSION_ID < 80000 ) { // xml_parser_free() has no effect as of PHP 8.0. 615 xml_parser_free( $parser ); 616 } 617 618 unset( $parser ); 619 620 if ( ! $code ) { 621 if ( ! str_contains( $data, '<' ) ) { 622 return array( 'text', $data ); 623 } else { 624 $data = "<div xmlns='http://www.w3.org/1999/xhtml'>$data</div>"; 625 return array( 'xhtml', $data ); 626 } 627 } 628 629 if ( ! str_contains( $data, ']]>' ) ) { 630 return array( 'html', "<![CDATA[$data]]>" ); 631 } else { 632 return array( 'html', htmlspecialchars( $data ) ); 633 } 634 } 635 636 /** 637 * Displays Site Icon in atom feeds. 638 * 639 * @since 4.3.0 640 * 641 * @see get_site_icon_url() 642 */ 643 function atom_site_icon() { 644 $url = get_site_icon_url( 32 ); 645 if ( $url ) { 646 echo '<icon>' . convert_chars( $url ) . "</icon>\n"; 647 } 648 } 649 650 /** 651 * Displays Site Icon in RSS2. 652 * 653 * @since 4.3.0 654 */ 655 function rss2_site_icon() { 656 $rss_title = get_wp_title_rss(); 657 if ( empty( $rss_title ) ) { 658 $rss_title = get_bloginfo_rss( 'name' ); 659 } 660 661 $url = get_site_icon_url( 32 ); 662 if ( $url ) { 663 echo ' 664 <image> 665 <url>' . convert_chars( $url ) . '</url> 666 <title>' . $rss_title . '</title> 667 <link>' . get_bloginfo_rss( 'url' ) . '</link> 668 <width>32</width> 669 <height>32</height> 670 </image> ' . "\n"; 671 } 672 } 673 674 /** 675 * Returns the link for the currently displayed feed. 676 * 677 * @since 5.3.0 678 * 679 * @return string Correct link for the atom:self element. 680 */ 681 function get_self_link() { 682 $parsed = parse_url( home_url() ); 683 684 $domain = $parsed['host']; 685 if ( isset( $parsed['port'] ) ) { 686 $domain .= ':' . $parsed['port']; 687 } 688 689 return set_url_scheme( 'http://' . $domain . wp_unslash( $_SERVER['REQUEST_URI'] ) ); 690 } 691 692 /** 693 * Displays the link for the currently displayed feed in a XSS safe way. 694 * 695 * Generate a correct link for the atom:self element. 696 * 697 * @since 2.5.0 698 */ 699 function self_link() { 700 /** 701 * Filters the current feed URL. 702 * 703 * @since 3.6.0 704 * 705 * @see set_url_scheme() 706 * @see wp_unslash() 707 * 708 * @param string $feed_link The link for the feed with set URL scheme. 709 */ 710 echo esc_url( apply_filters( 'self_link', get_self_link() ) ); 711 } 712 713 /** 714 * Retrieves the XML namespaces for the root element of a feed. 715 * 716 * Namespaces are keyed by their prefix, so the same prefix cannot be 717 * declared twice. The default namespaces of a feed type cannot be removed, 718 * as the bundled feed templates use them in their static markup. 719 * 720 * @since 7.2.0 721 * 722 * @param string $type Type of feed. Possible values include 'rss2', 'rss2-comments', 723 * 'rdf', 'atom', and 'atom-comments'. 724 * @return array<string, string> Array of namespace URIs, keyed by their prefix. 725 * @phpstan-param non-falsy-string $type 726 * @phpstan-return array<non-falsy-string, non-falsy-string> 727 */ 728 function wp_get_feed_namespaces( string $type ): array { 729 $defaults = array(); 730 731 switch ( $type ) { 732 case 'rss2': 733 $defaults = array( 734 'content' => 'http://purl.org/rss/1.0/modules/content/', 735 'wfw' => 'http://wellformedweb.org/CommentAPI/', 736 'dc' => 'http://purl.org/dc/elements/1.1/', 737 'atom' => 'http://www.w3.org/2005/Atom', 738 'sy' => 'http://purl.org/rss/1.0/modules/syndication/', 739 'slash' => 'http://purl.org/rss/1.0/modules/slash/', 740 ); 741 break; 742 743 case 'rss2-comments': 744 $defaults = array( 745 'content' => 'http://purl.org/rss/1.0/modules/content/', 746 'dc' => 'http://purl.org/dc/elements/1.1/', 747 'atom' => 'http://www.w3.org/2005/Atom', 748 'sy' => 'http://purl.org/rss/1.0/modules/syndication/', 749 ); 750 break; 751 752 case 'rdf': 753 $defaults = array( 754 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 755 'dc' => 'http://purl.org/dc/elements/1.1/', 756 'sy' => 'http://purl.org/rss/1.0/modules/syndication/', 757 'admin' => 'http://webns.net/mvcb/', 758 'content' => 'http://purl.org/rss/1.0/modules/content/', 759 ); 760 break; 761 762 case 'atom': 763 case 'atom-comments': 764 $defaults = array( 765 'thr' => 'http://purl.org/syndication/thread/1.0', 766 ); 767 break; 768 } 769 770 /** 771 * Filters the XML namespaces of a feed's root element. 772 * 773 * Namespaces are keyed by their prefix, which makes duplicate `xmlns` 774 * attributes impossible. 775 * 776 * @since 7.2.0 777 * 778 * @param array<string, string> $namespaces Array of namespace URIs, keyed by their prefix. 779 * @param string $type Type of feed. Possible values include 'rss2', 780 * 'rss2-comments', 'rdf', 'atom', and 'atom-comments'. 781 */ 782 $namespaces = apply_filters( 'wp_feed_namespaces', $defaults, $type ); 783 784 if ( ! is_array( $namespaces ) ) { 785 $namespaces = array(); 786 } 787 788 // The bundled feed templates use the default namespaces, so they cannot be removed. 789 $namespaces = array_merge( $namespaces, $defaults ); 790 791 $sanitized = array(); 792 793 foreach ( $namespaces as $prefix => $uri ) { 794 $prefix = (string) $prefix; 795 796 // Prefixes must be valid XML names, and the `xml` and `xmlns` prefixes are reserved. 797 if ( ! preg_match( '/^[\p{L}_][\p{L}\p{M}\p{N}._\-\x{B7}]*\z/u', $prefix ) 798 || in_array( strtolower( $prefix ), array( 'xml', 'xmlns' ), true ) 799 ) { 800 continue; 801 } 802 803 if ( ! is_string( $uri ) || empty( $uri ) ) { 804 continue; 805 } 806 807 $sanitized[ $prefix ] = $uri; 808 } 809 810 return $sanitized; 811 } 812 813 /** 814 * Displays the XML namespaces for the root element of a feed. 815 * 816 * Plugins should add namespaces via the {@see 'wp_feed_namespaces'} filter instead 817 * of the older {@see "{$type}_ns"} actions, as two action callbacks printing the 818 * same namespace produce a duplicate attribute, which is a well-formedness 819 * error in XML. 820 * 821 * @since 7.2.0 822 * 823 * @param string $type Type of feed. Possible values include 'rss2', 'rss2-comments', 824 * 'rdf', 'atom', and 'atom-comments'. 825 * @phpstan-param non-falsy-string $type 826 */ 827 function wp_feed_namespaces( string $type ): void { 828 foreach ( wp_get_feed_namespaces( $type ) as $prefix => $uri ) { 829 printf( "xmlns:%s=\"%s\"\n\t", $prefix, esc_attr( $uri ) ); 830 } 831 } 832 833 /** 834 * Gets the UTC time of the most recently modified post from WP_Query. 835 * 836 * If viewing a comment feed, the time of the most recently modified 837 * comment will be returned. 838 * 839 * @since 5.2.0 840 * 841 * @global WP_Query $wp_query WordPress Query object. 842 * 843 * @param string $format Date format string to return the time in. 844 * @return string|false The time in requested format, or false on failure. 845 */ 846 function get_feed_build_date( $format ) { 847 global $wp_query; 848 849 $datetime = false; 850 $max_modified_time = false; 851 $utc = new DateTimeZone( 'UTC' ); 852 853 if ( ! empty( $wp_query ) && $wp_query->have_posts() ) { 854 // Extract the post modified times from the posts. 855 $modified_times = wp_list_pluck( $wp_query->posts, 'post_modified_gmt' ); 856 857 // If this is a comment feed, check those objects too. 858 if ( $wp_query->is_comment_feed() && $wp_query->comment_count ) { 859 // Extract the comment modified times from the comments. 860 $comment_times = wp_list_pluck( $wp_query->comments, 'comment_date_gmt' ); 861 862 // Add the comment times to the post times for comparison. 863 $modified_times = array_merge( $modified_times, $comment_times ); 864 } 865 866 // Determine the maximum modified time. 867 $datetime = date_create_immutable_from_format( 'Y-m-d H:i:s', max( $modified_times ), $utc ); 868 } 869 870 if ( false === $datetime ) { 871 // Fall back to last time any post was modified or published. 872 $datetime = date_create_immutable_from_format( 'Y-m-d H:i:s', get_lastpostmodified( 'GMT' ), $utc ); 873 } 874 875 if ( false !== $datetime ) { 876 $max_modified_time = $datetime->format( $format ); 877 } 878 879 /** 880 * Filters the date the last post or comment in the query was modified. 881 * 882 * @since 5.2.0 883 * 884 * @param string|false $max_modified_time Date the last post or comment was modified in the query, in UTC. 885 * False on failure. 886 * @param string $format The date format requested in get_feed_build_date(). 887 */ 888 return apply_filters( 'get_feed_build_date', $max_modified_time, $format ); 889 } 890 891 /** 892 * Returns the content type for specified feed type. 893 * 894 * @since 2.8.0 895 * 896 * @param string $type Type of feed. Possible values include 'rss', rss2', 'atom', and 'rdf'. 897 * @return string Content type for specified feed type. 898 */ 899 function feed_content_type( $type = '' ) { 900 if ( empty( $type ) ) { 901 $type = get_default_feed(); 902 } 903 904 $types = array( 905 'rss' => 'application/rss+xml', 906 'rss2' => 'application/rss+xml', 907 'rss-http' => 'text/xml', 908 'atom' => 'application/atom+xml', 909 'rdf' => 'application/rdf+xml', 910 ); 911 912 $content_type = ( ! empty( $types[ $type ] ) ) ? $types[ $type ] : 'application/octet-stream'; 913 914 /** 915 * Filters the content type for a specific feed type. 916 * 917 * @since 2.8.0 918 * 919 * @param string $content_type Content type indicating the type of data that a feed contains. 920 * @param string $type Type of feed. Possible values include 'rss', rss2', 'atom', and 'rdf'. 921 */ 922 return apply_filters( 'feed_content_type', $content_type, $type ); 923 } 924 925 /** 926 * Builds SimplePie object based on RSS or Atom feed from URL. 927 * 928 * @since 2.8.0 929 * 930 * @param string|string[] $url URL of feed to retrieve. If an array of URLs, the feeds are merged 931 * using SimplePie's multifeed feature. 932 * See also {@link http://simplepie.org/wiki/faq/typical_multifeed_gotchas} 933 * @return SimplePie\SimplePie|WP_Error SimplePie object on success or WP_Error object on failure. 934 */ 935 function fetch_feed( $url ) { 936 if ( ! class_exists( 'SimplePie\SimplePie', false ) ) { 937 require_once ABSPATH . WPINC . '/class-simplepie.php'; 938 } 939 940 require_once ABSPATH . WPINC . '/class-wp-feed-cache-transient.php'; 941 require_once ABSPATH . WPINC . '/class-wp-simplepie-file.php'; 942 require_once ABSPATH . WPINC . '/class-wp-simplepie-sanitize-kses.php'; 943 944 $feed = new SimplePie\SimplePie(); 945 946 $feed->get_registry()->register( SimplePie\Sanitize::class, 'WP_SimplePie_Sanitize_KSES', true ); 947 948 /* 949 * We must manually overwrite $feed->sanitize because SimplePie's constructor 950 * sets it before we have a chance to set the sanitization class. 951 */ 952 $feed->sanitize = new WP_SimplePie_Sanitize_KSES(); 953 954 // Register the cache handler using the recommended method for SimplePie 1.3 or later. 955 if ( method_exists( 'SimplePie_Cache', 'register' ) ) { 956 SimplePie_Cache::register( 'wp_transient', 'WP_Feed_Cache_Transient' ); 957 $feed->set_cache_location( 'wp_transient' ); 958 } else { 959 // Back-compat for SimplePie 1.2.x. 960 require_once ABSPATH . WPINC . '/class-wp-feed-cache.php'; 961 $feed->set_cache_class( 'WP_Feed_Cache' ); 962 } 963 964 $feed->get_registry()->register( SimplePie\File::class, 'WP_SimplePie_File', true ); 965 966 /** This filter is documented in wp-includes/class-wp-feed-cache-transient.php */ 967 $feed->set_cache_duration( apply_filters( 'wp_feed_cache_transient_lifetime', 12 * HOUR_IN_SECONDS, $url ) ); 968 969 /** 970 * Fires just before processing the SimplePie feed object. 971 * 972 * @since 3.0.0 973 * 974 * @param SimplePie\SimplePie $feed SimplePie feed object (passed by reference). 975 * @param string|string[] $url URL of feed or array of URLs of feeds to retrieve. 976 */ 977 do_action_ref_array( 'wp_feed_options', array( &$feed, $url ) ); 978 979 if ( empty( $url ) ) { 980 /* 981 * @todo: Set $url to empty string once supported by SimplePie. 982 * 983 * The early return without proceeding is to work around a PHP 8.5 984 * deprecation issue resolved in https://github.com/simplepie/simplepie/pull/949 985 * 986 * To avoid the duplicate code, this block can be replaced with `$url = '';` once SimplePie 987 * is upgraded to a version that includes the fix. 988 */ 989 $feed->init(); 990 $feed->set_output_encoding( get_bloginfo( 'charset' ) ); 991 992 if ( $feed->error() ) { 993 return new WP_Error( 'simplepie-error', $feed->error() ); 994 } 995 996 return $feed; 997 } elseif ( is_array( $url ) && count( $url ) === 1 ) { 998 $url = array_shift( $url ); 999 } elseif ( is_array( $url ) ) { 1000 $feeds = array(); 1001 $simplepie_errors = array(); 1002 foreach ( $url as $feed_url ) { 1003 $simplepie_instance = clone $feed; 1004 $simplepie_instance->set_feed_url( $feed_url ); 1005 $simplepie_instance->init(); 1006 $simplepie_instance->set_output_encoding( get_bloginfo( 'charset' ) ); 1007 1008 if ( $simplepie_instance->error() ) { 1009 $simplepie_errors[] = sprintf( 1010 /* translators: %1$s is the feed URL, %2$s is the error message. */ 1011 __( 'Error fetching feed %1$s: %2$s' ), 1012 esc_url( $feed_url ), 1013 $simplepie_instance->error() 1014 ); 1015 unset( $simplepie_instance ); 1016 continue; 1017 } 1018 1019 $feeds[] = $simplepie_instance; 1020 unset( $simplepie_instance ); 1021 } 1022 1023 if ( ! empty( $simplepie_errors ) ) { 1024 return new WP_Error( 'simplepie-error', $simplepie_errors ); 1025 } 1026 1027 $feed->init(); 1028 $feed->data['items'] = SimplePie\SimplePie::merge_items( $feeds ); 1029 return $feed; 1030 } 1031 1032 $feed->set_feed_url( $url ); 1033 $feed->init(); 1034 $feed->set_output_encoding( get_bloginfo( 'charset' ) ); 1035 1036 if ( $feed->error() ) { 1037 return new WP_Error( 'simplepie-error', $feed->error() ); 1038 } 1039 1040 return $feed; 1041 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Mon Oct 5 08:20:36 2026 | Cross-referenced by PHPXref |