Completed
Push — master ( dde30f...3b3524 )
by Cristiano
02:10
created

Text::getString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the Phootwork package.
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 *
7
 * @license MIT License
8
 * @copyright Thomas Gossmann
9
 */
10
namespace phootwork\lang;
11
12
use phootwork\lang\parts\ArrayConversionsPart;
13
use phootwork\lang\parts\CheckerPart;
14
use phootwork\lang\parts\ComparisonPart;
15
use phootwork\lang\parts\InternalPart;
16
use phootwork\lang\parts\SearchPart;
17
use phootwork\lang\parts\TransformationsPart;
18
19
/**
20
 * Object representation of an immutable String
21
 *
22
 * @author gossi
23
 */
24
class Text implements Comparable {
25
	use ArrayConversionsPart;
26
	use CheckerPart;
27
	use ComparisonPart;
28
	use SearchPart;
29
	use InternalPart;
30
	use TransformationsPart;
31
32
	/** @var string */
33
	private $string;
34
35
	/** @var string */
36
	private $encoding;
37
38
	/**
39
	 * Initializes a String object ad assigns both string and encoding properties
40
	 * the supplied values. $string is cast to a string prior to assignment, and if
41
	 * $encoding is not specified, it defaults to mb_internal_encoding(). Throws
42
	 * an InvalidArgumentException if the first argument is an array or object
43
	 * without a __toString method.
44
	 *
45
	 * @param mixed $string Value to modify, after being cast to string
46
	 * @param string $encoding The character encoding
47
	 *
48
	 * @throws \InvalidArgumentException if an array or object without a __toString method is passed as the first argument
49
	 *
50
	 * @psalm-suppress PossiblyInvalidPropertyAssignmentValue mb_internal_encoding always return string when called as getter
51
	 */
52 99
	public function __construct($string = '', ?string $encoding = null) {
53 99
		if (is_array($string)) {
54 1
			throw new \InvalidArgumentException('The constructor parameter cannot be an array');
55 98
		} elseif (is_object($string) && !method_exists($string, '__toString')) {
56 1
			throw new \InvalidArgumentException('Passed object must implement  `__toString` method');
57
		}
58
59 97
		$this->string = (string) $string;
60 97
		$this->encoding = $encoding ?? mb_internal_encoding();
0 ignored issues
show
Documentation Bug introduced by
It seems like $encoding ?? mb_internal_encoding() can also be of type boolean. However, the property $encoding is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
61 97
	}
62
63 2
	public function __clone() {
64 2
		return new self($this->string, $this->encoding);
65
	}
66
67
	/**
68
	 * Static initializing a String object.
69
	 *
70
	 * @see Text::__construct()
71
	 *
72
	 * @param mixed $string
73
	 * @param string $encoding
74
	 *
75
	 * @return Text
76
	 */
77 6
	public static function create($string, ?string $encoding = null) {
78 6
		return new self($string, $encoding);
79
	}
80
81
	/**
82
	 * Returns the used encoding
83
	 *
84
	 * @return string
85
	 */
86 1
	public function getEncoding(): string {
87 1
		return $this->encoding;
88
	}
89
90
	/**
91
	 * Get string length
92
	 *
93
	 * <code>
94
	 * $str = new Text('Hello World!');<br>
95
	 * $str->length(); // 12
96
	 *
97
	 * $str = new Text('いちりんしゃ');<br>
98
	 * $str->length(); // 6
99
	 * </code>
100
	 *
101
	 * @return int Returns the length
102
	 */
103 41
	public function length(): int {
104 41
		return mb_strlen($this->string, $this->encoding);
105
	}
106
107
	/**
108
	 * Appends <code>$string</code> and returns as a new <code>Text</code>
109
	 *
110
	 * @param string|Text $string
111
	 *
112
	 * @return Text
113
	 */
114 8
	public function append($string): self {
115 8
		return new self($this->string . $string, $this->encoding);
116
	}
117
118
	/**
119
	 * Prepends <code>$string</code> and returns as a new <code>Text</code>
120
	 *
121
	 * @param string|Text $string
122
	 *
123
	 * @return Text
124
	 */
125 2
	public function prepend($string): self {
126 2
		return new self($string . $this->string, $this->encoding);
127
	}
128
129
	/**
130
	 * Inserts a substring at the given index
131
	 *
132
	 * <code>
133
	 * $str = new Text('Hello World!');<br>
134
	 * $str->insert('to this ', 5); // Hello to this World!
135
	 * </code>
136
	 *
137
	 * @param string|Text $substring
138
	 * @param int $index
139
	 *
140
	 * @return Text
141
	 */
142 1
	public function insert($substring, int $index): self {
143 1
		if ($index <= 0) {
144 1
			return $this->prepend($substring);
145
		}
146
147 1
		if ($index > $this->length()) {
148 1
			return $this->append($substring);
149
		}
150
151 1
		$start = mb_substr($this->string, 0, $index, $this->encoding);
152 1
		$end = mb_substr($this->string, $index, $this->length(), $this->encoding);
153
154 1
		return new self($start . $substring . $end);
155
	}
156
157
	//
158
	//
159
	// SLICING AND SUBSTRING
160
	//
161
	//
162
163
	/**
164
	 * Slices a piece of the string from a given offset with a specified length.
165
	 * If no length is given, the String is sliced to its maximum length.
166
	 *
167
	 * @see #substring
168
	 *
169
	 * @param int $offset
170
	 * @param int $length
171
	 *
172
	 * @return Text
173
	 */
174 11
	public function slice(int $offset, ?int $length = null): self {
175 11
		$offset = $this->prepareOffset($offset);
176 11
		$length = $this->prepareLength($offset, $length);
177
178 11
		if ($length === 0) {
179 1
			return new self('', $this->encoding);
180
		}
181
182 11
		return new self(mb_substr($this->string, $offset, $length, $this->encoding), $this->encoding);
183
	}
184
185
	/**
186
	 * Slices a piece of the string from a given start to an end.
187
	 * If no length is given, the String is sliced to its maximum length.
188
	 *
189
	 * @see #slice
190
	 *
191
	 * @param int $start
192
	 * @param int $end
193
	 *
194
	 * @return Text
195
	 */
196 20
	public function substring(int $start, ?int $end = null): self {
197 20
		$length = $this->length();
198
199 20
		if (null === $end) {
200 18
			$end = $length;
201
		}
202
203 20
		if ($end < 0) {
204 2
			$end = $length + $end;
205
		}
206
207 20
		$end = min($end, $length);
208 20
		$start = min($start, $end);
209 20
		$end = max($start, $end);
210 20
		$end = $end - $start;
211
212 20
		return new self(mb_substr($this->string, $start, $end, $this->encoding), $this->encoding);
213
	}
214
215
	/**
216
	 * Count the number of substring occurrences.
217
	 *
218
	 * @param string|Text $substring The substring to count the occurrencies
219
	 * @param bool $caseSensitive Force case-sensitivity
220
	 *
221
	 * @return int
222
	 */
223 3
	public function countSubstring($substring, bool $caseSensitive = true): int {
224 3
		$this->verifyNotEmpty($substring, '$substring');
225 2
		if ($caseSensitive) {
226 2
			return mb_substr_count($this->string, (string) $substring, $this->encoding);
227
		}
228 1
		$str = mb_strtoupper($this->string, $this->encoding);
229 1
		$substring = mb_strtoupper((string) $substring, $this->encoding);
230
231 1
		return mb_substr_count($str, (string) $substring, $this->encoding);
232
	}
233
234
	//
235
	//
236
	// REPLACING
237
	//
238
	//
239
240
	/**
241
	 * Replace all occurrences of the search string with the replacement string
242
	 *
243
	 * @see #supplant
244
	 *
245
	 * @param Arrayable|Text|array|string $search
246
	 * 		The value being searched for, otherwise known as the needle. An array may be used
247
	 * 		to designate multiple needles.
248
	 * @param Arrayable|Text|array|string $replace
249
	 * 		The replacement value that replaces found search values. An array may be used to
250
	 * 		designate multiple replacements.
251
	 *
252
	 * @return Text
253
	 */
254 10
	public function replace($search, $replace): self {
255 10
		if ($search instanceof self) {
256 3
			$search = $search->toString();
257 8
		} elseif ($search instanceof Arrayable) {
258 1
			$search = $search->toArray();
259
		}
260
261 10
		if ($replace instanceof self) {
262 1
			$replace = $replace->toString();
263 10
		} elseif ($replace instanceof Arrayable) {
264 1
			$replace = $replace->toArray();
265
		}
266
267 10
		return new self(str_replace($search, $replace, $this->string), $this->encoding);
268
	}
269
270
	/**
271
	 * Replaces all occurences of given replacement map. Keys will be replaced with its values.
272
	 *
273
	 * @param array $map the replacements. Keys will be replaced with its value.
274
	 *
275
	 * @return Text
276
	 */
277 1
	public function supplant(array $map): self {
278 1
		return new self(str_replace(array_keys($map), array_values($map), $this->string), $this->encoding);
279
	}
280
281
	/**
282
	 * Replace text within a portion of a string.
283
	 *
284
	 * @param string|Text $replacement
285
	 * @param int $offset
286
	 * @param int|null $length
287
	 *
288
	 * @throws \InvalidArgumentException If $offset is greater then the string length or $length is too small.
289
	 *
290
	 * @return Text
291
	 */
292 6
	public function splice($replacement, int $offset, ?int $length = null): self {
293 6
		$offset = $this->prepareOffset($offset);
294 3
		$length = $this->prepareLength($offset, $length);
295
296 1
		$start = $this->substring(0, $offset);
297 1
		$end = $this->substring($offset + $length);
298
299 1
		return new self($start . $replacement . $end);
300
	}
301
302
	//
303
	//
304
	// STRING OPERATIONS
305
	//
306
	//
307
308
	/**
309
	 * Strip whitespace (or other characters) from the beginning and end of the string
310
	 *
311
	 * @param string $characters
312
	 * 		Optionally, the stripped characters can also be specified using the mask parameter.
313
	 * 		Simply list all characters that you want to be stripped. With .. you can specify a
314
	 * 		range of characters.
315
	 *
316
	 * @return Text
317
	 */
318 3
	public function trim(string $characters = " \t\n\r\v\0"): self {
319 3
		return new self(trim($this->string, (string) $characters), $this->encoding);
320
	}
321
322
	/**
323
	 * Strip whitespace (or other characters) from the beginning of the string
324
	 *
325
	 * @param string $characters
326
	 * 		Optionally, the stripped characters can also be specified using the mask parameter.
327
	 * 		Simply list all characters that you want to be stripped. With .. you can specify a
328
	 * 		range of characters.
329
	 *
330
	 * @return Text
331
	 */
332 1
	public function trimStart(string $characters = " \t\n\r\v\0"): self {
333 1
		return new self(ltrim($this->string, (string) $characters), $this->encoding);
334
	}
335
336
	/**
337
	 * Strip whitespace (or other characters) from the end of the string
338
	 *
339
	 * @param string $characters
340
	 * 		Optionally, the stripped characters can also be specified using the mask parameter.
341
	 * 		Simply list all characters that you want to be stripped. With .. you can specify a
342
	 * 		range of characters.
343
	 *
344
	 * @return Text
345
	 */
346 1
	public function trimEnd(string $characters = " \t\n\r\v\0"): self {
347 1
		return new self(rtrim($this->string, (string) $characters), $this->encoding);
348
	}
349
350
	/**
351
	 * Adds padding to the start and end
352
	 *
353
	 * @param int $length
354
	 * @param string $padding
355
	 *
356
	 * @return Text
357
	 */
358 1
	public function pad(int $length, string $padding = ' '): self {
359 1
		$len = $length - $this->length();
360
361 1
		return $this->applyPadding(floor($len / 2), ceil($len / 2), $padding);
362
	}
363
364
	/**
365
	 * Adds padding to the start
366
	 *
367
	 * @param int $length
368
	 * @param string|Text $padding
369
	 *
370
	 * @return Text
371
	 */
372 1
	public function padStart(int $length, $padding = ' ') {
373 1
		return $this->applyPadding($length - $this->length(), 0, $padding);
374
	}
375
376
	/**
377
	 * Adds padding to the end
378
	 *
379
	 * @param int $length
380
	 * @param string|Text $padding
381
	 *
382
	 * @return Text
383
	 */
384 1
	public function padEnd(int $length, $padding = ' '): self {
385 1
		return $this->applyPadding(0, $length - $this->length(), $padding);
386
	}
387
388
	/**
389
	 * Adds the specified amount of left and right padding to the given string.
390
	 * The default character used is a space.
391
	 *
392
	 * @see https://github.com/danielstjules/Stringy/blob/master/src/Stringy.php
393
	 *
394
	 * @param int|float $left Length of left padding
395
	 * @param int|float $right Length of right padding
396
	 * @param string|Text $padStr String used to pad
397
	 *
398
	 * @return Text the padded string
399
	 */
400 1
	protected function applyPadding($left = 0, $right = 0, $padStr = ' ') {
401 1
		$length = mb_strlen((string) $padStr, $this->encoding);
402 1
		$strLength = $this->length();
403 1
		$paddedLength = $strLength + $left + $right;
404 1
		if (!$length || $paddedLength <= $strLength) {
405 1
			return $this;
406
		}
407
408 1
		$leftPadding = mb_substr(str_repeat((string) $padStr, (int) ceil($left / $length)), 0, (int) $left, $this->encoding);
409 1
		$rightPadding = mb_substr(str_repeat((string) $padStr, (int) ceil($right / $length)), 0, (int) $right, $this->encoding);
410
411 1
		return new self($leftPadding . $this->string . $rightPadding);
412
	}
413
414
	/**
415
	 * Ensures a given substring at the start of the string
416
	 *
417
	 * @param string $substring
418
	 *
419
	 * @return Text
420
	 */
421 1
	public function ensureStart(string $substring): self {
422 1
		if (!$this->startsWith($substring)) {
423 1
			return $this->prepend($substring);
424
		}
425
426 1
		return $this;
427
	}
428
429
	/**
430
	 * Ensures a given substring at the end of the string
431
	 *
432
	 * @param string $substring
433
	 *
434
	 * @return Text
435
	 */
436 1
	public function ensureEnd(string $substring): self {
437 1
		if (!$this->endsWith($substring)) {
438 1
			return $this->append($substring);
439
		}
440
441 1
		return $this;
442
	}
443
444
	/**
445
	 * Returns a copy of the string wrapped at a given number of characters
446
	 *
447
	 * @param int $width The number of characters at which the string will be wrapped.
448
	 * @param string $break The line is broken using the optional break parameter.
449
	 * @param bool $cut
450
	 * 		If the cut is set to TRUE, the string is always wrapped at or before the specified
451
	 * 		width. So if you have a word that is larger than the given width, it is broken apart.
452
	 *
453
	 * @return Text Returns the string wrapped at the specified length.
454
	 */
455 3
	public function wrapWords(int $width = 75, string $break = "\n", bool $cut = false): self {
456 3
		return new self(wordwrap($this->string, $width, $break, $cut), $this->encoding);
457
	}
458
459
	/**
460
	 * Repeat the string $times times. If $times is 0, it returns ''.
461
	 *
462
	 * @param int $multiplier
463
	 *
464
	 * @throws \InvalidArgumentException If $times is negative.
465
	 *
466
	 * @return Text
467
	 */
468 2
	public function repeat(int $multiplier): self {
469 2
		$this->verifyNotNegative($multiplier, 'Number of repetitions');
470
471 1
		return new self(str_repeat($this->string, $multiplier), $this->encoding);
472
	}
473
474
	/**
475
	 * Reverses the character order
476
	 *
477
	 * @return Text
478
	 */
479 1
	public function reverse(): self {
480 1
		return new self(strrev($this->string), $this->encoding);
481
	}
482
483
	/**
484
	 * Truncates the string with a substring and ensures it doesn't exceed the given length
485
	 *
486
	 * @param int $length
487
	 * @param string $substring
488
	 *
489
	 * @return Text
490
	 */
491 1
	public function truncate(int $length, string $substring = ''): self {
492 1
		if ($this->length() <= $length) {
493 1
			return new self($this->string, $this->encoding);
494
		}
495
496 1
		$substrLen = mb_strlen($substring, $this->encoding);
497
498 1
		if ($this->length() + $substrLen > $length) {
499 1
			$length -= $substrLen;
500
		}
501
502 1
		return $this->substring(0, $length)->append($substring);
503
	}
504
505
	/**
506
	 * Returns the native string
507
	 *
508
	 * @return string
509
	 */
510 65
	public function toString(): string {
511 65
		return $this->string;
512
	}
513
514 60
	protected function getString(): string {
515 60
		return $this->toString();
516
	}
517
518
	//
519
	//
520
	// MAGIC HAPPENS HERE
521
	//
522
	//
523
524 62
	public function __toString(): string {
525 62
		return $this->string;
526
	}
527
}
528