a3125f065a2a0d50934f45b77e95bf598bf121f5
[GitHub/WoltLab/WCF.git] /
1 <?php
2
3 /**
4 * @see https://github.com/laminas/laminas-stdlib for the canonical source repository
5 * @copyright https://github.com/laminas/laminas-stdlib/blob/master/COPYRIGHT.md
6 * @license https://github.com/laminas/laminas-stdlib/blob/master/LICENSE.md New BSD License
7 */
8
9 namespace Laminas\Stdlib\StringWrapper;
10
11 use Laminas\Stdlib\Exception;
12 use Laminas\Stdlib\StringUtils;
13
14 abstract class AbstractStringWrapper implements StringWrapperInterface
15 {
16 /**
17 * The character encoding working on
18 * @var string|null
19 */
20 protected $encoding = 'UTF-8';
21
22 /**
23 * An optionally character encoding to convert to
24 * @var string|null
25 */
26 protected $convertEncoding;
27
28 /**
29 * Check if the given character encoding is supported by this wrapper
30 * and the character encoding to convert to is also supported.
31 *
32 * @param string $encoding
33 * @param string|null $convertEncoding
34 * @return bool
35 */
36 public static function isSupported($encoding, $convertEncoding = null)
37 {
38 $supportedEncodings = static::getSupportedEncodings();
39
40 if (! in_array(strtoupper($encoding), $supportedEncodings)) {
41 return false;
42 }
43
44 if ($convertEncoding !== null && ! in_array(strtoupper($convertEncoding), $supportedEncodings)) {
45 return false;
46 }
47
48 return true;
49 }
50
51 /**
52 * Set character encoding working with and convert to
53 *
54 * @param string $encoding The character encoding to work with
55 * @param string|null $convertEncoding The character encoding to convert to
56 * @return StringWrapperInterface
57 */
58 public function setEncoding($encoding, $convertEncoding = null)
59 {
60 $supportedEncodings = static::getSupportedEncodings();
61
62 $encodingUpper = strtoupper($encoding);
63 if (! in_array($encodingUpper, $supportedEncodings)) {
64 throw new Exception\InvalidArgumentException(
65 'Wrapper doesn\'t support character encoding "' . $encoding . '"'
66 );
67 }
68
69 if ($convertEncoding !== null) {
70 $convertEncodingUpper = strtoupper($convertEncoding);
71 if (! in_array($convertEncodingUpper, $supportedEncodings)) {
72 throw new Exception\InvalidArgumentException(
73 'Wrapper doesn\'t support character encoding "' . $convertEncoding . '"'
74 );
75 }
76
77 $this->convertEncoding = $convertEncodingUpper;
78 } else {
79 $this->convertEncoding = null;
80 }
81 $this->encoding = $encodingUpper;
82
83 return $this;
84 }
85
86 /**
87 * Get the defined character encoding to work with
88 *
89 * @return string
90 * @throws Exception\LogicException If no encoding was defined
91 */
92 public function getEncoding()
93 {
94 return $this->encoding;
95 }
96
97 /**
98 * Get the defined character encoding to convert to
99 *
100 * @return string|null
101 */
102 public function getConvertEncoding()
103 {
104 return $this->convertEncoding;
105 }
106
107 /**
108 * Convert a string from defined character encoding to the defined convert encoding
109 *
110 * @param string $str
111 * @param bool $reverse
112 * @return string|false
113 */
114 public function convert($str, $reverse = false)
115 {
116 $encoding = $this->getEncoding();
117 $convertEncoding = $this->getConvertEncoding();
118 if ($convertEncoding === null) {
119 throw new Exception\LogicException(
120 'No convert encoding defined'
121 );
122 }
123
124 if ($encoding === $convertEncoding) {
125 return $str;
126 }
127
128 $from = $reverse ? $convertEncoding : $encoding;
129 $to = $reverse ? $encoding : $convertEncoding;
130 throw new Exception\RuntimeException(sprintf(
131 'Converting from "%s" to "%s" isn\'t supported by this string wrapper',
132 $from,
133 $to
134 ));
135 }
136
137 /**
138 * Wraps a string to a given number of characters
139 *
140 * @param string $string
141 * @param int $width
142 * @param string $break
143 * @param bool $cut
144 * @return string|false
145 */
146 public function wordWrap($string, $width = 75, $break = "\n", $cut = false)
147 {
148 $string = (string) $string;
149 if ($string === '') {
150 return '';
151 }
152
153 $break = (string) $break;
154 if ($break === '') {
155 throw new Exception\InvalidArgumentException('Break string cannot be empty');
156 }
157
158 $width = (int) $width;
159 if ($width === 0 && $cut) {
160 throw new Exception\InvalidArgumentException('Cannot force cut when width is zero');
161 }
162
163 if (StringUtils::isSingleByteEncoding($this->getEncoding())) {
164 return wordwrap($string, $width, $break, $cut);
165 }
166
167 $stringWidth = $this->strlen($string);
168 $breakWidth = $this->strlen($break);
169
170 $result = '';
171 $lastStart = $lastSpace = 0;
172
173 for ($current = 0; $current < $stringWidth; $current++) {
174 $char = $this->substr($string, $current, 1);
175
176 $possibleBreak = $char;
177 if ($breakWidth !== 1) {
178 $possibleBreak = $this->substr($string, $current, $breakWidth);
179 }
180
181 if ($possibleBreak === $break) {
182 $result .= $this->substr($string, $lastStart, $current - $lastStart + $breakWidth);
183 $current += $breakWidth - 1;
184 $lastStart = $lastSpace = $current + 1;
185 continue;
186 }
187
188 if ($char === ' ') {
189 if ($current - $lastStart >= $width) {
190 $result .= $this->substr($string, $lastStart, $current - $lastStart) . $break;
191 $lastStart = $current + 1;
192 }
193
194 $lastSpace = $current;
195 continue;
196 }
197
198 if ($current - $lastStart >= $width && $cut && $lastStart >= $lastSpace) {
199 $result .= $this->substr($string, $lastStart, $current - $lastStart) . $break;
200 $lastStart = $lastSpace = $current;
201 continue;
202 }
203
204 if ($current - $lastStart >= $width && $lastStart < $lastSpace) {
205 $result .= $this->substr($string, $lastStart, $lastSpace - $lastStart) . $break;
206 $lastStart = $lastSpace = $lastSpace + 1;
207 continue;
208 }
209 }
210
211 if ($lastStart !== $current) {
212 $result .= $this->substr($string, $lastStart, $current - $lastStart);
213 }
214
215 return $result;
216 }
217
218 /**
219 * Pad a string to a certain length with another string
220 *
221 * @param string $input
222 * @param int $padLength
223 * @param string $padString
224 * @param int $padType
225 * @return string
226 */
227 public function strPad($input, $padLength, $padString = ' ', $padType = STR_PAD_RIGHT)
228 {
229 if (StringUtils::isSingleByteEncoding($this->getEncoding())) {
230 return str_pad($input, $padLength, $padString, $padType);
231 }
232
233 $lengthOfPadding = $padLength - $this->strlen($input);
234 if ($lengthOfPadding <= 0) {
235 return $input;
236 }
237
238 $padStringLength = $this->strlen($padString);
239 if ($padStringLength === 0) {
240 return $input;
241 }
242
243 $repeatCount = floor($lengthOfPadding / $padStringLength);
244
245 if ($padType === STR_PAD_BOTH) {
246 $repeatCountLeft = $repeatCountRight = ($repeatCount - $repeatCount % 2) / 2;
247
248 $lastStringLength = $lengthOfPadding - 2 * $repeatCountLeft * $padStringLength;
249 $lastStringLeftLength = $lastStringRightLength = floor($lastStringLength / 2);
250 $lastStringRightLength += $lastStringLength % 2;
251
252 $lastStringLeft = $this->substr($padString, 0, $lastStringLeftLength);
253 $lastStringRight = $this->substr($padString, 0, $lastStringRightLength);
254
255 return str_repeat($padString, $repeatCountLeft) . $lastStringLeft
256 . $input
257 . str_repeat($padString, $repeatCountRight) . $lastStringRight;
258 }
259
260 $lastString = $this->substr($padString, 0, $lengthOfPadding % $padStringLength);
261
262 if ($padType === STR_PAD_LEFT) {
263 return str_repeat($padString, $repeatCount) . $lastString . $input;
264 }
265
266 return $input . str_repeat($padString, $repeatCount) . $lastString;
267 }
268 }