Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
SourceRange
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 13
210
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 expandTsrK
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 expandTsrV
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 join
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 substr
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getSourceString
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 offset
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 to
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 length
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 fromSource
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toJsonArray
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 newFromJsonArray
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 hint
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\Core;
5
6use Wikimedia\Assert\Assert;
7use Wikimedia\JsonCodec\Hint;
8use Wikimedia\JsonCodec\JsonCodecable;
9use Wikimedia\JsonCodec\JsonCodecableTrait;
10use Wikimedia\Parsoid\Tokens\KVSourceRange;
11use Wikimedia\Parsoid\Utils\PHPUtils;
12
13/**
14 * Represents a source offset range.
15 */
16class SourceRange implements JsonCodecable {
17    use JsonCodecableTrait;
18
19    /**
20     * Offset of the first character (range start is inclusive).
21     * @var ?int
22     */
23    public $start;
24
25    /**
26     * Offset just past the last character (range end is exclusive).
27     * @var ?int
28     */
29    public $end;
30
31    /**
32     * The "source text" for this range.
33     *
34     * Optional for now because (a) we're retrofitting this into existing
35     * code, and (b) we don't have a way to serialize this yet so in
36     * html2wt contexts this will typically be null (T405759).
37     */
38    public ?Source $source;
39
40    /**
41     * Create a new source offset range.
42     * @param ?int $start The starting index (UTF-8 byte count, inclusive)
43     * @param ?int $end The ending index (UTF-8 byte count, exclusive)
44     * @param ?Source $source
45     */
46    public function __construct( ?int $start, ?int $end, ?Source $source = null ) {
47        $this->start = $start;
48        $this->end = $end;
49        $this->source = $source;
50    }
51
52    /**
53     * Return a KVSourceRange where this SourceRange is the key,
54     * and the value has zero length.
55     * @return KVSourceRange
56     */
57    public function expandTsrK(): KVSourceRange {
58        return new KVSourceRange(
59            $this->start, $this->end, $this->end, $this->end,
60            $this->source, $this->source
61        );
62    }
63
64    /**
65     * Return a KVSourceRange where this SourceRange is the value,
66     * and the key has zero length.
67     * @return KVSourceRange
68     */
69    public function expandTsrV(): KVSourceRange {
70        return new KVSourceRange(
71            $this->start, $this->start, $this->start, $this->end,
72            $this->source, $this->source
73        );
74    }
75
76    /**
77     * Return a KVSourceRange by using this SourceRange for the key
78     * and the given SourceRange parameter for the value.
79     * @param SourceRange $value
80     * @return KVSourceRange
81     */
82    public function join( SourceRange $value ): KVSourceRange {
83        return new KVSourceRange(
84            $this->start, $this->end, $value->start, $value->end,
85            $this->source, $value->source,
86        );
87    }
88
89    /**
90     * Return the substring of the given string corresponding to this
91     * range.
92     * @param string|Source ...$str The source text string (optional)
93     *  The Source of this object (if non-null) is preferred over the given
94     *  argument.
95     * @return string
96     */
97    public function substr( string|Source ...$str ): string {
98        $str = $this->getSourceString( $str );
99        $start = $this->start;
100        $length = $this->length();
101        Assert::invariant( ( $start ?? -1 ) >= 0, "Bad SourceRange start" );
102        // @phan-suppress-next-line PhanCoalescingNeverNull
103        Assert::invariant( ( $length ?? -1 ) >= 0, "Bad SourceRange length" );
104        return PHPUtils::safeSubstr( $str, $start, $length );
105    }
106
107    /**
108     * Helper function to turn an optional string|Source argument into
109     * a source string.
110     * @param array{0:string|Source} $args
111     * @return string
112     */
113    protected function getSourceString( array $args ): string {
114        // If a string is provided, use that -- in the tokenizer for
115        // instance we're operating with a TSR offset so we can't use
116        // the Source (until later, after the TSR is shifted)
117        if ( is_string( $args[0] ?? null ) ) {
118            return $args[0];
119        }
120        // Prefer own our Source, which is presumed to be more accurate
121        // than whatever "get frame source" thing is providing the argument
122        $source = $this->source ?? $args[0] ?? null;
123        Assert::invariant( $source !== null, "Missing TSR/DSR source" );
124        return $source->getSrcText();
125    }
126
127    /**
128     * Return a new source range shifted by $amount.
129     * @param int $amount The amount to shift by
130     * @return SourceRange
131     */
132    public function offset( int $amount ): SourceRange {
133        return new SourceRange( $this->start + $amount, $this->end + $amount, $this->source );
134    }
135
136    /**
137     * Return a range from the end of this range to the start of the given
138     * range.
139     * @param SourceRange $sr
140     * @return SourceRange
141     */
142    public function to( SourceRange $sr ): SourceRange {
143        return new SourceRange( $this->end, $sr->start, $this->source );
144    }
145
146    /**
147     * Return the length of this source range.
148     * @return int
149     */
150    public function length(): int {
151        return $this->end - $this->start;
152    }
153
154    /**
155     * Create a new SourceRange spanning the given Source.
156     */
157    public static function fromSource( Source $source ): static {
158        return new SourceRange( 0, strlen( $source->getSrcText() ), $source );
159    }
160
161    /** @inheritDoc */
162    public function toJsonArray(): array {
163        return [ $this->start, $this->end ];
164    }
165
166    /** @inheritDoc */
167    public static function newFromJsonArray( array $json ): SourceRange {
168        Assert::invariant(
169            count( $json ) === 2,
170            'Wrong # of elements in SourceRange array'
171        );
172        return new SourceRange( $json[0], $json[1] );
173    }
174
175    /** JsonCodec serialization hint. */
176    public static function hint(): Hint {
177        return Hint::build( self::class, Hint::USE_SQUARE );
178    }
179}
180// @deprecated since 0.23; moved from Tokens to Core
181class_alias( SourceRange::class, 'Wikimedia\\Parsoid\\Tokens\\SourceRange' );