|
Server : LiteSpeed System : Linux srv526460274 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : kerao9884 ( 1082) PHP Version : 8.0.30 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, Directory : /home/token55.net/public_html/wp-content/plugins/amp/includes/utils/ |
Upload File : |
<?php
/**
* Class AMP_DOM_Utils.
*
* @package AMP
*/
use AmpProject\AmpWP\Dom\Options;
use AmpProject\Dom\Document;
use AmpProject\Dom\Element;
use AmpProject\Html\Tag;
/**
* Class AMP_DOM_Utils
*
* Functionality to simplify working with Dom\Documents and DOMElements.
*
* @since 0.2
*/
class AMP_DOM_Utils {
/**
* Regular expression pattern to match events and actions within an 'on' attribute.
*
* @since 1.4.0
* @var string
*/
const AMP_EVENT_ACTIONS_REGEX_PATTERN = '/((?<event>[^:;]+):(?<actions>(?:[^;,\(]+(?:\([^\)]+\))?,?)+))+?/';
/**
* Regular expression pattern to match individual actions within an event.
*
* @since 1.4.0
* @var string
*/
const AMP_ACTION_REGEX_PATTERN = '/(?<action>[^(),\s]+(?:\([^\)]+\))?)+/';
/**
* Regular expression pattern to match the contents of the <body> element.
*
* @since 1.5.0
* @var string
*/
const HTML_BODY_CONTENTS_PATTERN = '#^.*?<body.*?>(.*)</body>.*?$#si';
/**
* Return a valid Dom\Document representing HTML document passed as a parameter.
*
* @since 0.7
* @see AMP_DOM_Utils::get_content_from_dom_node()
* @codeCoverageIgnore
* @deprecated Use AmpProject\Dom\Document::fromHtml( $html, $encoding ) instead.
*
* @param string $document Valid HTML document to be represented by a Dom\Document.
* @param string $encoding Optional. Encoding to use for the content.
* @return Document|false Returns Dom\Document, or false if conversion failed.
*/
public static function get_dom( $document, $encoding = null ) {
_deprecated_function( __METHOD__, '1.5.0', 'AmpProject\Dom\Document::fromHtml()' );
$options = Options::DEFAULTS;
if ( null !== $encoding ) {
$options[ Document\Option::ENCODING ] = $encoding;
}
return Document::fromHtml( $document, $options );
}
/**
* Determine whether a node can be in the head.
*
* @link https://github.com/ampproject/amphtml/blob/445d6e3be8a5063e2738c6f90fdcd57f2b6208be/validator/engine/htmlparser.js#L83-L100
* @link https://www.w3.org/TR/html5/document-metadata.html
* @codeCoverageIgnore
* @deprecated Use AmpProject\Dom\Document->isValidHeadNode() instead.
*
* @param DOMNode $node Node.
* @return bool Whether valid head node.
*/
public static function is_valid_head_node( DOMNode $node ) {
_deprecated_function( __METHOD__, '1.5.0', 'AmpProject\Dom\Document->isValidHeadNode()' );
return Document::fromNode( $node )->isValidHeadNode( $node );
}
/**
* Return a valid Dom\Document representing arbitrary HTML content passed as a parameter.
*
* @see Reciprocal function get_content_from_dom()
*
* @since 0.2
*
* @param string $content Valid HTML content to be represented by a Dom\Document.
* @param string $encoding Optional. Encoding to use for the content. Defaults to `get_bloginfo( 'charset' )`.
*
* @return Document|false Returns a DOM document, or false if conversion failed.
*/
public static function get_dom_from_content( $content, $encoding = null ) {
// Detect encoding from the current WordPress installation.
if ( null === $encoding ) {
$encoding = get_bloginfo( 'charset' );
}
/*
* Wrap in dummy tags, since XML needs one parent node.
* It also makes it easier to loop through nodes.
* We can later use this to extract our nodes.
*/
$document = "<html><head></head><body>{$content}</body></html>";
$options = Options::DEFAULTS;
$options[ Document\Option::ENCODING ] = $encoding;
return Document::fromHtml( $document, $options );
}
/**
* Return valid HTML *body* content extracted from the Dom\Document passed as a parameter.
*
* @since 0.2
* @see AMP_DOM_Utils::get_content_from_dom_node() Reciprocal function.
*
* @param Document $dom Represents an HTML document from which to extract HTML content.
* @return string Returns the HTML content of the body element represented in the Dom\Document.
*/
public static function get_content_from_dom( Document $dom ) {
return preg_replace(
static::HTML_BODY_CONTENTS_PATTERN,
'$1',
$dom->saveHTML( $dom->body )
);
}
/**
* Return valid HTML content extracted from the DOMNode passed as a parameter.
*
* @since 0.6
* @see AMP_DOM_Utils::get_dom() Where the operations in this method are mirrored.
* @see AMP_DOM_Utils::get_content_from_dom() Reciprocal function.
* @codeCoverageIgnore
* @deprecated Use Dom\Document->saveHtml( $node ) instead.
*
* @param Document $dom Represents an HTML document.
* @param DOMElement $node Represents an HTML element of the $dom from which to extract HTML content.
* @return string Returns the HTML content represented in the DOMNode
*/
public static function get_content_from_dom_node( Document $dom, $node ) {
_deprecated_function( __METHOD__, '1.5.0', 'AmpProject\Dom\Document::saveHtml()' );
return $dom->saveHTML( $node );
}
/**
* Create a new node w/attributes (a DOMElement) and add to the passed Dom\Document.
*
* @since 0.2
*
* @param Document $dom A representation of an HTML document to add the new node to.
* @param string $tag A valid HTML element tag for the element to be added.
* @param string[] $attributes One of more valid attributes for the new node.
*
* @return Element|false The element for the given $tag, or false on failure
*/
public static function create_node( Document $dom, $tag, $attributes ) {
$node = $dom->createElement( $tag );
self::add_attributes_to_node( $node, $attributes );
return $node;
}
/**
* Extract a DOMElement node's HTML element attributes and return as an array.
*
* @since 0.2
*
* @param DOMElement $node Represents an HTML element for which to extract attributes.
*
* @return string[] The attributes for the passed node, or an
* empty array if it has no attributes.
*/
public static function get_node_attributes_as_assoc_array( $node ) {
$attributes = [];
if ( ! $node->hasAttributes() ) {
return $attributes;
}
foreach ( $node->attributes as $attribute ) {
$attributes[ $attribute->nodeName ] = $attribute->nodeValue;
}
return $attributes;
}
/**
* Add one or more HTML element attributes to a node's DOMElement.
*
* @since 0.2
*
* @param DOMElement $node Represents an HTML element.
* @param string[] $attributes One or more attributes for the node's HTML element.
*/
public static function add_attributes_to_node( $node, $attributes ) {
foreach ( $attributes as $name => $value ) {
try {
$node->setAttribute( $name, $value );
} catch ( DOMException $e ) {
/*
* Catch a "Invalid Character Error" when libxml is able to parse attributes with invalid characters,
* but it throws error when attempting to set them via DOM methods. For example, '...this' can be parsed
* as an attribute but it will throw an exception when attempting to setAttribute().
*/
continue;
}
}
}
/**
* Determines if a DOMElement's node is empty or not..
*
* @since 0.2
*
* @param DOMElement $node Represents an HTML element.
* @return bool Returns true if the DOMElement has no child nodes and
* the textContent property of the DOMElement is empty;
* Otherwise it returns false.
*/
public static function is_node_empty( $node ) {
return false === $node->hasChildNodes() && empty( $node->textContent );
}
/**
* Forces HTML element closing tags given a Dom\Document and optional DOMElement.
*
* @since 0.2
* @codeCoverageIgnore
* @deprecated
* @internal
*
* @param Document $dom Represents HTML document on which to force closing tags.
* @param DOMElement $node Represents HTML element to start closing tags on.
* If not passed, defaults to first child of body.
*/
public static function recursive_force_closing_tags( $dom, $node = null ) {
_deprecated_function( __METHOD__, '0.7' );
if ( null === $node ) {
$node = $dom->body;
}
if ( XML_ELEMENT_NODE !== $node->nodeType ) {
return;
}
if ( self::is_self_closing_tag( $node->nodeName ) ) {
/*
* Ensure there is no text content to accidentally force a child
*/
$node->textContent = '';
return;
}
if ( self::is_node_empty( $node ) ) {
$text_node = $dom->createTextNode( '' );
$node->appendChild( $text_node );
return;
}
$num_children = $node->childNodes->length;
for ( $i = $num_children - 1; $i >= 0; $i -- ) {
$child = $node->childNodes->item( $i );
self::recursive_force_closing_tags( $dom, $child );
}
}
/**
* Determines if an HTML element tag is validly a self-closing tag per W3C HTML5 specs.
*
* @since 0.2
*
* @param string $tag Tag.
* @return bool Returns true if a valid self-closing tag, false if not.
*/
private static function is_self_closing_tag( $tag ) {
return in_array( strtolower( $tag ), Tag::SELF_CLOSING_TAGS, true );
}
/**
* Check whether a given element has a specific class.
*
* @since 1.4.0
*
* @param DOMElement $element Element to check the classes of.
* @param string $class Class to check for.
* @return bool Whether the element has the requested class.
*/
public static function has_class( DOMElement $element, $class ) {
if ( ! $element->hasAttribute( 'class' ) ) {
return false;
}
$classes = $element->getAttribute( 'class' );
return in_array( $class, preg_split( '/\s/', $classes ), true );
}
/**
* Get the ID for an element.
*
* If the element does not have an ID, create one first.
*
* @since 1.4.0
* @since 1.5.1 Deprecated for AmpProject\Dom\Document::getElementId()
*
* @deprecated Use AmpProject\Dom\Document::getElementId() instead.
*
* @param DOMElement|Element $element Element to get the ID for.
* @param string $prefix Optional. Defaults to 'amp-wp-id'.
* @return string ID to use.
*/
public static function get_element_id( $element, $prefix = 'amp-wp-id' ) {
_deprecated_function(
'AMP_DOM_Utils::get_element_id',
'1.5.1',
'Use AmpProject\Amp\Dom\Document::getElementId() instead'
);
return Document::fromNode( $element )->getElementId( $element, $prefix );
}
/**
* Register an AMP action to an event on a given element.
*
* If the element already contains one or more events or actions, the method
* will assemble them in a smart way.
*
* @since 1.4.0
*
* @param DOMElement $element Element to add an action to.
* @param string $event Event to trigger the action on.
* @param string $action Action to add.
*/
public static function add_amp_action( DOMElement $element, $event, $action ) {
$event_action_string = "{$event}:{$action}";
if ( ! $element->hasAttribute( 'on' ) ) {
// There's no "on" attribute yet, so just add it and be done.
$element->setAttribute( 'on', $event_action_string );
return;
}
$element->setAttribute(
'on',
self::merge_amp_actions(
$element->getAttribute( 'on' ),
$event_action_string
)
);
}
/**
* Merge two sets of AMP events & actions.
*
* @since 1.4.0
*
* @param string $first First event/action string.
* @param string $second First event/action string.
* @return string Merged event/action string.
*/
public static function merge_amp_actions( $first, $second ) {
$events = [];
foreach ( [ $first, $second ] as $event_action_string ) {
$matches = [];
$results = preg_match_all( self::AMP_EVENT_ACTIONS_REGEX_PATTERN, $event_action_string, $matches );
if ( ! $results ) {
continue;
}
foreach ( $matches['event'] as $index => $event ) {
$events[ $event ][] = $matches['actions'][ $index ];
}
}
$value_strings = [];
foreach ( $events as $event => $action_strings_array ) {
$actions_array = [];
array_walk(
$action_strings_array,
static function ( $actions ) use ( &$actions_array ) {
$matches = [];
$results = preg_match_all( self::AMP_ACTION_REGEX_PATTERN, $actions, $matches );
if ( ! $results ) {
$actions_array[] = $actions;
return;
}
$actions_array = array_merge( $actions_array, $matches['action'] );
}
);
$actions = implode( ',', array_unique( array_filter( $actions_array ) ) );
$value_strings[] = "{$event}:{$actions}";
}
return implode( ';', $value_strings );
}
/**
* Copy one or more attributes from one element to the other.
*
* @param array|string $attributes Attribute name or array of attribute names to copy.
* @param DOMElement $from DOM element to copy the attributes from.
* @param DOMElement $to DOM element to copy the attributes to.
* @param string $default_separator Default separator to use for multiple values if the attribute is not known.
*/
public static function copy_attributes( $attributes, DOMElement $from, DOMElement $to, $default_separator = ',' ) {
foreach ( (array) $attributes as $attribute ) {
if ( $from->hasAttribute( $attribute ) ) {
$values = $from->getAttribute( $attribute );
if ( $to->hasAttribute( $attribute ) ) {
switch ( $attribute ) {
case 'on':
$values = self::merge_amp_actions( $to->getAttribute( $attribute ), $values );
break;
case 'class':
$values = $to->getAttribute( $attribute ) . ' ' . $values;
break;
default:
$values = $to->getAttribute( $attribute ) . $default_separator . $values;
}
}
$to->setAttribute( $attribute, $values );
}
}
}
}