string = (string) $string;
$this->encoding = $encoding ?? mb_internal_encoding();
}
/**
* Static initializing a String object.
*
* @param string|Stringable $string
* @param string|null $encoding
*
* @return static
*
* @see Text::__construct()
* @psalm-suppress UnsafeInstantiation
*/
public static function create(string|Stringable $string, ?string $encoding = null): static {
return new static($string, $encoding);
}
/**
* Returns the used encoding
*
* @return string
*/
public function getEncoding(): string {
return $this->encoding;
}
/**
* Get string length
*
*
* $str = new Text('Hello World!');
* $str->length(); // 12
*
* $str = new Text('いちりんしゃ');
* $str->length(); // 6
*
*
* @return int Returns the length
*/
public function length(): int {
return mb_strlen($this->string, $this->encoding);
}
/**
* Appends $string
and returns as a new Text
*
* @param string|Stringable $string
*
* @return Text
*/
public function append(string|Stringable $string): self {
return new self($this->string . $string, $this->encoding);
}
/**
* Prepends $string
and returns as a new Text
*
* @param string|Stringable $string $string
*
* @return Text
*/
public function prepend(string|Stringable $string): self {
return new self($string . $this->string, $this->encoding);
}
/**
* Inserts a substring at the given index
*
*
* $str = new Text('Hello World!');
* $str->insert('to this ', 5); // Hello to this World!
*
*
* @param string|Stringable $substring
* @param int $index
*
* @return Text
*/
public function insert(string|Stringable $substring, int $index): self {
if ($index <= 0) {
return $this->prepend($substring);
}
if ($index > $this->length()) {
return $this->append($substring);
}
$start = mb_substr($this->string, 0, $index, $this->encoding);
$end = mb_substr($this->string, $index, $this->length(), $this->encoding);
return new self($start . $substring . $end);
}
//
//
// SLICING AND SUBSTRING
//
//
/**
* Slices a piece of the string from a given offset with a specified length.
* If no length is given, the String is sliced to its maximum length.
*
* @see #substring
*
* @param int $offset
* @param int|null $length
*
* @return Text
*/
public function slice(int $offset, ?int $length = null): self {
$offset = $this->prepareOffset($offset);
$length = $this->prepareLength($offset, $length);
return new self(mb_substr($this->string, $offset, $length, $this->encoding), $this->encoding);
}
/**
* Slices a piece of the string from a given start to an end.
* If no length is given, the String is sliced to its maximum length.
*
* @see #slice
*
* @param int $start
* @param int|null $end
*
* @return Text
*/
public function substring(int $start, ?int $end = null): self {
$length = $this->length();
if (null === $end) {
$end = $length;
}
if ($end < 0) {
$end = $length + $end;
}
$end = min($end, $length);
$start = min($start, $end);
$end = max($start, $end);
$end = $end - $start;
return new self(mb_substr($this->string, $start, $end, $this->encoding), $this->encoding);
}
/**
* Count the number of substring occurrences.
*
* @param string|Stringable $substring The substring to count the occurrencies
* @param bool $caseSensitive Force case-sensitivity
*
* @return int
*/
public function countSubstring(string|Stringable $substring, bool $caseSensitive = true): int {
if (empty($substring)) {
throw new \InvalidArgumentException('$substring cannot be empty');
}
if ($caseSensitive) {
return mb_substr_count($this->string, (string) $substring, $this->encoding);
}
$str = mb_strtoupper($this->string, $this->encoding);
$substring = mb_strtoupper((string) $substring, $this->encoding);
return mb_substr_count($str, $substring, $this->encoding);
}
//
//
// REPLACING
//
//
/**
* Replace all occurrences of the search string with the replacement string
*
* @see #supplant
*
* @param Arrayable|Stringable|array|string $search
* The value being searched for, otherwise known as the needle. An array may be used
* to designate multiple needles.
* @param Arrayable|Stringable[]|array|string $replace
* The replacement value that replaces found search values. An array may be used to
* designate multiple replacements.
*
* @return Text
*
* @psalm-suppress MixedArgumentTypeCoercion
*/
public function replace(Arrayable|Stringable|array|string $search, Arrayable|Stringable|array|string $replace): self {
$search = $search instanceof Stringable ? (string) $search :
($search instanceof Arrayable ? $search->toArray() : $search);
$replace = $replace instanceof Stringable ? (string) $replace :
($replace instanceof Arrayable ? $replace->toArray() : $replace);
return new self(str_replace($search, $replace, $this->string), $this->encoding);
}
/**
* Replaces all occurrences of given replacement map. Keys will be replaced with its values.
*
* @param string[] $map the replacements. Keys will be replaced with its value.
*
* @return Text
*/
public function supplant(array $map): self {
return new self(str_replace(array_keys($map), array_values($map), $this->string), $this->encoding);
}
/**
* Replace text within a portion of a string.
*
* @param string|Stringable $replacement
* @param int $offset
* @param int|null $length
*
* @return Text
*/
public function splice(string|Stringable $replacement, int $offset, ?int $length = null): self {
$offset = $this->prepareOffset($offset);
$length = $this->prepareLength($offset, $length);
$start = $this->substring(0, $offset);
$end = $this->substring($offset + $length);
return new self($start . $replacement . $end);
}
//
//
// STRING OPERATIONS
//
//
/**
* Strip whitespace (or other characters) from the beginning and end of the string
*
* @param string|Stringable $characters
* Optionally, the stripped characters can also be specified using the mask parameter.
* Simply list all characters that you want to be stripped. With .. you can specify a
* range of characters.
*
* @return Text
*/
public function trim(string|Stringable $characters = " \t\n\r\v\0"): self {
return new self(trim($this->string, (string) $characters), $this->encoding);
}
/**
* Strip whitespace (or other characters) from the beginning of the string
*
* @param string|Stringable $characters
* Optionally, the stripped characters can also be specified using the mask parameter.
* Simply list all characters that you want to be stripped. With .. you can specify a
* range of characters.
*
* @return Text
*/
public function trimStart(string|Stringable $characters = " \t\n\r\v\0"): self {
return new self(ltrim($this->string, (string) $characters), $this->encoding);
}
/**
* Strip whitespace (or other characters) from the end of the string
*
* @param string|Stringable $characters
* Optionally, the stripped characters can also be specified using the mask parameter.
* Simply list all characters that you want to be stripped. With .. you can specify a
* range of characters.
*
* @return Text
*/
public function trimEnd(string|Stringable $characters = " \t\n\r\v\0"): self {
return new self(rtrim($this->string, (string) $characters), $this->encoding);
}
/**
* Adds padding to the start and end
*
* @param int $length
* @param string|Stringable $padding
*
* @return Text
*/
public function pad(int $length, string|Stringable $padding = ' '): self {
$len = $length - $this->length();
return $this->applyPadding(floor($len / 2), ceil($len / 2), $padding);
}
/**
* Adds padding to the start
*
* @param int $length
* @param string|Stringable $padding
*
* @return Text
*/
public function padStart(int $length, string|Stringable $padding = ' ') {
return $this->applyPadding($length - $this->length(), 0, $padding);
}
/**
* Adds padding to the end
*
* @param int $length
* @param string|Stringable $padding
*
* @return Text
*/
public function padEnd(int $length, string|Stringable $padding = ' '): self {
return $this->applyPadding(0, $length - $this->length(), $padding);
}
/**
* Adds the specified amount of left and right padding to the given string.
* The default character used is a space.
*
* @see https://github.com/danielstjules/Stringy/blob/master/src/Stringy.php
*
* @param int|float $left Length of left padding
* @param int|float $right Length of right padding
* @param string|Stringable $padStr String used to pad
*
* @return Text the padded string
*/
protected function applyPadding(int|float $left = 0, int|float $right = 0, string|Stringable $padStr = ' '): self {
$length = mb_strlen((string) $padStr, $this->encoding);
$strLength = $this->length();
$paddedLength = $strLength + $left + $right;
if (!$length || $paddedLength <= $strLength) {
return $this;
}
$leftPadding = mb_substr(str_repeat((string) $padStr, (int) ceil($left / $length)), 0, (int) $left, $this->encoding);
$rightPadding = mb_substr(str_repeat((string) $padStr, (int) ceil($right / $length)), 0, (int) $right, $this->encoding);
return new self($leftPadding . $this->string . $rightPadding);
}
/**
* Ensures a given substring at the start of the string
*
* @param string $substring
*
* @return Text
*/
public function ensureStart(string $substring): self {
if (!$this->startsWith($substring)) {
return $this->prepend($substring);
}
return $this;
}
/**
* Ensures a given substring at the end of the string
*
* @param string $substring
*
* @return Text
*/
public function ensureEnd(string $substring): self {
if (!$this->endsWith($substring)) {
return $this->append($substring);
}
return $this;
}
/**
* Returns a copy of the string wrapped at a given number of characters
*
* @param int $width The number of characters at which the string will be wrapped.
* @param string $break The line is broken using the optional break parameter.
* @param bool $cut
* If the cut is set to TRUE, the string is always wrapped at or before the specified
* width. So if you have a word that is larger than the given width, it is broken apart.
*
* @return Text Returns the string wrapped at the specified length.
*/
public function wrapWords(int $width = 75, string $break = "\n", bool $cut = false): self {
return new self(wordwrap($this->string, $width, $break, $cut), $this->encoding);
}
/**
* Repeat the string $times times. If $times is 0, it returns ''.
*
* @param int $multiplier
*
* @throws \InvalidArgumentException If $times is negative.
*
* @return Text
*/
public function repeat(int $multiplier): self {
return new self(str_repeat($this->string, $multiplier), $this->encoding);
}
/**
* Reverses the character order
*
* @return Text
*/
public function reverse(): self {
return new self(strrev($this->string), $this->encoding);
}
/**
* Truncates the string with a substring and ensures it doesn't exceed the given length
*
* @param int $length
* @param string $substring
*
* @return Text
*/
public function truncate(int $length, string $substring = ''): self {
if ($this->length() <= $length) {
return new self($this->string, $this->encoding);
}
$substrLen = mb_strlen($substring, $this->encoding);
if ($this->length() + $substrLen > $length) {
$length -= $substrLen;
}
return $this->substring(0, $length)->append($substring);
}
/**
* Returns the native string
*
* @return string
*/
public function toString(): string {
return $this->string;
}
protected function getString(): string {
return $this->toString();
}
//
//
// MAGIC HAPPENS HERE
//
//
public function __toString(): string {
return $this->string;
}
}