WP_Widget_Text::is_legacy_instance( array $instance ): bool

In this article

Determines whether a given instance is legacy and should bypass using TinyMCE.

Parameters

$instancearrayrequired
Instance data.
  • text string
    Content.
  • filter bool|string
    Whether autop or content filters should apply.
  • legacy bool
    Whether widget is in legacy mode.

Return

bool Whether Text widget instance contains legacy data.

Source

public function is_legacy_instance( $instance ) {

	// Legacy mode when not in visual mode.
	if ( isset( $instance['visual'] ) ) {
		return ! $instance['visual'];
	}

	// Or, the widget has been added/updated in 4.8.0 then filter prop is 'content' and it is no longer legacy.
	if ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ) {
		return false;
	}

	// If the text is empty, then nothing is preventing migration to TinyMCE.
	if ( empty( $instance['text'] ) ) {
		return false;
	}

	$wpautop         = ! empty( $instance['filter'] );
	$has_line_breaks = ( str_contains( trim( $instance['text'] ), "\n" ) );

	// If auto-paragraphs are not enabled and there are line breaks, then ensure legacy mode.
	if ( ! $wpautop && $has_line_breaks ) {
		return true;
	}

	// If an HTML comment is present, assume legacy mode.
	if ( str_contains( $instance['text'], '<!--' ) ) {
		return true;
	}

	// In the rare case that DOMDocument is not available we cannot reliably sniff content and so we assume legacy.
	if ( ! class_exists( 'DOMDocument' ) ) {
		// @codeCoverageIgnoreStart
		return true;
		// @codeCoverageIgnoreEnd
	}

	$doc = new DOMDocument();

	// Suppress warnings generated by loadHTML.
	$errors = libxml_use_internal_errors( true );
	// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
	@$doc->loadHTML(
		sprintf(
			'<!DOCTYPE html><html><head><meta charset="%s"></head><body>%s</body></html>',
			esc_attr( get_bloginfo( 'charset' ) ),
			$instance['text']
		)
	);
	libxml_use_internal_errors( $errors );

	$body = $doc->getElementsByTagName( 'body' )->item( 0 );

	// See $allowedposttags.
	$safe_elements_attributes = array(
		'strong'  => array(),
		'em'      => array(),
		'b'       => array(),
		'i'       => array(),
		'u'       => array(),
		's'       => array(),
		'ul'      => array(),
		'ol'      => array(),
		'li'      => array(),
		'hr'      => array(),
		'abbr'    => array(),
		'acronym' => array(),
		'code'    => array(),
		'dfn'     => array(),
		'a'       => array(
			'href' => true,
		),
		'img'     => array(
			'src' => true,
			'alt' => true,
		),
	);
	$safe_empty_elements      = array( 'img', 'hr', 'iframe' );

	foreach ( $body->getElementsByTagName( '*' ) as $element ) {
		/** @var DOMElement $element */
		$tag_name = strtolower( $element->nodeName );

		// If the element is not safe, then the instance is legacy.
		if ( ! isset( $safe_elements_attributes[ $tag_name ] ) ) {
			return true;
		}

		// If the element is not safely empty and it has empty contents, then legacy mode.
		if ( ! in_array( $tag_name, $safe_empty_elements, true ) && '' === trim( $element->textContent ) ) {
			return true;
		}

		// If an attribute is not recognized as safe, then the instance is legacy.
		foreach ( $element->attributes as $attribute ) {
			/** @var DOMAttr $attribute */
			$attribute_name = strtolower( $attribute->nodeName );

			if ( ! isset( $safe_elements_attributes[ $tag_name ][ $attribute_name ] ) ) {
				return true;
			}
		}
	}

	// Otherwise, the text contains no elements/attributes that TinyMCE could drop, and therefore the widget does not need legacy mode.
	return false;
}

Changelog

VersionDescription
4.8.1Introduced.

User Contributed Notes

You must log in before being able to contribute a note or feedback.