'awp_recent_comments awp_widget', 'description' => __( 'Widget to display recent comments.', 'advance-widget-pack' ), ); $control_ops = array( 'width' => 200, 'height' => 250, ); parent::__construct( false, __( 'AWP Recent Comments', 'advance-widget-pack' ), $widget_ops, $control_ops ); } function widget( $args, $instance ) { $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Recent Comments', 'advance-widget-pack' ); $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); // Clamped to 1-50 to match update() and the form's , // for the same key-space reason as the other AWP widgets: the cache // key below is built from this value. $number = ( ! empty( $instance['number'] ) ) ? max( 1, min( 50, absint( $instance['number'] ) ) ) : 5; $query_args = apply_filters( 'widget_comments_args', array( 'number' => $number, 'status' => 'approve', 'post_status' => 'publish', ) ); // Cache comment IDs (not WP_Comment objects) on a transient, matching // the house pattern in Awp_Recent_Post::get_post_ids(). Storing whole // objects would freeze a snapshot of comment content into wp_options // that then goes stale independently of the object cache; IDs are // rehydrated below through get_comment(), which is object-cache-backed. // // The variable part of the key is a hash of the POST-filter args. It is // hashed here — unlike every other AWP cache key, which embeds its // clamped integer verbatim for greppability — because a third party can // return an arbitrary array from `widget_comments_args`, so the input is // unbounded in shape rather than a clamped integer. Hashing the // post-filter args also means two differently-filtered configurations // get distinct keys instead of colliding on one cached list. $cache_key = 'awp_recent_comments_' . awp_cache_generation() . '_' . md5( (string) wp_json_encode( $query_args ) ); $comment_ids = get_transient( $cache_key ); if ( false === $comment_ids ) { $comment_ids = get_comments( array_merge( $query_args, array( 'fields' => 'ids' ) ) ); $comment_ids = array_map( 'intval', (array) $comment_ids ); // Comments are volatile, so 5 minutes (as Recent Posts uses), not // the 15 used for the less volatile top-authors/related-posts data. // Comment events also bump awp_cache_generation() via the // comment_post / transition_comment_status hooks, so approving or // posting a comment invalidates this immediately. set_transient( $cache_key, $comment_ids, 5 * MINUTE_IN_SECONDS ); } echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- sidebar-provided wrapper markup, trusted per WP core convention. if ( $title ) { echo $args['before_title'] . esc_html( $title ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- title text is esc_html()'d; before/after_title are sidebar-provided wrapper markup. } if ( $comment_ids ) { // Rehydrate in one query rather than N, so the per-comment // get_comment() calls below are all object-cache hits. _prime_comment_caches( $comment_ids ); $comments = array(); foreach ( $comment_ids as $comment_id ) { $comment = get_comment( $comment_id ); // A comment deleted or unapproved since the transient was // written is skipped rather than rendered as an empty row. if ( $comment instanceof WP_Comment && '1' === (string) $comment->comment_approved ) { $comments[] = $comment; } } if ( $comments ) { $post_ids = array_unique( wp_list_pluck( $comments, 'comment_post_ID' ) ); _prime_post_caches( $post_ids, strpos( get_option( 'permalink_structure' ), '%category%' ), false ); echo '
id="get_field_id( 'show_comment' ) ); ?>" name="get_field_name( 'show_comment' ) ); ?>" />
id="get_field_id( 'show_gravatar' ) ); ?>" name="get_field_name( 'show_gravatar' ) ); ?>" />