Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 19
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 / 19
0.00% covered (danger)
0.00%
0 / 4
156
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 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 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            $this->seenStarts[DOMCompat::getAttribute( $n, 'about' )] = true;
57        }
58    }
59
60    /**
61     * Unwrap a run of trailing nodes that don't need p-wrapping.
62     * This only matters for meta tags representing transclusions
63     * and annotations. Unwrapping can prevent unnecessary expansion
64     * of template/annotation ranges.
65     */
66    private function unwrapTrailingPWrapOptionalNodes() {
67        if ( $this->hasOptionalNode ) {
68            $lastChild = $this->p->lastChild;
69            while ( PWrap::pWrapOptional( $this->env, $lastChild ) ) {
70                $t = DOMUtils::matchNameAndTypeOf( $lastChild, 'meta', self::RANGE_TYPE_RE );
71                if ( $t && str_ends_with( $t, '/End' ) ) {
72                    '@phan-var Element $lastChild';  // @var Element $lastChild
73                    // Check if one of its prior siblings has a matching opening tag.
74                    // If so, we are done with unwrapping here since we don't want to
75                    // hoist this closing tag by itself.
76                    $aboutId = DOMCompat::getAttribute( $lastChild, 'about' );
77                    if ( $this->seenStarts[$aboutId] ?? null ) {
78                        break;
79                    }
80                }
81                $this->p->parentNode->insertBefore( $lastChild, $this->p->nextSibling );
82                $lastChild = $this->p->lastChild;
83            }
84        }
85    }
86
87}