4

I'm trying to inject some data into blocks via PHP but am running into trouble with parse_blocks/serialize_blocks breaking my content

I'm using the default 2020 theme and have no plugins installed

add_action('wp', function() {
    $oPost = get_post(119);

    printf("<h1>Post Content</h1><p>%s</p>", var_dump($oPost->post_content));

    $aBlocks = parse_blocks($oPost->post_content);


    printf("<h1>Parsed Blocks</h1><pre>%s</pre>", print_r($aBlocks, true));

    $sSerialisedBlocks = serialize_blocks($aBlocks);

    printf("<h1>Serialised Blocks</h1><p>%s</p>", var_dump($sSerialisedBlocks));
}, PHP_INT_MAX);

The first print (just outputting the post content) contains this text...

<h3>What types of accommodation are available in xxxx?<\/h3>

The second (after parsing into blocks) contains this...

<h3>What types of accommodation are available in xxxx?</h3>

But after re-serialising the blocks I get this...

\u003ch3\u003eWhat types of accommodation are available in xxxx?\u003c\/h3\u003e

Could someone tell me what I'm doing wrong?


EDIT

Ok so I followed the source code for serialize_blocks and it does seem like this is intentional with serialize_block_attributes explicitly converting some characters

My question is why then are these characters showing up in the WYSIWYG instead of being correctly converted back?

4
  • I really struggled to find a scenario where HTML was stored in a blocks attribute, not in its content. I would strongly advise against storing HTML in attributes, that's what the save method of a block is for. Have you confirmed the block editor is unable to handle the reserialized data?
    – Tom J Nowell
    Aug 27, 2020 at 16:52
  • This is how ACF is storing the data
    – michael
    Aug 28, 2020 at 18:33
  • If this is an ACF specific issue you should raiise it with them, however, there is no broken HTML here, those characters are just encoded, the decoded value is identical to if < and > is used. btw if you're doing this because you want to modify the HTML markup in PHP of blocks rendered with javascript, then no, just no. You can't do that. Your blocks will show as invalid when you reopen the block editor and fail validation. However if you do not care about that, then I do not see the problem. Just JSON decode the result to decode the characters.
    – Tom J Nowell
    Aug 31, 2020 at 10:58
  • And if encoded unicode characters are showing in ACF blocks, that's an ACF bug, report it to ACF. 3rd party plugin dev support is off topic here. The encoding of those characters is not a bug, it's intended behaviour in WordPress.
    – Tom J Nowell
    Aug 31, 2020 at 10:59

2 Answers 2

3

TLDR: It's not mangled, it's raw JSON values that haven't been decoded, not intended for us humans. JSON decode first before use.


This happens in serialize_block_attributes, the docblock explains why:

/**
...
 * The serialized result is a JSON-encoded string, with unicode escape sequence
 * substitution for characters which might otherwise interfere with embedding
 * the result in an HTML comment.
...
 */

So this is done as an encoding measure to avoid attributes accidentally closing a HTML comment and breaking the format of the document.

Without this, a HTML comment inside a block attribute would break the block and the rest of the content afterwards.

But How Do I Stop The Mangling?!!!

No, it isn't mangled it's encoded! You're not meant to read it literally, it needs decoding first. It's encoding certain characters by replacing them with unicode escaped versions to prevent breakage.

It's the same as opening up a fibre optic cable and looking at what's being sent, you won't see webpages you'll see pulses of light, a computer has to decode and process it and turn it into human friendly information. It's the same here.

Proof 1

Lets take the original code block from the question, and add the following fixes:

  • Wrap all in <pre> tags
  • Use esc_html so we can see the tags properly
  • Fix the printf by removing var_dump and using var_export with the second parameter so it returns rather than outputs
  • Add a final test case where we re-parse and re-serialize 10 times to compare the final result with the original
function reparse_reserialize( string $content, int $loops = 10 ) : string {
    $final_content = $content;
    for ($x = 0; $x <= $loops; $x++) {
        $blocks = parse_blocks( $final_content );
        $final_content = serialize_blocks( $blocks );
    }
    return $final_content;
}

add_action(
    'wp',
    function() {
        $p = get_post( 1 );

        echo '<p>Original content:</p>';
        echo '<pre>' . esc_html( var_export( $p->post_content, true ) ) . '</pre>';

        $final = reparse_reserialize( $p->post_content );

        echo '<p>10 parse and serialize loops later:</p>';
        echo '<pre>' . esc_html( var_export( $final, true ) ) . '</pre>';
        echo '<hr/>';
    },
    PHP_INT_MAX
);

Running that, we see that the content survived the process of being parsed and re-serialized 10 times. If mangling was occuring we would see progressively greater mangling occur

Proof 2

If we take the mangled markup:

\u003ch3\u003eWhat types of accommodation are available in xxxx?\u003c\/h3\u003e

Turn it into a JSON string, then decode it:

$json = '"\u003ch3\u003eWhat types of accommodation are available in xxxx?\u003c\/h3\u003e"';
echo '<pre>' . esc_html( json_decode( $json ) ) . '</pre>';

We get the original HTML:

<h3>What types of accommodation are available in xxxx?</h3>

So no mangling has taken place.

Summary

There is no mangling or corruption. It's just encoding the < and > to prevent breakage. JSON processors handle the unicode escape characters just fine.

If you are seeing these encoded characters in the block editor, then that is a bug, either in the block, or the ACF plugin. You should report it as such.

If you're seeing those encoded characters in your own work, run JSON decode before using the values. You can JSON encode if you want to serialize it back to the database. Remember, block attributes are normally stored as JSON inside a HTML comment, as long as it's valid JSON it should be ok when you decode it.

0
0

Found this solution in Wordpress documentation user comments:

wp_slash()

It worked in my case, without it the encoding was loosing the slashes when saving to database.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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