src/Range.php
/**
     * Extracts the content of the Range from the node tree and places it in a
     * DocumentFragment.
     *
     * @see https://dom.spec.whatwg.org/#dom-range-extractcontents
     */
Extracts the content of the Range from the node tree and places it in a
DocumentFragment.


Extracts the content of the Range from the node tree and places it in a DocumentFragment .
public function extractContents(): DocumentFragment
    {
        $fragment = $this->startNode->getNodeDocument()->createDocumentFragment();
        if ($this->startNode === $this->endNode && $this->startOffset === $this->endOffset) {
            return $fragment;
        }
        $originalStartNode = $this->startNode;
        $originalStartOffset = $this->startOffset;
        $originalEndNode = $this->endNode;
        $originalEndOffset = $this->endOffset;
        if (
            $originalStartNode === $originalEndNode
            && ($originalStartNode instanceof Text
                || $originalStartNode instanceof ProcessingInstruction
                || $originalStartNode instanceof Comment)
        ) {
            $clone = $originalStartNode->cloneNodeInternal();
            $clone->data = $originalStartNode->substringData(
                $originalStartOffset,
                $originalEndOffset - $originalStartOffset
            );
            $fragment->appendChild($clone);
            $originalStartNode->doReplaceData(
                $originalStartOffset,
                $originalEndOffset - $originalStartOffset,
                ''
            );
            return $fragment;
        }
        $commonAncestor = Node::getCommonAncestor(
            $originalStartNode,
            $originalEndNode
        );
        $firstPartiallyContainedChild = null;
        if (!$originalStartNode->contains($originalEndNode)) {
            foreach ($commonAncestor->childNodes as $node) {
                if ($this->isPartiallyContainedNode($node)) {
                    $firstPartiallyContainedChild = $node;
                    break;
                }
            }
        }
        $lastPartiallyContainedChild = null;
        if (!$originalEndNode->contains($originalStartNode)) {
            foreach (array_reverse(iterator_to_array($commonAncestor->childNodes)) as $node) {
                if ($this->isPartiallyContainedNode($node)) {
                    $lastPartiallyContainedChild = $node;
                    break;
                }
            }
        }
        $containedChildren = [];
        foreach ($commonAncestor->childNodes as $childNode) {
            if ($this->isFullyContainedNode($childNode)) {
                if ($childNode instanceof DocumentType) {
                    throw new HierarchyRequestError();
                }
                $containedChildren[] = $childNode;
            }
        }
        if ($originalStartNode->contains($originalEndNode)) {
            $newNode = $originalStartNode;
            $newOffset = $originalStartOffset;
        } else {
            $referenceNode = $originalStartNode;
            $parent = $referenceNode->parentNode;
            while ($parent && !$parent->contains($originalEndNode)) {
                $referenceNode = $parent;
                $parent = $referenceNode->parentNode;
            }
            $newNode = $parent;
            $newOffset = $referenceNode->getTreeIndex() + 1;
        }
        if (
            $firstPartiallyContainedChild instanceof Text
            || $firstPartiallyContainedChild instanceof ProcessingInstruction
            || $firstPartiallyContainedChild instanceof Comment
        ) {
            $clone = $originalStartNode->cloneNodeInternal();
            $clone->data = $originalStartNode->substringData(
                $originalStartOffset,
                $originalStartNode->length - $originalStartOffset
            );
            $fragment->appendChild($clone);
            $originalStartNode->doReplaceData(
                $originalStartOffset,
                $originalStartNode->length - $originalStartOffset,
                ''
            );
        } elseif ($firstPartiallyContainedChild) {
            $clone = $firstPartiallyContainedChild->cloneNodeInternal();
            $fragment->appendChild($clone);
            $subrange = clone $this;
            $subrange->startNode = $originalStartNode;
            $subrange->startOffset = $originalStartOffset;
            $subrange->endNode = $firstPartiallyContainedChild;
            $subrange->endOffset = $firstPartiallyContainedChild->getLength();
            $subfragment = $subrange->extractContents();
            $clone->appendChild($subfragment);
        }
        foreach ($containedChildren as $child) {
            $fragment->appendChild($child);
        }
        if (
            $lastPartiallyContainedChild instanceof Text
            || $lastPartiallyContainedChild instanceof ProcessingInstruction
            || $lastPartiallyContainedChild instanceof Comment
        ) {
            $clone = $originalEndNode->cloneNodeInternal();
            $clone->data = $originalEndNode->substringData(0, $originalEndOffset);
            $fragment->appendChild($clone);
            $originalEndNode->doReplaceData(0, $originalEndOffset, '');
        } elseif ($lastPartiallyContainedChild) {
            $clone = $lastPartiallyContainedChild->cloneNodeInternal();
            $fragment->appendChild($clone);
            $subrange = clone $this;
            $subrange->startNode = $lastPartiallyContainedChild;
            $subrange->startOffset = 0;
            $subrange->endNode = $originalEndNode;
            $subrange->endOffset = $originalEndOffset;
            $subfragment = $subrange->extractContents();
            $clone->appendChild($subfragment);
        }
        $this->startNode = $newNode;
        $this->startOffset = $newOffset;
        $this->endNode = $newNode;
        $this->endOffset = $newOffset;
        return $fragment;
    }
