Invision Power Services, Inc. * @copyright (c) Invision Power Services, Inc. * @license https://www.invisioncommunity.com/legal/standards/ * @package Invision Community * @since 8 Feb 2017 */ namespace IPS\Text; /* To prevent PHP errors (extending class does not exist) revealing path */ use DOMDocument; use DOMNode; use DOMNodeList; use function defined; use function substr; if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) ) { header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' ); exit; } /** * DOM Parser */ class DOMParser { /** * @brief Callback to parse a DOMElement object */ protected $elementParser; /** * @brief Callback to parse a DOMText object */ protected $textParser; /** * Parse * * @param string $value Contents to parse * @param callback $elementParser Callback to parse a DOMElement object. Is expected to call `$parent->appendChild( $element )` after doing any parsing and call `$parser->_parseDomNodeList()`. `function ( \DOMElement $element, \DOMNode $parent, \IPS\Text\DOMParser $parser )` * @param callback|null $textParser Callback to parse a DOMText object. Is expected to call `$parent->appendChild( $element )` after doing any parsing. `function ( \DOMText $textNode, \DOMNode $parent, \IPS\Text\DOMParser $parser )` * @return string */ public static function parse( string $value, callable $elementParser, callable $textParser = NULL ): string { $content = static::getDocumentBodyContents( ( new static( $elementParser, $textParser ) )->parseValueIntoDocument( $value ) ); /* Replace file storage tags */ $content = preg_replace( '/<fileStore\.([\d\w\_]+?)>/i', '', $content ); /* DOMDocument::saveHTML will encode the base_url brackets, so we need to make sure it's in the expected format. */ return str_replace( '<___base_url___>', '<___base_url___>', $content ); } /** * Constructor * * @param callback $elementParser Callback to parse a DOMElement object. Is expected to call `$parent->appendChild( $element )` after doing any parsing and call `$parser->_parseDomNodeList()`. `function ( \DOMElement $element, \DOMNode $parent, \IPS\Text\DOMParser $parser )` * @param callback|null $textParser Callback to parse a DOMText object. Is expected to call `$parent->appendChild( $element )` after doing any parsing. `function ( \DOMText $textNode, \DOMNode $parent, \IPS\Text\DOMParser $parser )` * @return void */ public function __construct( callable $elementParser, callable $textParser = NULL ) { $this->elementParser = $elementParser; $this->textParser = $textParser; } /** * Parse Value into DOMDocument * * @param string $value Contents to parse * @return \IPS\Xml\DOMDocument */ public function parseValueIntoDocument( string $value ): \IPS\Xml\DOMDocument { /* Load the value into a DOMDocument */ $source = new \IPS\Xml\DOMDocument( '1.0', 'UTF-8' ); $source->loadHTML( \IPS\Xml\DOMDocument::wrapHtml( $value ) ); /* Create a new DOMDocument which we will move nodes into */ $document = new \IPS\Xml\DOMDocument( '1.0', 'UTF-8' ); /* Parse */ $this->_parseDomNode( $source, $document ); /* Return */ return $document; } /** * Parse DOMNode * * @param DOMNode $node The node from the source document to parse * @param DOMNode $parent The node from the new document which will be this node's parent * @return void */ public function _parseDomNode( DOMNode $node, DOMNode &$parent ) : void { switch ( $node->nodeType ) { /* This is the main DOMDocument object and it contains HTML. We just need to loop children */ case XML_HTML_DOCUMENT_NODE: $this->_parseDomNodeList( $node->childNodes, $parent ); break; /* This is a HTML element (e.g. ,

, , etc.) represented as a DOMElement object. Parse it. */ case XML_ELEMENT_NODE: $function = $this->elementParser; $function( $node, $parent, $this ); break; /* This is text represented as a DOMText object. Parse it. */ case XML_TEXT_NODE: if ( $this->textParser ) { $function = $this->textParser; $function( $node, $parent, $this ); } else { $parent->appendChild( $parent->ownerDocument->importNode( $node ) ); } break; /* This is text represented as a DOMCharacterData object, for example, the contents of a