[wp-trac] [WordPress Trac] #41179: Adding get_the_content-filter

WordPress Trac noreply at wordpress.org
Sat Aug 2 21:29:42 UTC 2025


#41179: Adding get_the_content-filter
--------------------------------------+------------------------------
 Reporter:  Ninos Ego                 |       Owner:  (none)
     Type:  enhancement               |      Status:  new
 Priority:  normal                    |   Milestone:  Awaiting Review
Component:  Themes                    |     Version:  4.8
 Severity:  normal                    |  Resolution:
 Keywords:  needs-codex dev-feedback  |     Focuses:  docs, template
--------------------------------------+------------------------------

Comment (by deyril):

 Hello @joyously , I just coming here and a little bit shocked if this idea
 has been patched 8 years ago and still not considered if that's useful, I
 hope my use case can help you to understand why we need it. At the moment,
 I'm working on a plugin that extend a revision system, I asked AI for help
 me to explain it, I hope it can explains everything


 ===========================

 = WordPress Trac Ticket Proposal: Add Filter Hook for get_the_content()
 Function =

 == Summary ==
 Request to add a filter hook to the `get_the_content()` function to allow
 plugins to modify post content when accessed directly, similar to how
 `the_content` filter works for `the_content()` function.

 == Problem Description ==

 === Current Limitation ===
 The `get_the_content()` function in WordPress core does not provide any
 filter hooks, making it impossible for plugins to modify content when it's
 accessed directly. This creates an inconsistency with `the_content()`
 function which applies the `the_content` filter.

 === Real-World Use Case: Advanced Revision System ===
 We are developing an advanced revision system plugin that allows users to
 preview specific revisions of posts on the frontend. Our system works by:

  1. '''Revision Storage''': Storing complete post revisions with content,
 title, and metadata
  2. '''Frontend Preview''': Generating secure URLs with nonce verification
 to preview revisions
  3. '''Content Filtering''': Intercepting content display to show revision
 content instead of published content

 === The Problem ===
 Our revision system successfully filters content when themes use
 `the_content()` via the `the_content` filter. However, many plugins and
 themes call `get_the_content()` directly, bypassing our content filtering
 entirely.

 '''Detailed Example: qi-blocks Plugin Global Styles System'''

 The qi-blocks plugin (a Gutenberg blocks framework) has a global styles
 system that needs to analyze post content to:

  1. '''Generate Dynamic CSS''': Create inline styles based on block
 configurations
  2. '''Process Reusable Blocks''': Expand reusable block references to get
 their actual content
  3. '''Font Loading Optimization''': Detect which fonts are actually used
 in content
  4. '''Style Cleanup''': Remove unused styles from the database when
 blocks are deleted

 Here's the problematic code from `add_page_inline_style()` method from qi-
 blocks plugin https://wordpress.org/plugins/qi-blocks/ at file `class-qi-
 blocks.php` :

 {{{
 public function add_page_inline_style() {
     $global_styles = get_option( 'qi_blocks_global_styles' );

     if ( ! empty( $global_styles ) ) {
         $page_id = apply_filters(
 'qi_blocks_filter_page_inline_style_page_id', get_queried_object_id() );
         $styles  = array();
         $fonts   = array(
             'family' => array(),
             'weight' => array(),
             'style'  => array(),
         );

         $update               = false;
         $include_italic_fonts = false;
         $templates_selector   = 'body ';


         // Post blocks.
         if ( isset( $global_styles['posts'][ $page_id ] ) && ! empty(
 $global_styles['posts'][ $page_id ] ) ) {
             $get_page_content = trim( get_the_content( null, false,
 $page_id ) );

             // Check if the content contains reusable blocks and expand
 the page content with the real reusable content.
             preg_match_all( '({[\s]?[\'\"]ref[\'\"]:(.*?)[\s]?})',
 $get_page_content, $reusable_matches );

             if ( ! empty( $reusable_matches ) && isset(
 $reusable_matches[1] ) && ! empty( $reusable_matches[1] ) ) {
                 $usable_content = '';

                 if ( is_array( $reusable_matches[1] ) ) {
                     foreach ( $reusable_matches[1] as $reusable_match_item
 ) {
                         $usable_content .= get_the_content( null, false,
 intval( $reusable_match_item ) );
                     }
                 } else {
                     $usable_content = get_the_content( null, false,
 intval( $reusable_matches[1] ) );
                 }

                 if ( ! empty( $usable_content ) ) {
                     $get_page_content .= $usable_content;
                 }
             }

             // Check if the content contains italic html selector and
 include corresponding font weights and styles for it.
             if ( strpos( $get_page_content, '<em>' ) !== false ) {
                 $include_italic_fonts = true;
             }

             foreach ( $global_styles['posts'][ $page_id ] as $block_index
 => $block_style ) {
                 $block_style_fonts = (array) $block_style->fonts;

                 foreach ( array_keys( $fonts ) as $font_key ) {
                     if ( array_key_exists( $font_key, $block_style_fonts )
 ) {
                         $fonts[ $font_key ] = array_merge( $fonts[
 $font_key ], $block_style_fonts[ $font_key ] );
                     }
                 }

                 // Remove unnecessary styles from the database if IDS not
 match or values are empty, because Gutenberg can change the block ID.
                 if ( ! empty( $get_page_content ) && property_exists(
 $block_style, 'key' ) ) {
                     $blocks_removed = false;

                     if ( empty( $block_style->values ) ) {
                         $blocks_removed = true;
                     } elseif ( ! empty( $block_style->key ) && strpos(
 $get_page_content, $block_style->key ) === false ) {
                         $blocks_removed = true;
                     }

                     if ( $blocks_removed ) {
                         unset( $global_styles['posts'][ $page_id ][
 $block_index ] );

                         $update = true;
                     }
                 }

                 foreach ( $block_style->values as $block_element ) {

                     if ( ! empty( $block_element->styles ) ) {
                         $styles[] = $block_element->selector . '{' .
 $block_element->styles . '}';
                     }

                     if ( isset( $block_element->tablet_styles ) && !
 empty( $block_element->tablet_styles ) ) {
                         $styles[] = '@media (max-width: 1024px) { ' .
 $block_element->selector . '{' . $block_element->tablet_styles . '} }';
                     }

                     if ( isset( $block_element->mobile_styles ) && !
 empty( $block_element->mobile_styles ) ) {
                         $styles[] = '@media (max-width: 680px) { ' .
 $block_element->selector . '{' . $block_element->mobile_styles . '} }';
                     }

                     if ( isset( $block_element->custom_styles ) && !
 empty( $block_element->custom_styles ) ) {
                         foreach ( $block_element->custom_styles as
 $custom_style ) {
                             if ( isset( $custom_style->value ) && ! empty(
 $custom_style->value ) ) {
                                 $styles[] = '@media (max-width: ' .
 $custom_style->key . 'px) { ' . $block_element->selector . '{' .
 $custom_style->value . '} }';
                             }
                         }
                     }
                 }
             }

             // Update global options indexes.
             if ( $update ) {
                 $global_styles['posts'][ $page_id ] = array_values(
 $global_styles['posts'][ $page_id ] );
             }
         }

         // Enqueue Google Fonts.
         if ( isset( $fonts['family'] ) && ! empty( $fonts['family'] ) ) {
             $this->include_google_fonts( $fonts, $include_italic_fonts );
         }

         // Update global options.
         if ( $update ) {
             update_option( 'qi_blocks_global_styles', $global_styles );
         }

         // Load styles.
         if ( ! empty( $styles ) ) {
             wp_add_inline_style( 'qi-blocks-main', implode( ' ', $styles )
 );
         }
     }
 }
 }}}

 '''Why This Breaks Our Revision System:'''

 When a user previews a revision via our system:
  1. URL: `example.com/post-name/?revision=123&_wpnonce=abc123`
  2. Our `the_content` filter correctly shows revision content in the main
 post area
  3. But qi-blocks calls `get_the_content()` directly, getting the
 '''published''' content instead of '''revision''' content
  4. This creates inconsistency: main content shows revision, but styles
 are generated from published content
  5. Result: Broken layout, missing styles, or incorrect styling for the
 revision preview

 === Current Workarounds and Their Limitations ===

 We've attempted several workarounds:

  1. '''`the_posts` Filter''': Modifying post objects before they're
 processed
     * '''Issue''': Doesn't reliably intercept `get_the_content()` calls
     * '''Timing''': Filter may run too early or late in the process

  2. '''Global `$post` Object Modification''': Directly modifying the
 global post
     * '''Issue''': Risky and can cause conflicts with other plugins
     * '''Scope''': May affect unintended parts of the application

  3. '''Function Override''': Attempting to override `get_the_content()`
     * '''Issue''': Not possible without modifying core files
     * '''Maintenance''': Would break on WordPress updates

 == Proposed Solution ==

 Add a filter hook to the `get_the_content()` function, similar to existing
 patterns in WordPress:

 === Suggested Implementation ===
 {{{
 function get_the_content( $more_link_text = null, $strip_teaser = false,
 $post = null ) {
     // ... existing code ...

     // Before returning content, apply filter
     $content = apply_filters( 'get_the_content', $content,
 $more_link_text, $strip_teaser, $post );

     return $content;
 }
 }}}

 === Alternative Filter Names ===
  * `pre_get_the_content` - Applied before content processing
  * `the_content_raw` - Applied to raw content before formatting
  * `get_the_content_filter` - Explicit naming

 == Benefits ==

 === 1. Plugin Compatibility ===
  * Allows content modification plugins to work with all content access
 methods
  * Ensures consistent behavior between `the_content()` and
 `get_the_content()`
  * Enables advanced features like revision previews, content translation,
 A/B testing

 === 2. Developer Experience ===
  * Provides expected filtering capability that developers assume exists
  * Maintains consistency with WordPress filter patterns
  * Reduces need for complex workarounds

 === 3. Backward Compatibility ===
  * Adding a filter hook is non-breaking
  * Existing code continues to work unchanged
  * Optional for developers who don't need it

 == Use Cases Beyond Revision Systems ==

  1. '''Content Translation Plugins''': Modify content based on user
 language
  2. '''A/B Testing Plugins''': Serve different content variations
  3. '''Content Security Plugins''': Filter or sanitize content before
 display
  4. '''SEO Plugins''': Modify content for search engine optimization
  5. '''Personalization Plugins''': Customize content based on user
 preferences
  6. '''Content Caching Plugins''': Implement custom caching strategies

 == Technical Considerations ==

 === Performance Impact ===
  * Minimal: Filter only runs when `get_the_content()` is called
  * Optional: No performance impact if no filters are registered
  * Consistent: Same pattern as existing WordPress filters

 === Implementation Location ===
  * '''File''': `wp-includes/post-template.php`
  * '''Function''': `get_the_content()`
  * '''Line''': ~380 (before return statement)

 === Filter Parameters ===
 {{{
 apply_filters( 'get_the_content', $content, $more_link_text,
 $strip_teaser, $post )
 }}}

  * `$content` (string): The post content
  * `$more_link_text` (string): More link text
  * `$strip_teaser` (bool): Whether to strip teaser
  * `$post` (WP_Post|null): Post object

 == Conclusion ==

 Adding a filter hook to `get_the_content()` would:
  * Solve real-world plugin compatibility issues
  * Maintain consistency with WordPress patterns
  * Enable advanced content management features
  * Require minimal code changes with zero breaking changes

 This enhancement would benefit the entire WordPress ecosystem by providing
 developers with the filtering capabilities they need for modern content
 management requirements.

 == Related Functions That Have Filters ==
  * `the_content()` - applies `the_content` filter
  * `the_title()` - applies `the_title` filter
  * `the_excerpt()` - applies `the_excerpt` filter
  * `get_the_excerpt()` - applies `get_the_excerpt` filter

 The absence of a filter in `get_the_content()` is an inconsistency that
 should be addressed.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/41179#comment:9>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list