src/Models/Dom/RichText.php
private static function insertSpans($text, array $spans, $linkResolver = null)
    {
        if (empty($spans)) {
            return htmlentities($text, null, 'UTF-8');
        }
        $tagsStart = array();
        $tagsEnd = array();
        foreach ($spans as $span) {
            $span = (object) $span;
            if (!array_key_exists($span->start, $tagsStart)) {
                $tagsStart[$span->start] = array();
            }
            if (!array_key_exists($span->end, $tagsEnd)) {
                $tagsEnd[$span->end] = array();
            }
            array_push($tagsStart[$span->start], $span);
            array_push($tagsEnd[$span->end], $span);
        }
        $c = null;
        $html = '';
        $stack = array();
        for ($pos = 0, $len = strlen($text) + 1; $pos < $len; $pos++) { // Looping to length + 1 to catch closing tags
            if (array_key_exists($pos, $tagsEnd)) {
                foreach ($tagsEnd[$pos] as $endTag) {
                    // Close a tag
                    $tag = array_pop($stack);
                    // Continue only if block contains content.
                    if ($tag && $tag['span']) {
                        $innerHtml = trim(RichText::serialize((object)$tag['span'], $tag['text'], $linkResolver));
                        if (count($stack) == 0) {
                            // The tag was top level
                            $html .= $innerHtml;
                        } else {
                            // Add the content to the parent tag
                            $last = array_pop($stack);
                            $last['text'] = $last['text'] . $innerHtml;
                            array_push($stack, $last);
                        }
                    }
                }
            }
            if (array_key_exists($pos, $tagsStart)) {
                // Sort bigger tags first to ensure the right tag hierarchy
                $sspans = $tagsStart[$pos];
                $spanSort = function ($a, $b) {
                    return ($b->end - $b->start) - ($a->end - $a->start);
                };
                usort($sspans, $spanSort);
                foreach ($sspans as $span) {
                    // Open a tag
                    array_push($stack, array(
                        'span' => $span,
                        'text' => ''
                    ));
                }
            }
            if ($pos < strlen($text)) {
                $c = mb_substr($text, $pos, 1, 'UTF-8');
                if (count($stack) == 0) {
                    // Top-level text
                    $html .= htmlentities($c, null, 'UTF-8');
                } else {
                    // Inner text of a span
                    $last_idx = count($stack) - 1;
                    $last = $stack[$last_idx];
                    $stack[$last_idx] = array(
                        'span' => $last['span'],
                        'text' => $last['text'] . htmlentities($c, null, 'UTF-8')
                    );
                }
            }
        }
        return $html;
    }
private static function insertSpans($text, array $spans, $linkResolver = null)
    {
        if (empty($spans)) {
            return htmlentities($text, null, 'UTF-8');
        }
        $tagsStart = array();
        $tagsEnd = array();
        foreach ($spans as $span) {
            $span = (object) $span;
            if (!array_key_exists($span->start, $tagsStart)) {
                $tagsStart[$span->start] = array();
            }
            if (!array_key_exists($span->end, $tagsEnd)) {
                $tagsEnd[$span->end] = array();
            }
            array_push($tagsStart[$span->start], $span);
            array_push($tagsEnd[$span->end], $span);
        }
        $c = null;
        $html = '';
        $stack = array();
        for ($pos = 0, $len = strlen($text) + 1; $pos < $len; $pos++) { // Looping to length + 1 to catch closing tags
            if (array_key_exists($pos, $tagsEnd)) {
                foreach ($tagsEnd[$pos] as $endTag) {
                    // Close a tag
                    $tag = array_pop($stack);
                    // Continue only if block contains content.
                    if ($tag && $tag['span']) {
                        $innerHtml = trim(RichText::serialize((object)$tag['span'], $tag['text'], $linkResolver));
                        if (count($stack) == 0) {
                            // The tag was top level
                            $html .= $innerHtml;
                        } else {
                            // Add the content to the parent tag
                            $last = array_pop($stack);
                            $last['text'] = $last['text'] . $innerHtml;
                            array_push($stack, $last);
                        }
                    }
                }
            }
            if (array_key_exists($pos, $tagsStart)) {
                // Sort bigger tags first to ensure the right tag hierarchy
                $sspans = $tagsStart[$pos];
                $spanSort = function ($a, $b) {
                    return ($b->end - $b->start) - ($a->end - $a->start);
                };
                usort($sspans, $spanSort);
                foreach ($sspans as $span) {
                    // Open a tag
                    array_push($stack, array(
                        'span' => $span,
                        'text' => ''
                    ));
                }
            }
            if ($pos < strlen($text)) {
                $c = mb_substr($text, $pos, 1, 'UTF-8');
                if (count($stack) == 0) {
                    // Top-level text
                    $html .= htmlentities($c, null, 'UTF-8');
                } else {
                    // Inner text of a span
                    $last_idx = count($stack) - 1;
                    $last = $stack[$last_idx];
                    $stack[$last_idx] = array(
                        'span' => $last['span'],
                        'text' => $last['text'] . htmlentities($c, null, 'UTF-8')
                    );
                }
            }
        }
        return $html;
    }
Transforms a text block into HTML


@param string                $text           the raw text of the block
@param array                 $spans          the spans of the block
@param LinkResolver $linkResolver   the link resolver

@return string the HTML representation of the block