. * * Usage in a template: * echo wp_kses( * FilterX_Template_Loader::highlight( get_the_title(), $search_query ), * array( 'mark' => array( 'class' => array() ) ) * ); * * Or use the standalone function: * echo filterx_highlight( get_the_title(), $search_query ); * * @param string $text The text to search within. * @param string $keyword The keyword to highlight. * @return string Text with … wraps. */ function filterx_highlight( $text, $keyword ) { if ( empty( $keyword ) || empty( $text ) ) { return $text; } $escaped = preg_quote( $keyword, '/' ); return preg_replace( '/(' . $escaped . ')/iu', '$1', $text ); } // Also expose on the Loader class so templates can call either form if ( ! method_exists( 'FilterX_Template_Loader', 'highlight' ) ) { class_alias( 'FilterX_Template_Loader', '_FilterX_Template_Loader_Alias' ); } // Attach static method via runtime trick (PHP doesn't allow adding methods post-definition, // so we document this as a standalone function — see above). // Templates should call: FilterX_Template_Loader::highlight() or filterx_highlight() /* ------------------------------------------------------------------ */ /* Taxonomy term tags */ /* ------------------------------------------------------------------ */ /** * Build an HTML string of taxonomy term tags for a post. * * Returns a string of Term Name elements. * Returns empty string if the post has no terms in the given taxonomies. * * @param int $post_id Post ID. * @param array $taxonomies Array of taxonomy slugs to query. * @return string Safe HTML string (each term name is esc_html'd). */ function filterx_get_term_tags_html( $post_id, $taxonomies = array() ) { if ( empty( $taxonomies ) ) { return ''; } $parts = array(); foreach ( $taxonomies as $taxonomy ) { $terms = get_the_terms( absint( $post_id ), sanitize_key( $taxonomy ) ); if ( ! $terms || is_wp_error( $terms ) ) { continue; } foreach ( $terms as $term ) { $parts[] = sprintf( '%s', esc_attr( $taxonomy ), esc_url( get_term_link( $term ) ), esc_html( $term->name ) ); } } return implode( '', $parts ); } /* ------------------------------------------------------------------ */ /* Post thumbnail with fallback */ /* ------------------------------------------------------------------ */ /** * Get the post thumbnail HTML, or a placeholder if none exists. * * @param int $post_id Post ID (defaults to current post in loop). * @param string $size Image size (default: 'medium'). * @param array $attr Extra attributes. * @param string $placeholder_html HTML to output when no thumbnail. Pass empty * string to suppress placeholder entirely. * @return string */ function filterx_get_thumbnail( $post_id = 0, $size = 'medium', $attr = array(), $placeholder_html = '' ) { $post_id = $post_id ?: get_the_ID(); if ( has_post_thumbnail( $post_id ) ) { $default_attr = array( 'class' => 'filterx-card-thumb', 'loading' => 'lazy' ); $merged_attr = array_merge( $default_attr, $attr ); return get_the_post_thumbnail( $post_id, $size, $merged_attr ); } if ( '' === $placeholder_html ) { // Default SVG placeholder return ''; } return $placeholder_html; } /* ------------------------------------------------------------------ */ /* Schema.org Article markup helper */ /* ------------------------------------------------------------------ */ /** * Output basic Schema.org Article JSON-LD for the current post in loop. * Call inside a card template to improve SEO. * * Usage: * * @param int $post_id Post ID (defaults to current post in loop). */ function filterx_the_schema( $post_id = 0 ) { $post_id = $post_id ?: get_the_ID(); $schema = array( '@context' => 'https://schema.org', '@type' => 'Article', 'headline' => get_the_title( $post_id ), 'url' => get_permalink( $post_id ), 'datePublished' => get_the_date( 'c', $post_id ), 'dateModified' => get_the_modified_date( 'c', $post_id ), ); if ( has_post_thumbnail( $post_id ) ) { $schema['image'] = wp_get_attachment_image_url( get_post_thumbnail_id( $post_id ), 'large' ); } echo '' . "\n"; }