Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
DomSourceRange
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 13
420
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
 innerSubstr
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 innerStart
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 innerEnd
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 innerLength
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 openSubstr
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 closeSubstr
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 stripTags
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 offset
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 hasValidTagWidths
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
20
 fromTsr
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 fromArray
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 jsonSerialize
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\Core;
5
6use Wikimedia\Assert\Assert;
7use Wikimedia\Parsoid\Tokens\SourceRange;
8use Wikimedia\Parsoid\Utils\PHPUtils;
9
10/**
11 * Represents a DOM source range.  That is, for a given DOM tree, gives
12 * the source offset range in the original wikitext for this DOM tree,
13 * as well as the opening and closing tag widths if appropriate.
14 */
15class DomSourceRange extends SourceRange {
16    /**
17     * Opening tag width.
18     * @var ?int
19     */
20    public $openWidth;
21
22    /**
23     * Closing tag width.
24     * @var ?int
25     */
26    public $closeWidth;
27
28    /**
29     * Width of trimmed whitespace between opening tag & first child.
30     * Defaults to zero since for most nodes, there is no ws trimming.
31     * -1 indicates that this information is invalid and should not be used.
32     * @var int
33     */
34    public $leadingWS = 0;
35
36    /**
37     * Width of trimmed whitespace between last child & closing tag.
38     * Defaults to zero since for most nodes, there is no ws trimming.
39     * -1 indicates that this information is invalid and should not be used.
40     * @var int
41     */
42    public $trailingWS = 0;
43
44    /**
45     * Create a new DOM source offset range (DSR).
46     * @param ?int $start The starting index (UTF-8 byte count, inclusive)
47     * @param ?int $end The ending index (UTF-8 byte count, exclusive)
48     * @param ?int $openWidth The width of the open container tag
49     * @param ?int $closeWidth The width of the close container tag
50     * @param int $leadingWS The width of WS chars between opening tag & first child
51     * @param int $trailingWS The width of WS chars between last child & closing tag
52     */
53    public function __construct(
54        ?int $start, ?int $end, ?int $openWidth, ?int $closeWidth,
55        int $leadingWS = 0,
56        int $trailingWS = 0
57    ) {
58        parent::__construct( $start, $end );
59        $this->openWidth = $openWidth;
60        $this->closeWidth = $closeWidth;
61        $this->leadingWS = $leadingWS;
62        $this->trailingWS = $trailingWS;
63    }
64
65    /**
66     * Return the substring of the given string corresponding to the
67     * inner portion of this range (that is, not including the opening
68     * and closing tag widths).
69     * @param string $str The source text string
70     * @return string
71     */
72    public function innerSubstr( string $str ): string {
73        return PHPUtils::safeSubstr( $str, $this->innerStart(), $this->innerLength() );
74    }
75
76    /**
77     * Return the "inner start", that is, the start offset plus the open width.
78     * @return int
79     */
80    public function innerStart(): int {
81        return $this->start + ( $this->openWidth ?? 0 );
82    }
83
84    /**
85     * Return the "inner end", that is, the end offset minus the close width.
86     * @return int
87     */
88    public function innerEnd(): int {
89        return $this->end - ( $this->closeWidth ?? 0 );
90    }
91
92    /**
93     * Return the length of this source range, excluding the open and close
94     * tag widths.
95     * @return int
96     */
97    public function innerLength(): int {
98        return $this->innerEnd() - $this->innerStart();
99    }
100
101    /**
102     * Return the substring of the given string corresponding to the
103     * open portion of this range.
104     * @param string $str The source text string
105     * @return string
106     */
107    public function openSubstr( string $str ): string {
108        return PHPUtils::safeSubstr( $str, $this->start, $this->openWidth );
109    }
110
111    /**
112     * Return the substring of the given string corresponding to the
113     * close portion of this range.
114     * @param string $str The source text string
115     * @return string
116     */
117    public function closeSubstr( string $str ): string {
118        return PHPUtils::safeSubstr( $str, $this->innerEnd(), $this->closeWidth );
119    }
120
121    /**
122     * Strip the tag open and close from the beginning and end of the
123     * provided string.  This is similar to `DomSourceRange::innerSubstr()`
124     * but we assume that the string before `$this->start` and after
125     * `$this->end` has already been removed. (That is, that the input
126     * is `$this->substr( $originalWikitextSource )`.)
127     *
128     * @param string $src The source text string from `$this->start`
129     *   (inclusive) to `$this->end` (exclusive).
130     * @return string
131     */
132    public function stripTags( string $src ): string {
133        Assert::invariant(
134            strlen( $src ) === $this->length(),
135            "Input string not the expected length"
136        );
137        return PHPUtils::safeSubstr(
138            $src,
139            $this->openWidth,
140            -$this->closeWidth
141        );
142    }
143
144    /**
145     * Return a new DOM source range shifted by $amount.
146     * @param int $amount The amount to shift by
147     * @return DomSourceRange
148     */
149    public function offset( int $amount ): DomSourceRange {
150        return new DomSourceRange(
151            $this->start + $amount,
152            $this->end + $amount,
153            $this->openWidth,
154            $this->closeWidth,
155            $this->leadingWS,
156            $this->trailingWS
157        );
158    }
159
160    /**
161     * @return bool True if the tag widths are valid.
162     */
163    public function hasValidTagWidths(): bool {
164        return $this->openWidth !== null && $this->closeWidth !== null &&
165            $this->openWidth >= 0 && $this->closeWidth >= 0;
166    }
167
168    /**
169     * Convert a TSR to a DSR with zero-width container open/close tags.
170     * @param SourceRange $tsr
171     * @return DomSourceRange
172     */
173    public static function fromTsr( SourceRange $tsr ): DomSourceRange {
174        return new DomSourceRange( $tsr->start, $tsr->end, null, null );
175    }
176
177    /**
178     * Create a new DomSourceRange from an array of integers/null (such as
179     * created during JSON serialization).
180     * @param array<int|null> $dsr
181     * @return DomSourceRange
182     */
183    public static function fromArray( array $dsr ): DomSourceRange {
184        $n = count( $dsr );
185        Assert::invariant( $n === 2 || $n === 4 || $n === 6, 'Not enough elements in DSR array' );
186        return new DomSourceRange(
187            $dsr[0], $dsr[1], $dsr[2] ?? null, $dsr[3] ?? null, $dsr[4] ?? 0, $dsr[5] ?? 0
188        );
189    }
190
191    /**
192     * @inheritDoc
193     */
194    public function jsonSerialize(): array {
195        $a = [ $this->start, $this->end, $this->openWidth, $this->closeWidth ];
196        if ( $this->leadingWS !== 0 || $this->trailingWS !== 0 ) {
197            $a[] = $this->leadingWS;
198            $a[] = $this->trailingWS;
199        }
200        return $a;
201    }
202}