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 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 57
TokenTransformManager
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 10
870
0.00% covered (danger)
0.00%
0 / 57
 __construct
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 6
 setPipelineId
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 getFrame
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 getOptions
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 addTransformer
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 shuttleTokensToEndOfStage
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 4
 processChunk
0.00% covered (danger)
0.00%
0 / 1
182
0.00% covered (danger)
0.00%
0 / 26
 resetState
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 5
 process
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 1
 processChunkily
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 5
<?php
declare( strict_types = 1 );
namespace Wikimedia\Parsoid\Wt2Html;
use Generator;
use Wikimedia\Parsoid\Config\Env;
use Wikimedia\Parsoid\Config\Profile;
use Wikimedia\Parsoid\Tokens\SelfclosingTagTk;
use Wikimedia\Parsoid\Utils\PHPUtils;
use Wikimedia\Parsoid\Wt2Html\TT\TokenHandler;
use Wikimedia\Parsoid\Wt2Html\TT\TraceProxy;
/**
 * Token transformation manager. Individual transformations
 * implement the TokenHandler interface. The parser pipeline
 * registers individual transformers.
 *
 * See https://www.mediawiki.org/wiki/Parsoid/Token_stream_transformations
 * for more documentation.  This abstract class could eventually be
 * eliminated and the various token transforms just extend PipelineStage
 * directly.
 */
class TokenTransformManager extends PipelineStage {
    /** @var array */
    private $options;
    /** @var string */
    private $traceType = "";
    /** @var bool */
    private $traceEnabled;
    /** @var TokenHandler[] */
    private $transformers = [];
    /** @var int For TraceProxy */
    public $tokenTimes = 0;
    /** @var Profile|null For TraceProxy */
    public $profile;
    /** @var bool */
    private $hasShuttleTokens = false;
    /**
     * @param Env $env
     * @param array $options
     * @param string $stageId
     * @param ?PipelineStage $prevStage
     */
    public function __construct(
        Env $env, array $options, string $stageId,
        ?PipelineStage $prevStage = null
    ) {
        parent::__construct( $env, $prevStage );
        $this->options = $options;
        $this->pipelineId = null;
        $this->traceType = 'trace/ttm:' . str_replace( 'TokenTransform', '', $stageId );
        $this->traceEnabled = $env->hasTraceFlags();
    }
    /**
     * @param int $id
     */
    public function setPipelineId( int $id ): void {
        parent::setPipelineId( $id );
        foreach ( $this->transformers as $transformer ) {
            $transformer->setPipelineId( $id );
        }
    }
    /**
     * @return Frame
     */
    public function getFrame(): Frame {
        return $this->frame;
    }
    /**
     * @return array
     */
    public function getOptions(): array {
        return $this->options;
    }
    /**
     * @inheritDoc
     */
    public function addTransformer( TokenHandler $t ): void {
        if ( $this->traceEnabled ) {
            $this->transformers[] = new TraceProxy( $this, $this->options, $this->traceType, $t );
        } else {
            $this->transformers[] = $t;
        }
    }
    /**
     * @param array $toks
     * @return array
     */
    public function shuttleTokensToEndOfStage( array $toks ): array {
        $this->hasShuttleTokens = true;
        $ttmEnd = new SelfclosingTagTk( 'mw:ttm-end' );
        $ttmEnd->dataAttribs->getTemp()->shuttleTokens = $toks;
        return [ $ttmEnd ];
    }
    /**
     * Push the tokens through all the registered transformers.
     * @inheritDoc
     */
    public function processChunk( array $tokens ): ?array {
        // Trivial case
        if ( count( $tokens ) === 0 ) {
            return $tokens;
        }
        $startTime = null;
        $profile = $this->profile = $this->env->profiling() ? $this->env->getCurrentProfile() : null;
        if ( $profile ) {
            $startTime = microtime( true );
            $this->tokenTimes = 0;
        }
        foreach ( $this->transformers as $transformer ) {
            if ( !$transformer->isDisabled() ) {
                if ( count( $tokens ) === 0 ) {
                    break;
                }
                $tokens = $transformer->process( $tokens );
            }
        }
        // Unpack tokens that were shuttled to the end of the stage.  This happens
        // when we used a nested pipeline to process tokens to the end of the
        // current stage but then they need to be reinserted into the stream
        // and we don't want them to be processed by subsequent handlers again.
        if ( $this->hasShuttleTokens ) {
            $this->hasShuttleTokens = false;
            $accum = [];
            foreach ( $tokens as $i => $t ) {
                if ( $t instanceof SelfclosingTagTk && $t->getName() === 'mw:ttm-end' ) {
                    $toks = $t->dataAttribs->getTemp()->shuttleTokens;
                    PHPUtils::pushArray( $accum, $toks );
                } else {
                    $accum[] = $t;
                }
            }
            $tokens = $accum;
        }
        if ( $profile ) {
            $profile->bumpTimeUse( 'TTM',
                ( microtime( true ) - $startTime ) * 1000 - $this->tokenTimes,
                'TTM' );
        }
        return $tokens;
    }
    /**
     * @inheritDoc
     */
    public function resetState( array $opts ): void {
        $this->hasShuttleTokens = false;
        parent::resetState( $opts );
        foreach ( $this->transformers as $transformer ) {
            $transformer->resetState( $opts );
        }
    }
    /**
     * See PipelineStage::process docs as well. This doc block refines
     * the generic arg types to be specific to this pipeline stage.
     *
     * Process a chunk of tokens.
     *
     * @param array $tokens Array of tokens to process
     * @param ?array $opts
     * @return array Returns the array of processed tokens
     */
    public function process( $tokens, ?array $opts = null ): array {
        return $this->processChunk( $tokens );
    }
    /**
     * @inheritDoc
     */
    public function processChunkily( $input, array $opts = null ): Generator {
        if ( $this->prevStage ) {
            foreach ( $this->prevStage->processChunkily( $input, $opts ) as $chunk ) {
                '@phan-var array $chunk'; // @var array $chunk
                yield $this->processChunk( $chunk );
            }
        } else {
            yield $this->process( $input, $opts );
        }
    }
}