Escaping WordPress output with only specific HTML elements

Sometimes when developing for WordPress, you will want to echo something. As all good developers know, any such content must be escaped. This makes sure that nothing nasty can be echoed and keeps you code more secure.

WordPress comes with a number of escaping functions, however sometimes they don’t quite do as you want. Take this example.

<figure
  class="wp-block-embed alignwide is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"
>
  <div class="wp-block-embed__wrapper">
    <iframe
      loading="lazy"
      width="500"
      height="281"
      src="https://www.youtube-nocookie.com/embed/yCtadbz_pgE?feature=oembed&amp;rel=0&amp;showinfo=0&amp;autoplay=1"
      frameborder="0"
      allowfullscreen=""
      allow="autoplay"
    ></iframe>
  </div>
</figure>

You may notice this is the output from a core embed block. It always contains the same markup. A <figure> with a <div> inside and inside that, the <iframe>. Therefore we need a way to escape this but make sure that these elements are not escaped.

Initially you might think that using wp_kses_post() would work. This is for escaping the output of post content. However this doesn’t work as it removes iFrames. So how do we get around this problem?

The answer is to use wp_kses() but pass it an array of the elements we want to allow.

We create a function (hd_allowed_embed_html()) which returns an array of the elements and the element attributes that are allowed. We then wrap the echoed content with wp_kses() and pass our function (hd_allowed_embed_html()) as the second argument.

Now anything that is not an <iframe><div> or <figure> with specific attributes will be escaped for us.

<?php
/**
 * Creates a escaping function to allowed certain HTML for embed content.
 * Needed for when echoing the innerblock HTML.
 *
 * @param array An array of HTML elements allowed.
 */
function hd_allowed_embed_html() {

	/**
	 * Return the allowed html
	 * These are the elements in the rendered embed block for youtube and vimeo videos.
	 * Therefore we need to allow these to keep the same structure.
	 */
	return [
		'iframe' => [
			'src'             => true,
			'height'          => true,
			'width'           => true,
			'frameborder'     => true,
			'allowfullscreen' => true,
		],
		'figure' => [
			'class' => true,
		],
		'div'    => [
			'class' => true,
		]
	];

}

// this is the content we want to echo
$content = '<figure class="wp-block-embed alignwide is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" width="500" height="281" src="https://www.youtube-nocookie.com/embed/yCtadbz_pgE?feature=oembed&amp;rel=0&amp;showinfo=0&amp;autoplay=1" frameborder="0" allowfullscreen="" allow="autoplay"></iframe></div></figure>';

// we echo like this - to make sure it is escaped correctly.
echo wp_kses( $content, hd_allowed_embed_html() );