Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 47
SpanHandler
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 3
600
0.00% covered (danger)
0.00%
0 / 47
 __construct
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 handle
0.00% covered (danger)
0.00%
0 / 1
462
0.00% covered (danger)
0.00%
0 / 42
 isRecognizedSpanWrapper
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 3
<?php
declare( strict_types = 1 );
namespace Wikimedia\Parsoid\Html2Wt\DOMHandlers;
use Wikimedia\Parsoid\Core\MediaStructure;
use Wikimedia\Parsoid\DOM\Element;
use Wikimedia\Parsoid\DOM\Node;
use Wikimedia\Parsoid\DOM\Text;
use Wikimedia\Parsoid\Html2Wt\LinkHandlerUtils;
use Wikimedia\Parsoid\Html2Wt\SerializerState;
use Wikimedia\Parsoid\Html2Wt\WTSUtils;
use Wikimedia\Parsoid\Tokens\KV;
use Wikimedia\Parsoid\Utils\DOMCompat;
use Wikimedia\Parsoid\Utils\DOMDataUtils;
use Wikimedia\Parsoid\Utils\DOMUtils;
use Wikimedia\Parsoid\Utils\Utils;
use Wikimedia\Parsoid\Utils\WTUtils;
class SpanHandler extends DOMHandler {
    public function __construct() {
        parent::__construct( false );
    }
    /** @inheritDoc */
    public function handle(
        Element $node, SerializerState $state, bool $wrapperUnmodified = false
    ): ?Node {
        $env = $state->getEnv();
        $dp = DOMDataUtils::getDataParsoid( $node );
        if ( self::isRecognizedSpanWrapper( $node ) ) {
            if ( DOMUtils::hasTypeOf( $node, 'mw:Nowiki' ) ) {
                $ext = $env->getSiteConfig()->getExtTagImpl( 'nowiki' );
                $src = $ext->domToWikitext( $state->extApi, $node, $wrapperUnmodified );
                $state->singleLineContext->disable();
                $state->serializer->emitWikitext( $src, $node );
                $state->singleLineContext->pop();
            } elseif ( WTUtils::isInlineMedia( $node ) ) {
                LinkHandlerUtils::figureHandler(
                    $state, $node, MediaStructure::parse( $node )
                );
            } elseif (
                DOMUtils::hasTypeOf( $node, 'mw:Entity' ) &&
                DOMUtils::hasNChildren( $node, 1 )
            ) {
                $contentSrc = ( $node->textContent != '' ) ? $node->textContent
                    : DOMCompat::getInnerHTML( $node );
                // handle a new mw:Entity (not handled by selser) by
                // serializing its children
                if ( isset( $dp->src ) && $contentSrc === ( $dp->srcContent ?? null ) ) {
                    $state->serializer->emitWikitext( $dp->src, $node );
                } elseif ( $node->firstChild instanceof Text ) {
                    $state->emitChunk(
                        Utils::entityEncodeAll( $node->firstChild->nodeValue ),
                        $node->firstChild );
                } else {
                    $state->serializeChildren( $node );
                }
            } elseif ( DOMUtils::hasTypeOf( $node, 'mw:DisplaySpace' ) ) {
                // FIXME(T254501): Turn this into an `PHPUtils::unreachable()`
                $state->emitChunk( ' ', $node );
            } elseif ( DOMUtils::matchTypeOf( $node, '#^mw:Placeholder(/|$)#' ) ) {
                if ( isset( $dp->src ) ) {
                    $this->emitPlaceholderSrc( $node, $state );
                    return $node->nextSibling;
                } else {
                    ( new FallbackHTMLHandler )->handle( $node, $state );
                }
            }
        } elseif ( $node->hasAttribute( 'data-mw-selser-wrapper' ) ) {
            $state->serializeChildren( $node );
        } else {
            $kvs = array_filter( WTSUtils::getAttributeKVArray( $node ), static function ( KV $kv ) {
                return !preg_match( '/^data-parsoid/', $kv->k )
                    && ( $kv->k !== DOMDataUtils::DATA_OBJECT_ATTR_NAME )
                    && !( $kv->k === 'id' && preg_match( '/^mw[\w-]{2,}$/D', $kv->v ) );
            } );
            if ( !empty( $dp->misnested ) && ( $dp->stx ?? null ) !== 'html'
                && !count( $kvs )
            ) {
                // Discard span wrappers added to flag misnested content.
                // Warn since selser should have reused source.
                $env->log( 'warn', 'Serializing misnested content: ' . DOMCompat::getOuterHTML( $node ) );
                $state->serializeChildren( $node );
            } else {
                // Fall back to plain HTML serialization for spans created
                // by the editor.
                ( new FallbackHTMLHandler )->handle( $node, $state );
            }
        }
        return $node->nextSibling;
    }
    /**
     * @param Element $node
     * @return string|null
     */
    private static function isRecognizedSpanWrapper( Element $node ): ?string {
        return DOMUtils::matchTypeOf(
            $node,
            // FIXME(T254501): Remove mw:DisplaySpace
            '#^mw:('
                . 'Nowiki|Entity|DisplaySpace|Placeholder(/\w+)?'
                . '|(Image|Video|Audio)(/(Frameless|Frame|Thumb))?'
                . ')$#'
        );
    }
}