WordPress wpautop/shortcode generating invalid markup

I notice that theres invalid markup in my shortcode. Its ok if I disable wpautop tho

function shortcode_banner($attrs, $content = '') {
    echo ' --- ' . $content . ' --- ';
    $html = '<section id="banner"><div class="wrap">' . do_shortcode($content) . '</div></section>';
    die($html);
}

When autop is enabled I get (notice the closing </p> at the start?)

Read More
--- </p>
<h1>The title</h1>
xxx ... xxx ...
--- 
<section id="banner"><div class="wrap"></p>
<h1>The title</h1>
xxx ... xxx ...
</div></section>

When disabled I get

 --- 
<h1>The header</h1>
xxx ... xxx ...
 --- 
<section id="banner"><div class="wrap">
<h1>The header</h1>
xxx ... xxx ...    
</div></section>

UPDATE: I notice this happens only when I have a tag (e.g. <h1>) after my shortcode. Like:

[banner]
<h1>Test</h1>

If I have

[banner]
xxx ...

it’s ok, except WordPress adds a <br />

Related posts

Leave a Reply

2 comments

  1. Try this:

    // replaces [banner ...] .. [/banner] with <!--banner ID--> before the_content filters run
    add_filter('the_content', 'protect_my_shortcode', -100);
    function protect_my_shortcode($content){
      return preg_replace_callback('/[(banner)b(.*?)(?:(/))?](.+?)[/1]/s', 'protect_my_shortcode_callback', $content);
    }
    
    function protect_my_shortcode_callback($matches){
      global $__banners;
      $id = '<!--banner '.count($__banners).'-->';
    
      $__banners[$id] = do_shortcode($matches[0]);
      // or '['.$matches[1].' '.$matches[2].']'.$matches[4].'[/'.$matches[1].']'
      // if you need to run any filters on the shortcode content ($matches[4])
    
      return $id;
    }
    
    // replaces <!--banner ID--> with the processed shortcode after all the filters run
    add_filter('the_content', 'unprotect_my_shortcode', 100);
    function unprotect_my_shortcode($content){
      global $__banners;
      return str_replace(array_keys($__banners), array_values($__banners), $content);
    }