Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
PWrapState
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 4
182
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 reset
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 processOptionalNode
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
 unwrapTrailingPWrapOptionalNodes
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\Wt2Html\DOM\Processors;
5
6use Wikimedia\Parsoid\Config\Env;
7use Wikimedia\Parsoid\DOM\Element;
8use Wikimedia\Parsoid\DOM\Node;
9use Wikimedia\Parsoid\Utils\DOMCompat;
10use Wikimedia\Parsoid\Utils\DOMUtils;
11
12class PWrapState {
13
14    private const RANGE_TYPE_RE = '!^mw:(Transclusion(/|$)|Param(/|$)|Annotation/)!';
15
16    /** @var ?Element */
17    public $p = null;
18
19    /** @var bool */
20    private $hasOptionalNode = false;
21
22    /**
23     * About ids of starts we've seen in this paragraph
24     *
25     * @var array
26     */
27    private $seenStarts = [];
28
29    private Env $env;
30
31    public function __construct( Env $env ) {
32        $this->env = $env;
33    }
34
35    /**
36     * Unwrap + reset
37     */
38    public function reset() {
39        $this->unwrapTrailingPWrapOptionalNodes();
40
41        $this->p = null;
42        $this->hasOptionalNode = false;
43        $this->seenStarts = [];
44    }
45
46    /**
47     * Record that we've encountered an optional node to potentially unwrap
48     *
49     * @param Node $n
50     */
51    public function processOptionalNode( Node $n ) {
52        $t = DOMUtils::matchNameAndTypeOf( $n, 'meta', self::RANGE_TYPE_RE );
53        $this->hasOptionalNode = (bool)$t || $this->hasOptionalNode;
54        if ( $t && !str_ends_with( $t, '/End' ) ) {
55            '@phan-var Element $n';  // @var Element $n
56            $about = DOMCompat::getAttribute( $n, 'about' );
57            if ( $about !== null ) {
58                $this->seenStarts[$about] = true;
59            }
60        }
61    }
62
63    /**
64     * Unwrap a run of trailing nodes that don't need p-wrapping.
65     * This only matters for meta tags representing transclusions
66     * and annotations. Unwrapping can prevent unnecessary expansion
67     * of template/annotation ranges.
68     */
69    private function unwrapTrailingPWrapOptionalNodes() {
70        if ( $this->hasOptionalNode ) {
71            $lastChild = $this->p->lastChild;
72            while ( PWrap::pWrapOptional( $this->env, $lastChild ) ) {
73                $t = DOMUtils::matchNameAndTypeOf( $lastChild, 'meta', self::RANGE_TYPE_RE );
74                if ( $t && str_ends_with( $t, '/End' ) ) {
75                    '@phan-var Element $lastChild';  // @var Element $lastChild
76                    // Check if one of its prior siblings has a matching opening tag.
77                    // If so, we are done with unwrapping here since we don't want to
78                    // hoist this closing tag by itself.
79                    $aboutId = DOMCompat::getAttribute( $lastChild, 'about' );
80                    if ( $this->seenStarts[$aboutId] ?? null ) {
81                        break;
82                    }
83                }
84                $this->p->parentNode->insertBefore( $lastChild, $this->p->nextSibling );
85                $lastChild = $this->p->lastChild;
86            }
87        }
88    }
89
90}