By default running content through the_content filter automatically checks for oEmbed content. However when returning data through heartbeat_received the filter for oEmbed doesn't seem to work.

For example -

function test_heartbeat_received( $response, $data ) {
    if( $data['test_heartbeat'] == 'test' ) {
        $content = "https://twitter.com/WordPress/status/456502643738030080";

        // Doesn't work
        global $wp_embed;
        $content = $wp_embed->autoembed( $content );
        $response['test_heartbeat'] = $content;

        // Also doesn't work
        $response['test_heartbeat'] = apply_filters( 'the_content', $content );
    return $response;
add_filter( 'heartbeat_received', 'test_heartbeat_received', 10, 2 );
add_filter( 'heartbeat_nopriv_received', 'test_heartbeat_received', 10, 2 );

Doing the exact same without using Heartbeat seems to work. The content filter is being applied as the formatting exists, just without oEmbed.

Any suggestions?


2 Answers 2


The problem:

The reason why this isn't working, is this part of the WP_Embed::shortcode() method:

 if( $post_ID ) {
    .... cut ...

    // Use oEmbed to get the HTML
    $html = wp_oembed_get( $url, $attr );

    ... cut ...

When trying to autoembed with the Heartbeat API the $post_ID is null, so wp_oembed_get() is never activated.

No Caching:

When you autoembed the Twitter link in the post editor, for a given $post_ID, the oembed HTML is cached in the post meta table under a key like this: _oembed_7bc759c5dcea2e4b77c939fc109996fb and a value like this:

<blockquote class="twitter-tweet" width="550">
      WordPress 3.9 “Smith” is now available with a smoother media editing experience, 
      live widget previews, and more: 
      <a href="http://t.co/mEbgUFdpyG">http://t.co/mEbgUFdpyG</a>
   &mdash; WordPress (@WordPress) 
   <a href="https://twitter.com/WordPress/statuses/456502643738030080">April 16, 2014</a>  
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

Each oembed link gets it's own row in the post meta table, well if it's not part of the default oembed handlers.

This caching mechanism is skipped when the $post_ID is missing, so you might want to consider some caching for your setup.

Possible workarounds:

i) You need to take all the matches of '|^\s*(https?://[^\s"]+)\s*$|im', within your string, and run it through the wp_oembed_get() function and replace the links from the original string.

ii) We could also associate it with a given post, within the test_heartbeat_received callback:

global $wp_embed, $post;
$post = get_post( 3147 ); // Post ID: 3147

$content = $wp_embed->autoembed( $content );
$response['test_heartbeat'] = $content;

to get around the missing $post_ID part and use the default caching for that post. You just have to remember that the oembed cache clears when you update that post.

If you try for example two Twitter links, with the above method:

$content = "

then after the autoembed process, you get two rows in the post meta table, assigned to post_id: 3147:

oembed rows

  • That's the problem, thanks! I'll try your solutions and report back :)
    – Elliott
    Apr 27, 2014 at 18:29
function test_heartbeat_received( $response, $data ) {
    if( $data['test_heartbeat'] == 'test' ) {
        $embed_code = wp_oembed_get('https://twitter.com/WordPress/status/456502643738030080');
        $response['test_heartbeat'] = apply_filters( 'the_content', $embed_code );
    return $response;
add_filter( 'heartbeat_received', 'test_heartbeat_received', 10, 2 );
add_filter( 'heartbeat_nopriv_received', 'test_heartbeat_received', 10, 2 );

you can try also after add this(for mixed content):


function div_wrapper($content) {
     // match any iframes
        $pattern = '~<iframe.*</iframe>|<embed.*</embed>~';
        preg_match_all($pattern, $content, $matches);

        foreach ($matches[0] as $match) {
            // wrap matched iframe with div
            $wrappedframe = apply_filters( 'the_content', $match );    
            //replace original iframe with new in content
            $content = str_replace($match, $wrappedframe, $content);

        return $content;    
    add_filter('the_content', 'div_wrapper');
  • Hey, thanks, that does work but only if the content is a full Twitter URL. I need it to work with mixed content (like a post).
    – Elliott
    Apr 25, 2014 at 9:41
  • The div_wrapper function doesn't fix the issue with mixed content I'm afraid =(
    – Elliott
    Apr 25, 2014 at 18:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.