Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
TokenTransformManager
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 10
702
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 setPipelineId
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getFrame
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getOptions
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addTransformer
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 shuttleTokensToEndOfStage
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 processChunk
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
156
 resetState
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 process
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 processChunkily
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\Wt2Html;
5
6use Generator;
7use Wikimedia\Parsoid\Config\Env;
8use Wikimedia\Parsoid\Config\Profile;
9use Wikimedia\Parsoid\Tokens\SelfclosingTagTk;
10use Wikimedia\Parsoid\Utils\PHPUtils;
11use Wikimedia\Parsoid\Wt2Html\TT\TokenHandler;
12use Wikimedia\Parsoid\Wt2Html\TT\TraceProxy;
13
14/**
15 * Token transformation manager. Individual transformations
16 * implement the TokenHandler interface. The parser pipeline
17 * registers individual transformers.
18 *
19 * See https://www.mediawiki.org/wiki/Parsoid/Token_stream_transformations
20 * for more documentation.  This abstract class could eventually be
21 * eliminated and the various token transforms just extend PipelineStage
22 * directly.
23 */
24class TokenTransformManager extends PipelineStage {
25    /** @var array */
26    private $options;
27
28    /** @var string */
29    private $traceType = "";
30
31    /** @var bool */
32    private $traceEnabled;
33
34    /** @var TokenHandler[] */
35    private $transformers = [];
36
37    /** @var int For TraceProxy */
38    public $tokenTimes = 0;
39
40    /** @var Profile|null For TraceProxy */
41    public $profile;
42
43    /** @var bool */
44    private $hasShuttleTokens = false;
45
46    public function __construct(
47        Env $env, array $options, string $stageId,
48        ?PipelineStage $prevStage = null
49    ) {
50        parent::__construct( $env, $prevStage );
51        $this->options = $options;
52        $this->pipelineId = null;
53        $this->traceType = 'trace/ttm:' . str_replace( 'TokenTransform', '', $stageId );
54        $this->traceEnabled = $env->hasTraceFlags();
55    }
56
57    public function setPipelineId( int $id ): void {
58        parent::setPipelineId( $id );
59        foreach ( $this->transformers as $transformer ) {
60            $transformer->setPipelineId( $id );
61        }
62    }
63
64    public function getFrame(): Frame {
65        return $this->frame;
66    }
67
68    public function getOptions(): array {
69        return $this->options;
70    }
71
72    /**
73     * @inheritDoc
74     */
75    public function addTransformer( TokenHandler $t ): void {
76        if ( $this->traceEnabled ) {
77            $this->transformers[] = new TraceProxy( $this, $this->options, $this->traceType, $t );
78        } else {
79            $this->transformers[] = $t;
80        }
81    }
82
83    public function shuttleTokensToEndOfStage( array $toks ): array {
84        $this->hasShuttleTokens = true;
85        $ttmEnd = new SelfclosingTagTk( 'mw:ttm-end' );
86        $ttmEnd->dataParsoid->getTemp()->shuttleTokens = $toks;
87        return [ $ttmEnd ];
88    }
89
90    /**
91     * Push the tokens through all the registered transformers.
92     * @inheritDoc
93     */
94    public function processChunk( array $tokens ): ?array {
95        // Trivial case
96        if ( !$tokens ) {
97            return $tokens;
98        }
99
100        $startTime = null;
101        $profile = $this->profile = $this->env->profiling() ? $this->env->getCurrentProfile() : null;
102
103        if ( $profile ) {
104            $startTime = microtime( true );
105            $this->tokenTimes = 0;
106        }
107
108        foreach ( $this->transformers as $transformer ) {
109            if ( !$transformer->isDisabled() ) {
110                if ( !$tokens ) {
111                    break;
112                }
113                $tokens = $transformer->process( $tokens );
114            }
115        }
116
117        // Unpack tokens that were shuttled to the end of the stage.  This happens
118        // when we used a nested pipeline to process tokens to the end of the
119        // current stage but then they need to be reinserted into the stream
120        // and we don't want them to be processed by subsequent handlers again.
121        if ( $this->hasShuttleTokens ) {
122            $this->hasShuttleTokens = false;
123            $accum = [];
124            foreach ( $tokens as $i => $t ) {
125                if ( $t instanceof SelfclosingTagTk && $t->getName() === 'mw:ttm-end' ) {
126                    $toks = $t->dataParsoid->getTemp()->shuttleTokens;
127                    PHPUtils::pushArray( $accum, $toks );
128                } else {
129                    $accum[] = $t;
130                }
131            }
132            $tokens = $accum;
133        }
134
135        if ( $profile ) {
136            $profile->bumpTimeUse( 'TTM',
137                ( microtime( true ) - $startTime ) * 1000 - $this->tokenTimes,
138                'TTM' );
139        }
140
141        return $tokens;
142    }
143
144    /**
145     * @inheritDoc
146     */
147    public function resetState( array $opts ): void {
148        $this->hasShuttleTokens = false;
149        parent::resetState( $opts );
150        foreach ( $this->transformers as $transformer ) {
151            $transformer->resetState( $opts );
152        }
153    }
154
155    /**
156     * See PipelineStage::process docs as well. This doc block refines
157     * the generic arg types to be specific to this pipeline stage.
158     *
159     * Process a chunk of tokens.
160     *
161     * @param array $tokens Array of tokens to process
162     * @param array $opts
163     * @return array Returns the array of processed tokens
164     */
165    public function process( $tokens, array $opts ): array {
166        return $this->processChunk( $tokens );
167    }
168
169    /**
170     * @inheritDoc
171     */
172    public function processChunkily( $input, array $opts ): Generator {
173        if ( $this->prevStage ) {
174            foreach ( $this->prevStage->processChunkily( $input, $opts ) as $chunk ) {
175                '@phan-var array $chunk'; // @var array $chunk
176                yield $this->processChunk( $chunk );
177            }
178        } else {
179            yield $this->process( $input, $opts );
180        }
181    }
182}