PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 All 504 releases
← All changes | modules/infinite-scroll/infinity.php +119 -97 12.9.516.2 View file →
@@ -2,10 +2,13 @@
2 2
3 3 // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
4 4
5 5 use Automattic\Jetpack\Assets;
6 -use Automattic\Jetpack\Redirect;
7 6
7 +if ( ! defined( 'ABSPATH' ) ) {
8 + exit( 0 );
9 +}
10 +
8 11 /*
9 12 Plugin Name: The Neverending Home Page.
10 13 Plugin URI: https://automattic.com/
11 14 Description: Adds infinite scrolling support to the front-end blog post view for themes, pulling the next set of posts automatically into view when the reader approaches the bottom of the page.
@@ -19,8 +22,10 @@
19 22
20 23 /**
21 24 * Class: The_Neverending_Home_Page relies on add_theme_support, expects specific
22 25 * styling from each theme; including fixed footer.
26 + *
27 + * @phan-constructor-used-for-side-effects
23 28 */
24 29 class The_Neverending_Home_Page {
25 30 /**
26 31 * Maximum allowed number of posts per page in $_REQUEST.
@@ -89,23 +94,24 @@
89 94 * @uses get_theme_support, infinite_scroll_has_footer_widgets, sanitize_title, add_action, get_option, wp_parse_args, is_active_sidebar
90 95 * @return object
91 96 */
92 97 public static function get_settings() {
98 + $defaults = array(
99 + 'type' => 'scroll', // scroll | click
100 + 'requested_type' => 'scroll', // store the original type for use when logic overrides it
101 + 'footer_widgets' => false, // true | false | sidebar_id | array of sidebar_ids -- last two are checked with is_active_sidebar
102 + 'container' => 'content', // container html id
103 + 'wrapper' => true, // true | false | html class -- the html class.
104 + 'render' => false, // optional function, otherwise the `content` template part will be used
105 + 'footer' => true, // boolean to enable or disable the infinite footer | string to provide an html id to derive footer width from
106 + 'footer_callback' => false, // function to be called to render the IS footer, in place of the default
107 + 'posts_per_page' => false,
108 + 'click_handle' => true, // boolean to enable or disable rendering the click handler div. If type is click and this is false, page must include its own trigger with the HTML ID `infinite-handle`.
109 + );
110 +
93 111 if ( self::$settings === null ) {
94 112 $css_pattern = '#[^A-Z\d\-_]#i';
95 113
96 - $defaults = array(
97 - 'type' => 'scroll', // scroll | click
98 - 'requested_type' => 'scroll', // store the original type for use when logic overrides it
99 - 'footer_widgets' => false, // true | false | sidebar_id | array of sidebar_ids -- last two are checked with is_active_sidebar
100 - 'container' => 'content', // container html id
101 - 'wrapper' => true, // true | false | html class -- the html class.
102 - 'render' => false, // optional function, otherwise the `content` template part will be used
103 - 'footer' => true, // boolean to enable or disable the infinite footer | string to provide an html id to derive footer width from
104 - 'footer_callback' => false, // function to be called to render the IS footer, in place of the default
105 - 'posts_per_page' => false, // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page -- int | false to set based on IS type
106 - 'click_handle' => true, // boolean to enable or disable rendering the click handler div. If type is click and this is false, page must include its own trigger with the HTML ID `infinite-handle`.
107 - );
108 114 $settings = $defaults;
109 115 // Validate settings passed through add_theme_support()
110 116 $_settings = get_theme_support( 'infinite-scroll' );
111 117
@@ -213,8 +219,9 @@
213 219 // Check if a legacy `infinite_scroll_has_footer_widgets()` function is defined and override the footer_widgets parameter's value.
214 220 // Otherwise, if a widget area ID or array of IDs was provided in the footer_widgets parameter, check if any contains any widgets.
215 221 // It is safe to use `is_active_sidebar()` before the sidebar is registered as this function doesn't check for a sidebar's existence when determining if it contains any widgets.
216 222 if ( function_exists( 'infinite_scroll_has_footer_widgets' ) ) {
223 + // @phan-suppress-next-line PhanUndeclaredFunction -- Checked above. See also https://github.com/phan/phan/issues/1204.
217 224 $settings['footer_widgets'] = (bool) infinite_scroll_has_footer_widgets();
218 225 } elseif ( is_array( $settings['footer_widgets'] ) ) {
219 226 $sidebar_ids = $settings['footer_widgets'];
220 227 $settings['footer_widgets'] = false;
@@ -250,9 +257,9 @@
250 257
251 258 // Ensure that IS is enabled and no footer widgets exist if the IS type isn't already "click".
252 259 if ( 'click' !== $settings['type'] ) {
253 260 // Check the setting status
254 - $disabled = '' === get_option( self::$option_name_enabled ) ? true : false;
261 + $disabled = '' === get_option( self::$option_name_enabled );
255 262
256 263 // Footer content or Reading option check
257 264 if ( $settings['footer_widgets'] || $disabled ) {
258 265 $settings['type'] = 'click';
@@ -264,22 +271,24 @@
264 271 $settings['click_handle'] = true;
265 272 }
266 273
267 274 // Store final settings in a class static to avoid reparsing
268 - /**
269 - * Filter the array of Infinite Scroll settings.
270 - *
271 - * @module infinite-scroll
272 - *
273 - * @since 2.0.0
274 - *
275 - * @param array $settings Array of Infinite Scroll settings.
276 - */
277 - self::$settings = apply_filters( 'infinite_scroll_settings', $settings );
275 + self::$settings = $settings;
278 276 }
279 277
280 - /** This filter is already documented in modules/infinite-scroll/infinity.php */
281 - return (object) apply_filters( 'infinite_scroll_settings', self::$settings );
278 + /**
279 + * Filter the array of Infinite Scroll settings.
280 + *
281 + * @module infinite-scroll
282 + *
283 + * @since 2.0.0
284 + *
285 + * @param array $settings Array of Infinite Scroll settings.
286 + */
287 + $filtered_settings = apply_filters( 'infinite_scroll_settings', self::$settings );
288 +
289 + // Ensure all properties are still set.
290 + return (object) wp_parse_args( $filtered_settings, $defaults );
282 291 }
283 292
284 293 /**
285 294 * Number of posts per page.
@@ -287,14 +296,15 @@
287 296 * @uses self::wp_query, self::get_settings, apply_filters
288 297 * @return int
289 298 */
290 299 public static function posts_per_page() {
291 - $posts_per_page = self::get_settings()->posts_per_page ? self::get_settings()->posts_per_page : self::wp_query()->get( 'posts_per_page' );
300 + $settings = self::get_settings();
301 + $posts_per_page = $settings->posts_per_page ? $settings->posts_per_page : self::wp_query()->get( 'posts_per_page' );
292 302 $posts_per_page_core_option = get_option( 'posts_per_page' );
293 303
294 304 // If Infinite Scroll is set to click, and if the site owner changed posts_per_page, let's use that.
295 305 if (
296 - 'click' === self::get_settings()->type
306 + 'click' === $settings->type
297 307 && ( '10' !== $posts_per_page_core_option )
298 308 ) {
299 309 $posts_per_page = $posts_per_page_core_option;
300 310 }
@@ -370,9 +380,9 @@
370 380 * @param bool|null null Bool if value should be overridden, null to determine from query
371 381 * @param object self::wp_query() WP_Query object for current request
372 382 * @param object self::get_settings() Infinite Scroll settings
373 383 */
374 - $override = apply_filters( 'infinite_scroll_is_last_batch', null, self::wp_query(), self::get_settings() ); // phpcs:ignore WordPress.WP.ClassNameCase.Incorrect -- False positive.
384 + $override = apply_filters( 'infinite_scroll_is_last_batch', null, self::wp_query(), self::get_settings() );
375 385 if ( is_bool( $override ) ) {
376 386 return $override;
377 387 }
378 388
@@ -380,11 +390,11 @@
380 390 $posts_per_page = self::posts_per_page();
381 391
382 392 // This is to cope with an issue in certain themes or setups where posts are returned but found_posts is 0.
383 393 if ( 0 === $entries ) {
384 - return (bool) ( ! is_countable( self::wp_query()->posts ) || ( count( self::wp_query()->posts ) < $posts_per_page ) );
394 + return ( ! is_countable( self::wp_query()->posts ) || ( count( self::wp_query()->posts ) < $posts_per_page ) );
385 395 }
386 - $paged = max( 1, self::wp_query()->get( 'paged' ) );
396 + $paged = max( 1, (int) self::wp_query()->get( 'paged' ) );
387 397
388 398 // Are there enough posts for more than the first page?
389 399 if ( $entries <= $posts_per_page ) {
390 400 return true;
@@ -430,19 +440,8 @@
430 440 if ( ! current_theme_supports( 'infinite-scroll' ) ) {
431 441 return;
432 442 }
433 443
434 - if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
435 - // This setting is no longer configurable in wp-admin on WordPress.com -- leave a pointer
436 - add_settings_field(
437 - self::$option_name_enabled,
438 - '<span id="infinite-scroll-options">' . esc_html__( 'Infinite Scroll Behavior', 'jetpack' ) . '</span>',
439 - array( $this, 'infinite_setting_html_calypso_placeholder' ),
440 - 'reading'
441 - );
442 - return;
443 - }
444 -
445 444 // Add the setting field [infinite_scroll] and place it in Settings > Reading
446 445 add_settings_field( self::$option_name_enabled, '<span id="infinite-scroll-options">' . esc_html__( 'Infinite Scroll Behavior', 'jetpack' ) . '</span>', array( $this, 'infinite_setting_html' ), 'reading' );
447 446 register_setting( 'reading', self::$option_name_enabled, 'esc_attr' );
448 447 }
@@ -447,29 +446,16 @@
447 446 register_setting( 'reading', self::$option_name_enabled, 'esc_attr' );
448 447 }
449 448
450 449 /**
451 - * Render the redirect link to the infinite scroll settings in Calypso.
452 - */
453 - public function infinite_setting_html_calypso_placeholder() {
454 - $details = get_blog_details();
455 - $writing_url = Redirect::get_url( 'calypso-settings-writing', array( 'site' => $details->domain ) );
456 - echo '<span>' . sprintf(
457 - /* translators: Variables are the enclosing link to the settings page */
458 - esc_html__( 'This option has moved. You can now manage it %1$shere%2$s.', 'jetpack' ),
459 - '<a href="' . esc_url( $writing_url ) . '">',
460 - '</a>'
461 - ) . '</span>';
462 - }
463 -
464 - /**
465 450 * HTML code to display a checkbox true/false option
466 451 * for the infinite_scroll setting.
467 452 */
468 453 public function infinite_setting_html() {
454 + $settings = self::get_settings();
469 455
470 456 // If the blog has footer widgets, show a notice instead of the checkbox
471 - if ( self::get_settings()->footer_widgets || 'click' === self::get_settings()->requested_type ) {
457 + if ( $settings->footer_widgets || 'click' === $settings->requested_type ) {
472 458 echo '<label><em>' . esc_html__( 'We&rsquo;ve changed this option to a click-to-scroll version for you since you have footer widgets in Appearance &rarr; Widgets, or your theme uses click-to-scroll as the default behavior.', 'jetpack' ) . '</em></label>';
473 459 } else {
474 460 echo '<label><input name="infinite_scroll" type="checkbox" value="1" ' . checked( 1, '' !== get_option( self::$option_name_enabled ), false ) . ' /> ' . esc_html__( 'Check to load posts as you scroll. Uncheck to show clickable button to load posts', 'jetpack' ) . '</label>';
475 461 // translators: the number of posts to show on each page load.
@@ -563,15 +549,16 @@
563 549 *
564 550 * @return string
565 551 */
566 552 public function body_class() {
567 - $classes = '';
553 + $settings = self::get_settings();
554 + $classes = '';
568 555 // Do not add infinity-scroll class if disabled through the Reading page
569 - $disabled = '' === get_option( self::$option_name_enabled ) ? true : false;
570 - if ( ! $disabled || 'click' === self::get_settings()->type ) {
556 + $disabled = '' === get_option( self::$option_name_enabled );
557 + if ( ! $disabled || 'click' === $settings->type ) {
571 558 $classes = 'infinite-scroll';
572 559
573 - if ( 'scroll' === self::get_settings()->type ) {
560 + if ( 'scroll' === $settings->type ) {
574 561 $classes .= ' neverending';
575 562 }
576 563 }
577 564
@@ -590,9 +577,13 @@
590 577
591 578 $excluded_posts = array();
592 579 // loop through posts returned by wp_query call
593 580 foreach ( self::wp_query()->get_posts() as $post ) {
581 + if ( ! $post instanceof \WP_Post ) {
582 + continue;
583 + }
594 584
585 + // @phan-suppress-next-line PhanPluginDuplicateConditionalNullCoalescing -- probably would be safe to collapse, but not changing just in case.
595 586 $orderby = isset( self::wp_query()->query_vars['orderby'] ) ? self::wp_query()->query_vars['orderby'] : '';
596 587 $post_date = ( ! empty( $post->post_date ) ? $post->post_date : false );
597 588 if ( 'modified' === $orderby || false === $post_date ) {
598 589 $post_date = $post->post_modified;
@@ -619,9 +610,9 @@
619 610 $query_vars = self::wp_query()->query_vars;
620 611 // applies to search page only
621 612 if ( true === self::wp_query()->is_search() ) {
622 613 // set post__not_in array in query_vars in case it does not exists
623 - if ( false === isset( $query_vars['post__not_in'] ) ) {
614 + if ( ! isset( $query_vars['post__not_in'] ) ) {
624 615 $query_vars['post__not_in'] = array();
625 616 }
626 617 // get excluded posts
627 618 $excluded = self::get_excluded_posts();
@@ -646,12 +637,15 @@
646 637 }
647 638
648 639 // grab the last posts in the stack as if the last one is title-matching the rest is title-matching as well
649 640 $post = end( self::wp_query()->posts );
641 + if ( ! $post instanceof WP_Post ) {
642 + return false;
643 + }
650 644
651 645 // code inspired by WP_Query class
652 646 if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', self::wp_query()->get( 's' ), $matches ) ) {
653 - $search_terms = self::wp_query()->query_vars['search_terms'];
647 + $search_terms = self::wp_query()->query_vars['search_terms'] ?? null;
654 648 // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence
655 649 if ( empty( $search_terms ) || ! is_countable( $search_terms ) || count( $search_terms ) > 9 ) {
656 650 $search_terms = array( self::wp_query()->get( 's' ) );
657 651 }
@@ -692,11 +686,11 @@
692 686 if ( true === self::has_only_title_matching_posts() ) {
693 687 return false;
694 688 }
695 689
696 - $post = end( self::wp_query()->posts );
697 - $orderby = isset( self::wp_query()->query_vars['orderby'] ) ?
698 - self::wp_query()->query_vars['orderby'] : '';
690 + $post = end( self::wp_query()->posts );
691 + // @phan-suppress-next-line PhanPluginDuplicateConditionalNullCoalescing -- probably would be safe to collapse, but not changing just in case.
692 + $orderby = isset( self::wp_query()->query_vars['orderby'] ) ? self::wp_query()->query_vars['orderby'] : '';
699 693 $post_date = ( ! empty( $post->post_date ) ? $post->post_date : false );
700 694 switch ( $orderby ) {
701 695 case 'modified':
702 696 return $post->post_modified;
@@ -720,9 +714,9 @@
720 714 if ( empty( $query ) ) {
721 715 $query = self::wp_query();
722 716 }
723 717
724 - $orderby = isset( $query->query_vars['orderby'] ) ? $query->query_vars['orderby'] : '';
718 + $orderby = $query->query_vars['orderby'] ?? '';
725 719
726 720 switch ( $orderby ) {
727 721 case 'modified':
728 722 return 'post_modified';
@@ -751,13 +745,14 @@
751 745 global $wpdb;
752 746
753 747 $sort_field = self::get_query_sort_field( $query );
754 748
755 - if ( 'post_date' !== $sort_field || 'DESC' !== $_REQUEST['query_args']['order'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no changes made to the site.
749 + if ( 'post_date' !== $sort_field ||
750 + ! isset( $_REQUEST['query_args']['order'] ) || 'DESC' !== $_REQUEST['query_args']['order'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes made to the site.
756 751 return $where;
757 752 }
758 753
759 - $query_before = sanitize_text_field( wp_unslash( $_REQUEST['query_before'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no changes made to the site.
754 + $query_before = isset( $_REQUEST['query_before'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['query_before'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes made to the site.
760 755
761 756 if ( empty( $query_before ) ) {
762 757 return $where;
763 758 }
@@ -769,8 +764,10 @@
769 764 * Filter Infinite Scroll's SQL date query making sure post queries
770 765 * will always return results prior to (descending sort)
771 766 * or before (ascending sort) the last post date.
772 767 *
768 + * @deprecated 14.0
769 + *
773 770 * @module infinite-scroll
774 771 *
775 772 * @param string $clause SQL Date query.
776 773 * @param object $query Query.
@@ -776,11 +773,11 @@
776 773 * @param object $query Query.
777 774 * @param string $operator @deprecated Query operator.
778 775 * @param string $last_post_date @deprecated Last Post Date timestamp.
779 776 */
780 - $operator = 'ASC' === $_REQUEST['query_args']['order'] ? '>' : '<'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no changes to the site.
781 - $last_post_date = sanitize_text_field( wp_unslash( $_REQUEST['last_post_date'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no changes to the site.
782 - $where .= apply_filters( 'infinite_scroll_posts_where', $clause, $query, $operator, $last_post_date );
777 + $operator = '<';
778 + $last_post_date = isset( $_REQUEST['last_post_date'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['last_post_date'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes to the site
779 + $where .= apply_filters_deprecated( 'infinite_scroll_posts_where', array( $clause, $query, $operator, $last_post_date ), '14.0', '' );
783 780 }
784 781
785 782 return $where;
786 783 }
@@ -880,8 +877,10 @@
880 877 public function action_wp_footer_settings() {
881 878 global $wp_rewrite;
882 879 global $currentday;
883 880
881 + $settings = self::get_settings();
882 +
884 883 // Default click handle text
885 884 $click_handle_text = __( 'Older posts', 'jetpack' );
886 885
887 886 // If a single CPT is displayed, use its plural name instead of "posts"
@@ -901,11 +900,13 @@
901 900 // If not, use "Posts" without confusing the users.
902 901 if (
903 902 is_a( $taxonomy, 'WP_Taxonomy' )
904 903 && is_countable( $taxonomy->object_type )
904 + && ! empty( $taxonomy->object_type )
905 905 && count( $taxonomy->object_type ) < 2
906 906 ) {
907 - $post_type = $taxonomy->object_type[0];
907 + // It seems [0] doesn't work, as sometimes plugins can deregister a taxonomy but not reindex.
908 + $post_type = reset( $taxonomy->object_type );
908 909 }
909 910 }
910 911
911 912 if ( is_string( $post_type ) && ! empty( $post_type ) ) {
@@ -929,17 +930,17 @@
929 930 unset( $post_type );
930 931
931 932 // Base JS settings
932 933 $js_settings = array(
933 - 'id' => self::get_settings()->container,
934 + 'id' => $settings->container,
934 935 'ajaxurl' => esc_url_raw( self::ajax_url() ),
935 - 'type' => esc_js( self::get_settings()->type ),
936 + 'type' => esc_js( $settings->type ),
936 937 'wrapper' => self::has_wrapper(),
937 - 'wrapper_class' => is_string( self::get_settings()->wrapper ) ? esc_js( self::get_settings()->wrapper ) : 'infinite-wrap',
938 - 'footer' => is_string( self::get_settings()->footer ) ? esc_js( self::get_settings()->footer ) : self::get_settings()->footer,
939 - 'click_handle' => esc_js( self::get_settings()->click_handle ),
940 - 'text' => esc_js( $click_handle_text ),
941 - 'totop' => esc_js( __( 'Scroll back to top', 'jetpack' ) ),
938 + 'wrapper_class' => is_string( $settings->wrapper ) ? esc_js( $settings->wrapper ) : 'infinite-wrap',
939 + 'footer' => is_string( $settings->footer ) ? esc_js( $settings->footer ) : $settings->footer,
940 + 'click_handle' => esc_js( $settings->click_handle ),
941 + 'text' => $click_handle_text,
942 + 'totop' => __( 'Scroll back to top', 'jetpack' ),
942 943 'currentday' => $currentday,
943 944 'order' => 'DESC',
944 945 'scripts' => array(),
945 946 'styles' => array(),
@@ -954,9 +955,9 @@
954 955 'query_args' => self::get_query_vars(),
955 956 'query_before' => current_time( 'mysql' ),
956 957 'last_post_date' => self::get_last_post_date(),
957 958 'body_class' => self::body_class(),
958 - 'loading_text' => esc_js( __( 'Loading new page', 'jetpack' ) ),
959 + 'loading_text' => __( 'Loading new page', 'jetpack' ),
959 960 );
960 961
961 962 // Optional order param
962 963 if ( isset( $_REQUEST['order'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes made to the site.
@@ -988,9 +989,9 @@
988 989 do_action( 'infinite_scroll_wp_head' );
989 990
990 991 ?>
991 992 <script type="text/javascript">
992 - var infiniteScroll = <?php echo wp_json_encode( array( 'settings' => $js_settings ), JSON_HEX_TAG ); ?>;
993 + var infiniteScroll = <?php echo wp_json_encode( array( 'settings' => $js_settings ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
993 994 </script>
994 995 <?php
995 996 }
996 997
@@ -1107,10 +1108,10 @@
1107 1108 }
1108 1109
1109 1110 return out;
1110 1111 };
1111 - extend( window.infiniteScroll.settings.scripts, <?php echo wp_json_encode( $scripts ); ?> );
1112 - extend( window.infiniteScroll.settings.styles, <?php echo wp_json_encode( $styles ); ?> );
1112 + extend( window.infiniteScroll.settings.scripts, <?php echo wp_json_encode( $scripts, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
1113 + extend( window.infiniteScroll.settings.styles, <?php echo wp_json_encode( $styles, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> );
1113 1114 })();
1114 1115 </script>
1115 1116 <?php
1116 1117 $aria_live = 'assertive';
@@ -1372,9 +1373,9 @@
1372 1373 * @uses current_theme_supports, get_option, self::wp_query, current_user_can, apply_filters, self::get_settings, add_filter, WP_Query, remove_filter, have_posts, wp_head, do_action, add_action, this::render, this::has_wrapper, esc_attr, wp_footer, sharing_register_post_for_share_counts, get_the_id
1373 1374 */
1374 1375 public function query() {
1375 1376 if ( ! isset( $_REQUEST['page'] ) || ! current_theme_supports( 'infinite-scroll' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes to the site.
1376 - die;
1377 + die( 0 );
1377 1378 }
1378 1379
1379 1380 // @todo see if we should validate this nonce since we use it to form a query.
1380 1381 $page = (int) $_REQUEST['page']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're casting this to an int and not making changes to the site.
@@ -1379,9 +1380,9 @@
1379 1380 // @todo see if we should validate this nonce since we use it to form a query.
1380 1381 $page = (int) $_REQUEST['page']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're casting this to an int and not making changes to the site.
1381 1382
1382 1383 // Sanitize and set $previousday. Expected format: dd.mm.yy
1383 - if ( isset( $_REQUEST['currentday'] ) && preg_match( '/^\d{2}\.\d{2}\.\d{2}$/', $_REQUEST['currentday'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput, WordPress.Security.NonceVerification.Recommended -- manually validating, no changes to site
1384 + if ( isset( $_REQUEST['currentday'] ) && is_string( $_REQUEST['currentday'] ) && preg_match( '/^\d{2}\.\d{2}\.\d{2}$/', $_REQUEST['currentday'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput, WordPress.Security.NonceVerification.Recommended -- manually validating, no changes to site
1384 1385 global $previousday;
1385 1386 $previousday = $_REQUEST['currentday']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput
1386 1387 }
1387 1388
@@ -1396,9 +1397,9 @@
1396 1397 self::wp_query()->query_vars,
1397 1398 array(
1398 1399 'paged' => $page,
1399 1400 'post_status' => $post_status,
1400 - 'posts_per_page' => self::posts_per_page(), // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page
1401 + 'posts_per_page' => self::posts_per_page(),
1401 1402 'order' => $order,
1402 1403 )
1403 1404 );
1404 1405
@@ -1430,9 +1431,9 @@
1430 1431 $GLOBALS['wp_query'] = $infinite_scroll_query;
1431 1432
1432 1433 $infinite_scroll_query->query( $query_args );
1433 1434
1434 - remove_filter( 'posts_where', array( $this, 'query_time_filter' ), 10, 2 );
1435 + remove_filter( 'posts_where', array( $this, 'query_time_filter' ), 10 );
1435 1436
1436 1437 $results = array();
1437 1438
1438 1439 if ( have_posts() ) {
@@ -1473,8 +1474,9 @@
1473 1474 foreach ( $callbacks as $callback ) {
1474 1475 if ( false !== $callback && is_callable( $callback ) ) {
1475 1476 rewind_posts();
1476 1477 ob_start();
1478 +
1477 1479 add_action( 'infinite_scroll_render', $callback );
1478 1480
1479 1481 /**
1480 1482 * This action is already documented above.
@@ -1482,8 +1484,11 @@
1482 1484 * for more details as to why it was introduced.
1483 1485 */
1484 1486 do_action( 'infinite_scroll_render' );
1485 1487
1488 + // Fire wp_head to ensure that all necessary scripts are enqueued. Output isn't used, but scripts are extracted in self::action_wp_footer.
1489 + wp_head();
1490 +
1486 1491 $results['html'] = ob_get_clean();
1487 1492 remove_action( 'infinite_scroll_render', $callback );
1488 1493 }
1489 1494 if ( ! empty( $results['html'] ) ) {
@@ -1539,9 +1544,22 @@
1539 1544
1540 1545 sharing_register_post_for_share_counts( get_the_ID() );
1541 1546 }
1542 1547
1543 - $results['postflair'] = array_flip( $jetpack_sharing_counts );
1548 + // If sharing counts are not initialized for any reason, we initialize them here.
1549 + if ( ! is_array( $jetpack_sharing_counts ) ) {
1550 + $jetpack_sharing_counts = array();
1551 + } else {
1552 + // Filter out non-string and non-integer values to avoid warnings with array_flip.
1553 + $flippable_jetpack_sharing_counts = array_filter(
1554 + $jetpack_sharing_counts,
1555 + function ( $value ) {
1556 + return is_string( $value ) || is_int( $value );
1557 + }
1558 + );
1559 + }
1560 +
1561 + $results['postflair'] = array_flip( $flippable_jetpack_sharing_counts ?? array() );
1544 1562 }
1545 1563 } else {
1546 1564 /** This action is already documented in modules/infinite-scroll/infinity.php */
1547 1565 do_action( 'infinite_scroll_empty' );
@@ -1559,9 +1577,11 @@
1559 1577 * @param array $results Array of Infinite Scroll results.
1560 1578 * @param array $query_args Array of main query arguments.
1561 1579 * @param WP_Query $wp_query WP Query.
1562 1580 */
1563 - apply_filters( 'infinite_scroll_results', $results, $query_args, self::wp_query() )
1581 + apply_filters( 'infinite_scroll_results', $results, $query_args, self::wp_query() ),
1582 + null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
1583 + JSON_UNESCAPED_SLASHES
1564 1584 );
1565 1585 }
1566 1586
1567 1587 /**
@@ -1698,15 +1718,17 @@
1698 1718 if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
1699 1719 return;
1700 1720 }
1701 1721
1722 + $settings = self::get_settings();
1723 +
1702 1724 // Bail if theme requested footer not show
1703 - if ( false === self::get_settings()->footer ) {
1725 + if ( false === $settings->footer ) {
1704 1726 return;
1705 1727 }
1706 1728
1707 1729 // We only need the new footer for the 'scroll' type
1708 - if ( 'scroll' !== self::get_settings()->type || ! self::archive_supports_infinity() ) {
1730 + if ( 'scroll' !== $settings->type || ! self::archive_supports_infinity() ) {
1709 1731 return;
1710 1732 }
1711 1733
1712 1734 if ( self::is_last_batch() ) {
@@ -1713,10 +1735,10 @@
1713 1735 return;
1714 1736 }
1715 1737
1716 1738 // Display a footer, either user-specified or a default
1717 - if ( false !== self::get_settings()->footer_callback && is_callable( self::get_settings()->footer_callback ) ) {
1718 - call_user_func( self::get_settings()->footer_callback, self::get_settings() );
1739 + if ( false !== $settings->footer_callback && is_callable( $settings->footer_callback ) ) {
1740 + call_user_func( $settings->footer_callback, $settings );
1719 1741 } else {
1720 1742 self::default_footer();
1721 1743 }
1722 1744 }
@@ -1815,9 +1837,9 @@
1815 1837
1816 1838 $scripts_data[ $key ]['extra_data'] = sprintf(
1817 1839 'window.%s = %s',
1818 1840 '_wpmejsSettings',
1819 - wp_json_encode( apply_filters( 'mejs_settings', $mejs_settings ) )
1841 + wp_json_encode( apply_filters( 'mejs_settings', $mejs_settings ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP )
1820 1842 );
1821 1843 }
1822 1844 }
1823 1845 return $scripts_data;
@@ -1976,9 +1998,9 @@
1976 1998 ?>
1977 1999 <amp-next-page max-pages="<?php echo esc_attr( static::amp_get_max_pages() ); ?>">
1978 2000 <script type="application/json">
1979 2001 [
1980 - <?php echo wp_json_encode( $this->amp_next_page() ); ?>
2002 + <?php echo wp_json_encode( $this->amp_next_page(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>
1981 2003 ]
1982 2004 </script>
1983 2005 <div separator>
1984 2006 <?php
@@ -2074,9 +2096,9 @@
2074 2096 */
2075 2097 protected static function amp_get_max_pages() {
2076 2098 global $wp_query;
2077 2099
2078 - return (int) $wp_query->max_num_pages - $wp_query->query_vars['paged'];
2100 + return (int) $wp_query->max_num_pages - (int) $wp_query->query_vars['paged'];
2079 2101 }
2080 2102
2081 2103 /**
2082 2104 * Is the last page.