public function extractContents ( ) : DocumentFragment { $ fragment = $ this -> startNode -> getNodeDocument ( ) -> createDocumentFragment ( ) ; if ( $ this -> startNode === $ this -> endNode && $ this -> startOffset === $ this -> endOffset ) { return $ fragment ; } $ originalStartNode = $ this -> startNode ; $ originalStartOffset = $ this -> startOffset ; $ originalEndNode = $ this -> endNode ; $ originalEndOffset = $ this -> endOffset ; if ( $ originalStartNode === $ originalEndNode && ( $ originalStartNode instanceof Text || $ originalStartNode instanceof ProcessingInstruction || $ originalStartNode instanceof Comment ) ) { $ clone = $ originalStartNode -> cloneNodeInternal ( ) ; $ clone -> data = $ originalStartNode -> substringData ( $ originalStartOffset , $ originalEndOffset - $ originalStartOffset ) ; $ fragment -> appendChild ( $ clone ) ; $ originalStartNode -> doReplaceData ( $ originalStartOffset , $ originalEndOffset - $ originalStartOffset , '' ) ; return $ fragment ; } $ commonAncestor = Node :: getCommonAncestor ( $ originalStartNode , $ originalEndNode ) ; $ firstPartiallyContainedChild = null ; if ( ! $ originalStartNode -> contains ( $ originalEndNode ) ) { foreach ( $ commonAncestor -> childNodes as $ node ) { if ( $ this -> isPartiallyContainedNode ( $ node ) ) { $ firstPartiallyContainedChild = $ node ; break ; } } } $ lastPartiallyContainedChild = null ; if ( ! $ originalEndNode -> contains ( $ originalStartNode ) ) { foreach ( array_reverse ( iterator_to_array ( $ commonAncestor -> childNodes ) ) as $ node ) { if ( $ this -> isPartiallyContainedNode ( $ node ) ) { $ lastPartiallyContainedChild = $ node ; break ; } } } $ containedChildren = [ ] ; foreach ( $ commonAncestor -> childNodes as $ childNode ) { if ( $ this -> isFullyContainedNode ( $ childNode ) ) { if ( $ childNode instanceof DocumentType ) { throw new HierarchyRequestError ( ) ; } $ containedChildren [ ] = $ childNode ; } } if ( $ originalStartNode -> contains ( $ originalEndNode ) ) { $ newNode = $ originalStartNode ; $ newOffset = $ originalStartOffset ; } else { $ referenceNode = $ originalStartNode ; $ parent = $ referenceNode -> parentNode ; while ( $ parent && ! $ parent -> contains ( $ originalEndNode ) ) { $ referenceNode = $ parent ; $ parent = $ referenceNode -> parentNode ; } $ newNode = $ parent ; $ newOffset = $ referenceNode -> getTreeIndex ( ) + 1 ; } if ( $ firstPartiallyContainedChild instanceof Text || $ firstPartiallyContainedChild instanceof ProcessingInstruction || $ firstPartiallyContainedChild instanceof Comment ) { $ clone = $ originalStartNode -> cloneNodeInternal ( ) ; $ clone -> data = $ originalStartNode -> substringData ( $ originalStartOffset , $ originalStartNode -> length - $ originalStartOffset ) ; $ fragment -> appendChild ( $ clone ) ; $ originalStartNode -> doReplaceData ( $ originalStartOffset , $ originalStartNode -> length - $ originalStartOffset , '' ) ; } elseif ( $ firstPartiallyContainedChild ) { $ clone = $ firstPartiallyContainedChild -> cloneNodeInternal ( ) ; $ fragment -> appendChild ( $ clone ) ; $ subrange = clone $ this ; $ subrange -> startNode = $ originalStartNode ; $ subrange -> startOffset = $ originalStartOffset ; $ subrange -> endNode = $ firstPartiallyContainedChild ; $ subrange -> endOffset = $ firstPartiallyContainedChild -> getLength ( ) ; $ subfragment = $ subrange -> extractContents ( ) ; $ clone -> appendChild ( $ subfragment ) ; } foreach ( $ containedChildren as $ child ) { $ fragment -> appendChild ( $ child ) ; } if ( $ lastPartiallyContainedChild instanceof Text || $ lastPartiallyContainedChild instanceof ProcessingInstruction || $ lastPartiallyContainedChild instanceof Comment ) { $ clone = $ originalEndNode -> cloneNodeInternal ( ) ; $ clone -> data = $ originalEndNode -> substringData ( 0 , $ originalEndOffset ) ; $ fragment -> appendChild ( $ clone ) ; $ originalEndNode -> doReplaceData ( 0 , $ originalEndOffset , '' ) ; } elseif ( $ lastPartiallyContainedChild ) { $ clone = $ lastPartiallyContainedChild -> cloneNodeInternal ( ) ; $ fragment -> appendChild ( $ clone ) ; $ subrange = clone $ this ; $ subrange -> startNode = $ lastPartiallyContainedChild ; $ subrange -> startOffset = 0 ; $ subrange -> endNode = $ originalEndNode ; $ subrange -> endOffset = $ originalEndOffset ; $ subfragment = $ subrange -> extractContents ( ) ; $ clone -> appendChild ( $ subfragment ) ; } $ this -> startNode = $ newNode ; $ this -> startOffset = $ newOffset ; $ this -> endNode = $ newNode ; $ this -> endOffset = $ newOffset ; return $ fragment ; }
Extracts the content of the Range from the node tree and places it in a
 DocumentFragment.
Extracts the content of the Range from the node tree and places it in a DocumentFragment .
// In this case, last partially contained child is original end node