Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
97.44% |
38 / 39 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
WrappedString | |
97.44% |
38 / 39 |
|
80.00% |
4 / 5 |
19 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
extend | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
compact | |
95.24% |
20 / 21 |
|
0.00% |
0 / 1 |
10 | |||
join | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Copyright 2015 Timo Tijhof |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining |
6 | * a copy of this software and associated documentation files (the |
7 | * "Software"), to deal in the Software without restriction, including |
8 | * without limitation the rights to use, copy, modify, merge, publish, |
9 | * distribute, sublicense, and/or sell copies of the Software, and to |
10 | * permit persons to whom the Software is furnished to do so, subject to |
11 | * the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be |
14 | * included in all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
21 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
22 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | * @file |
25 | */ |
26 | |
27 | namespace Wikimedia; |
28 | |
29 | use DomainException; |
30 | |
31 | class WrappedString { |
32 | /** @var string */ |
33 | protected $value; |
34 | |
35 | /** @var string|null */ |
36 | protected $prefix; |
37 | |
38 | /** @var string|null */ |
39 | protected $suffix; |
40 | |
41 | /** |
42 | * @param string $value |
43 | * @param string|null $prefix |
44 | * @param string|null $suffix |
45 | */ |
46 | public function __construct( $value, $prefix = null, $suffix = null ) { |
47 | $prefixLen = strlen( $prefix ?? '' ); |
48 | if ( $prefixLen && substr( $value, 0, $prefixLen ) !== $prefix ) { |
49 | throw new DomainException( 'Prefix must match value' ); |
50 | } |
51 | $suffixLen = strlen( $suffix ?? '' ); |
52 | if ( $suffixLen && substr( $value, -$suffixLen ) !== $suffix ) { |
53 | throw new DomainException( 'Suffix must match value' ); |
54 | } |
55 | |
56 | $this->value = $value; |
57 | $this->prefix = $prefix; |
58 | $this->suffix = $suffix; |
59 | } |
60 | |
61 | /** |
62 | * @param string $value Value of a WrappedString with the same prefix and suffix |
63 | * @return WrappedString Newly wrapped string |
64 | */ |
65 | protected function extend( $value ) { |
66 | $wrap = clone $this; |
67 | $suffixLen = strlen( $this->suffix ); |
68 | // Remove the suffix (temporarily), to open the string for merging. |
69 | if ( $suffixLen ) { |
70 | $wrap->value = substr( $this->value, 0, -$suffixLen ); |
71 | } |
72 | // Append the next value without a prefix, thus ending with the suffix again. |
73 | $prefixLen = strlen( $this->prefix ); |
74 | $wrap->value .= substr( $value, $prefixLen ); |
75 | return $wrap; |
76 | } |
77 | |
78 | /** |
79 | * Merge consecutive WrappedString objects with the same prefix and suffix. |
80 | * |
81 | * Does not modify the array or the WrappedString objects. |
82 | * |
83 | * NOTE: This is an internal method. Use join() or WrappedStringList instead. |
84 | * |
85 | * @param array<string|WrappedString|WrappedStringList> $wraps |
86 | * @return array<string|WrappedString|WrappedStringList> Compacted list to be treated as strings |
87 | */ |
88 | public static function compact( array $wraps ) { |
89 | $consolidated = []; |
90 | if ( $wraps === [] ) { |
91 | // Return early so that we don't have to deal with $prev being |
92 | // set or not set, and avoid the risk of adding $prev's initial null |
93 | // value to the list as extra value (T196496). |
94 | return $consolidated; |
95 | } |
96 | $first = true; |
97 | $prev = null; |
98 | foreach ( $wraps as $wrap ) { |
99 | if ( $first ) { |
100 | $first = false; |
101 | $prev = $wrap; |
102 | continue; |
103 | } |
104 | if ( $prev instanceof WrappedString |
105 | && $wrap instanceof WrappedString |
106 | && $prev->prefix !== null |
107 | && $prev->prefix === $wrap->prefix |
108 | && $prev->suffix !== null |
109 | && $prev->suffix === $wrap->suffix |
110 | ) { |
111 | $prev = $prev->extend( $wrap->value ); |
112 | } else { |
113 | $consolidated[] = $prev; |
114 | $prev = $wrap; |
115 | } |
116 | } |
117 | // Add last one |
118 | $consolidated[] = $prev; |
119 | |
120 | return $consolidated; |
121 | } |
122 | |
123 | /** |
124 | * Join several wrapped strings with a separator between each. |
125 | * |
126 | * This method is compatible with native PHP implode(). The actual join |
127 | * operation is deferred to WrappedStringList::__toString(). This allows |
128 | * callers to collect multiple lists and compact them together. |
129 | * |
130 | * @param string $sep |
131 | * @param (string|WrappedString|WrappedStringList)[] $wraps |
132 | * @return WrappedStringList |
133 | */ |
134 | public static function join( $sep, array $wraps ) { |
135 | return new WrappedStringList( $sep, $wraps ); |
136 | } |
137 | |
138 | /** @return string */ |
139 | public function __toString() { |
140 | return $this->value; |
141 | } |
142 | } |