From 18397477d6734b4336c8b273f069cd8584173435 Mon Sep 17 00:00:00 2001 From: flashwave Date: Thu, 4 Jan 2024 02:07:43 +0000 Subject: [PATCH] Removed AString and IString and turned WString into a utility class like XString for multibyte strings. --- VERSION | 2 +- composer.lock | 108 +++++----- src/AString.php | 278 ------------------------- src/DateTime.php | 11 +- src/ICloneable.php | 18 -- src/IString.php | 198 ------------------ src/StringIterator.php | 83 -------- src/WString.php | 463 +++++++++++------------------------------ src/XString.php | 54 +++-- src/XStringTrait.php | 65 ------ tests/StringTest.php | 287 ------------------------- tests/WStringTest.php | 47 +++++ tests/XArrayTest.php | 14 +- tests/XStringTest.php | 34 +++ 14 files changed, 285 insertions(+), 1377 deletions(-) delete mode 100644 src/AString.php delete mode 100644 src/ICloneable.php delete mode 100644 src/IString.php delete mode 100644 src/StringIterator.php delete mode 100644 src/XStringTrait.php delete mode 100644 tests/StringTest.php create mode 100644 tests/WStringTest.php create mode 100644 tests/XStringTest.php diff --git a/VERSION b/VERSION index fcc7236..0823a1f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2311.201900 +0.2401.40206 diff --git a/composer.lock b/composer.lock index 81563dc..5bb0506 100644 --- a/composer.lock +++ b/composer.lock @@ -68,16 +68,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.17.1", + "version": "v4.18.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", "shasum": "" }, "require": { @@ -118,9 +118,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" }, - "time": "2023-08-13T19:53:39+00:00" + "time": "2023-12-10T21:03:43+00:00" }, { "name": "phar-io/manifest", @@ -235,16 +235,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.41", + "version": "1.10.50", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "c6174523c2a69231df55bdc65b61655e72876d76" + "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6174523c2a69231df55bdc65b61655e72876d76", - "reference": "c6174523c2a69231df55bdc65b61655e72876d76", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/06a98513ac72c03e8366b5a0cb00750b487032e4", + "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4", "shasum": "" }, "require": { @@ -293,27 +293,27 @@ "type": "tidelift" } ], - "time": "2023-11-05T12:57:57+00:00" + "time": "2023-12-13T10:59:42+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.7", + "version": "10.1.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "355324ca4980b8916c18b9db29f3ef484078f26e" + "reference": "78c3b7625965c2513ee96569a4dbb62601784145" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e", - "reference": "355324ca4980b8916c18b9db29f3ef484078f26e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145", + "reference": "78c3b7625965c2513ee96569a4dbb62601784145", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1", "phpunit/php-file-iterator": "^4.0", "phpunit/php-text-template": "^3.0", @@ -363,7 +363,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11" }, "funding": [ { @@ -371,7 +371,7 @@ "type": "github" } ], - "time": "2023-10-04T15:34:17+00:00" + "time": "2023-12-21T15:38:30+00:00" }, { "name": "phpunit/php-file-iterator", @@ -618,16 +618,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.4.2", + "version": "10.5.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1" + "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", - "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ed21115d505b4b4f7dc7b5651464e19a2c7f7856", + "reference": "ed21115d505b4b4f7dc7b5651464e19a2c7f7856", "shasum": "" }, "require": { @@ -667,7 +667,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.4-dev" + "dev-main": "10.5-dev" } }, "autoload": { @@ -699,7 +699,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.2" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.5" }, "funding": [ { @@ -715,7 +715,7 @@ "type": "tidelift" } ], - "time": "2023-10-26T07:21:45+00:00" + "time": "2023-12-27T15:13:52+00:00" }, { "name": "sebastian/cli-parser", @@ -963,20 +963,20 @@ }, { "name": "sebastian/complexity", - "version": "3.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -985,7 +985,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -1009,7 +1009,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -1017,20 +1017,20 @@ "type": "github" } ], - "time": "2023-09-28T11:50:59+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/diff", - "version": "5.0.3", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f", + "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f", "shasum": "" }, "require": { @@ -1043,7 +1043,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -1076,7 +1076,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.0" }, "funding": [ { @@ -1084,7 +1084,7 @@ "type": "github" } ], - "time": "2023-05-01T07:48:21+00:00" + "time": "2023-12-22T10:55:06+00:00" }, { "name": "sebastian/environment", @@ -1292,20 +1292,20 @@ }, { "name": "sebastian/lines-of-code", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { - "nikic/php-parser": "^4.10", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=8.1" }, "require-dev": { @@ -1338,7 +1338,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -1346,7 +1346,7 @@ "type": "github" } ], - "time": "2023-08-31T09:25:50+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", @@ -1634,16 +1634,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { @@ -1672,7 +1672,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { @@ -1680,7 +1680,7 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2023-11-20T00:12:19+00:00" } ], "aliases": [], @@ -1693,5 +1693,5 @@ "ext-mbstring": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/AString.php b/src/AString.php deleted file mode 100644 index e9f5a75..0000000 --- a/src/AString.php +++ /dev/null @@ -1,278 +0,0 @@ -value = $value; - } - - public function getLength(): int { - return strlen($this->value); - } - - public function isEmpty(): bool { - return $this->value === ''; - } - - public function __toString(): string { - return $this->value; - } - - /** - * Checks if an offset exists in the string. - * - * You should call isset($string[$offset]) instead of $string->offsetExists($offset). - * - * @see https://www.php.net/manual/en/arrayaccess.offsetexists.php - * @param int $offset Character offset. - * @return bool true if it exists, false if not. - */ - public function offsetExists(mixed $offset): bool { - return isset($this->value[$offset]); - } - - /** - * Gets an offset from the string. - * - * You should do $string[$offset] instead of $string->offsetGet($offset). - * - * @see https://www.php.net/manual/en/arrayaccess.offsetget.php - * @param int $offset Character offset. - * @return string Character at that offset. - */ - public function offsetGet(mixed $offset): mixed { - return $this->value[$offset]; - } - - /** - * Gets an iterator object for this string. - * - * @return StringIterator An iterator for this string. - */ - public function getIterator(): Traversable { - return new StringIterator($this); - } - - /** - * Returns the data which should be serialized as json. - * - * @see https://www.php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed Data to be passed to json_encode. - */ - public function jsonSerialize(): mixed { - return $this->value; - } - - public function bencodeSerialise(): mixed { - return $this->value; - } - - /** - * Gets a serialized representation of this object. - * - * @return array Serialized data. - */ - public function __serialize(): array { - return [$this->value]; - } - - /** - * Reconstructs an object from a serialized string. - * - * @param array $serialized Serialized data. - */ - public function __unserialize(array $serialized): void { - $this->value = $serialized[0]; - } - - /** - * Checks whether this string is identical to another. - * - * @param mixed $other An instance of AString or a PHP string. - * @return bool true if the strings have the same value, false if not. - */ - public function equals(mixed $other): bool { - return $this->compare($other) === 0; - } - - /** - * Compares whether this string is identical to another. - * - * @param mixed $other An instance of IString or a PHP string. - */ - public function compare(mixed $other): int { - return strcmp($this->value, (string)$other); - } - - /** - * Creates a new identical AString instance. - * - * This method is somewhat pointless, given the immutable nature of this object, - * but rather people calling this instead of calling ->substring(0); - * - * @return AString A new identical instance of AString. - */ - public function clone(): mixed { - return new AString($this->value); - } - - public function indexOf(IString|string $text, int $offset = 0): int { - $pos = strpos($this->value, (string)$text, $offset); - if($pos === false) - return -1; - return $pos; - } - - public function contains(IString|string $text): bool { - return str_contains($this->value, (string)$text); - } - - public function substring(int $offset, int|null $length = null): IString { - return new AString(substr($this->value, $offset, $length)); - } - - public function replace(IString|string $search, IString|string $replace): IString { - return new AString(str_replace((string)$search, (string)$replace, $this->value)); - } - - public function append(IString|string $string): IString { - return new AString($this->value . (string)$string); - } - - public function prepend(IString|string $string): IString { - return new AString(((string)$string) . $this->value); - } - - public function split(IString|string $separator, int $limit = PHP_INT_MAX): array { - $separator = (string)$separator; - if(empty($separator)) - throw new InvalidArgumentException('$separator may not be empty.'); - return XArray::select( - explode($separator, $this->value, $limit), - fn($str) => new AString($str) - ); - } - - public function chunk(int $chunkSize): array { - return XArray::select( - str_split($this->value, $chunkSize), - fn($str) => new AString($str) - ); - } - - public function trim(IString|string $characters = IString::TRIM_CHARS): IString { - return new AString(trim($this->value, (string)$characters)); - } - - public function trimStart(IString|string $characters = IString::TRIM_CHARS): IString { - return new AString(ltrim($this->value, (string)$characters)); - } - - public function trimEnd(IString|string $characters = IString::TRIM_CHARS): IString { - return new AString(rtrim($this->value, (string)$characters)); - } - - public function toLower(): IString { - return new AString(strtolower($this->value)); - } - - public function toUpper(): IString { - return new AString(strtoupper($this->value)); - } - - public function reverse(): IString { - return new AString(strrev($this->value)); - } - - public function countUnique(): int { - return XString::countUnique($this->value); - } - - public function startsWith(IString|string $text): bool { - return str_starts_with($this->value, (string)$text); - } - - public function endsWith(IString|string $text): bool { - return str_ends_with($this->value, (string)$text); - } - - /** - * Casts this AString to a WString. - * - * @param ?string $encoding Intended encoding, null for the Index-level default. - * @param bool $convert true to convert the string to the target encoding, false to leave the bytes as-is. - * @return WString A WString of the provided encoding with the value of this AString. - */ - public function toWString(?string $encoding = null, bool $convert = true): WString { - $value = $this->value; - $encoding ??= WString::getDefaultEncoding(); - if($convert) - $value = mb_convert_encoding($value, $encoding, 'ascii'); - return new WString($value, $encoding); - } - - /** - * Joins an iterable object together with a separator to create a string. - * - * @param iterable $source Source object. - * @param IString|string $separator Separator to use as glue. - * @return AString Resulting string. - */ - public static function join(iterable $source, IString|string $separator = ''): AString { - if(!is_array($source)) { - $parts = []; - foreach($source as $value) - $parts[] = $value; - $source = $parts; - } - - return new AString(implode((string)$separator, $source)); - } - - /** - * Returns a reusable empty string instance. - * - * @return AString An empty string. - */ - public static function empty(): AString { - static $empty = null; - $empty ??= new AString(''); - return $empty; - } - - /** - * Converts a value to AString. - * - * @param mixed $value Source value. - * @return AString An AString representing the given value. - */ - public static function cast(mixed $value): AString { - if($value instanceof AString) - return $value; - if($value instanceof WString) - return $value->toAString(); - return new AString((string)$value); - } -} diff --git a/src/DateTime.php b/src/DateTime.php index 008a685..cfdda7b 100644 --- a/src/DateTime.php +++ b/src/DateTime.php @@ -1,7 +1,7 @@ add(TimeSpan::fromMicroseconds($micros)); } - /** - * Alias for subtract, must exist because of the DateTimeImmutable inheritance but I don't like short names. - * - * @internal - */ - public function sub(DateInterval $interval): DateTime { - return $this->subtract($interval); - } - /** * Subtracts a time period from this DateTime. * diff --git a/src/ICloneable.php b/src/ICloneable.php deleted file mode 100644 index 5011995..0000000 --- a/src/ICloneable.php +++ /dev/null @@ -1,18 +0,0 @@ -chunk(1) instead. - * - * @see https://www.php.net/manual/en/function.explode - * @param IString|string $separator The boundary string. - * @param int $limit Maximum number of elements expected in the array. - * @return array The resulting array of substrings. - */ - function split(IString|string $separator, int $limit = PHP_INT_MAX): array; - - /** - * Splits a string in substring chunks of a given length. - * - * @see https://www.php.net/manual/en/function.str-split.php - * @param int $chunkSize Maximum length of a chunk. - * @return array The resulting array of substrings. - */ - function chunk(int $chunkSize): array; - - /** - * Strip whitespace or other characters from the beginning and end of a string. - * - * @see https://www.php.net/manual/en/function.trim - * @param IString|string $characters Characters to strip. - * @return IString A new instance of IString with the characters removed. - */ - function trim(IString|string $characters = self::TRIM_CHARS): IString; - - /** - * Strip whitespace or other characters from the beginning of a string. - * - * @see https://www.php.net/manual/en/function.ltrim.php - * @param IString|string $characters Characters to strip. - * @return IString A new instance of IString with the characters removed. - */ - function trimStart(IString|string $characters = self::TRIM_CHARS): IString; - - /** - * Strip whitespace or other characters from the end of a string. - * - * @see https://www.php.net/manual/en/function.rtrim.php - * @param IString|string $characters Characters to strip. - * @return IString A new instance of IString with the characters removed. - */ - function trimEnd(IString|string $characters = self::TRIM_CHARS): IString; - - /** - * Convert string to lowercase. - * - * @see https://www.php.net/manual/en/function.strtolower.php - * @return IString A new IString instance with all lowercase characters. - */ - function toLower(): IString; - - /** - * Convert string to uppercase. - * - * @see https://www.php.net/manual/en/function.strtoupper.php - * @return IString A new IString instance with all uppercase characters. - */ - function toUpper(): IString; - - /** - * Reverses a string. - * - * @see https://www.php.net/manual/en/function.strrev - * @return IString A new IString instance containing the reversed string. - */ - function reverse(): IString; - - /** - * Counts unique characters in a string. - * - * @return int Unique character count. - */ - function countUnique(): int; - - /** - * Returns a copy of the standard PHP string. - * - * @return string PHP string. - */ - function __toString(): string; -} diff --git a/src/StringIterator.php b/src/StringIterator.php deleted file mode 100644 index 3988ccf..0000000 --- a/src/StringIterator.php +++ /dev/null @@ -1,83 +0,0 @@ -value = $string; - $this->length = $string->getLength(); - } - - /** - * Returns the current character. - * - * @see https://www.php.net/manual/en/iterator.current.php - * @return mixed Current character. - */ - public function current(): mixed { - return $this->value[$this->index]; - } - - /** - * Returns the index of the current character. - * - * @see https://www.php.net/manual/en/iterator.key.php - * @return int Index of the current character. - */ - public function key(): mixed { - return $this->index; - } - - /** - * Move forward to the next character. - * - * @see https://www.php.net/manual/en/iterator.next.php - */ - public function next(): void { - $next = $this->index + 1; - $this->wasValid = $next < $this->length; - - if($this->wasValid) - $this->index = $next; - } - - /** - * Rewind to the first character. - * - * @see https://www.php.net/manual/en/iterator.rewind.php - */ - public function rewind(): void { - $this->index = 0; - $this->wasValid = true; - } - - /** - * Checks if the current index is valid. - * - * @see https://www.php.net/manual/en/iterator.rewind.php - * @return bool Whether the current index is valid. - */ - public function valid(): bool { - return $this->wasValid; - } -} diff --git a/src/WString.php b/src/WString.php index cbf759d..ecdbbd2 100644 --- a/src/WString.php +++ b/src/WString.php @@ -1,394 +1,175 @@ value = $value; - $this->encoding = mb_preferred_mime_name($encoding); - } - - public function getLength(): int { - return mb_strlen($this->value, $this->encoding); + /** + * Checks if a multibyte string starts with a given substring. + * + * @param Stringable|string $haystack String to search in. + * @param Stringable|string $needle Sustring to search for in the haystack. + * @param ?string $encoding String character encoding. null for mb_internal_encoding value. + * @return bool true if haystack begins with needle, false otherwise. + */ + public static function startsWith(Stringable|string $haystack, Stringable|string $needle, ?string $encoding = null): bool { + return mb_strpos((string)$haystack, (string)$needle, encoding: $encoding) === 0; } /** - * Returns the amount of bytes the string contains. + * Checks if a multibyte string ends with a given substring. * - * @return int Amount of raw bytes. + * @param Stringable|string $haystack String to search in. + * @param Stringable|string $needle Sustring to search for in the haystack. + * @param ?string $encoding String character encoding. null for mb_internal_encoding value. + * @return bool true if haystack ends with needle, false otherwise. */ - public function getByteCount(): int { - return strlen($this->value); + public static function endsWith(Stringable|string $haystack, Stringable|string $needle, ?string $encoding = null): bool { + $haystack = (string)$haystack; + $haystackLength = mb_strlen($haystack, $encoding); + + $needle = (string)$needle; + $needleLength = mb_strlen($needle, $encoding); + + return mb_substr($haystack, -$needleLength, encoding: $encoding) === $needle; } - public function isEmpty(): bool { - // an empty string is an empty string so this should be fine regardless of encoding - return $this->value === ''; - } + private static function trimInternal(Stringable|string $string, string $chars, ?string $encoding, ?string $charsEncoding, int $flags): string { + $encoding = $encoding === null ? mb_internal_encoding() : mb_preferred_mime_name($encoding); + $charsEncoding = $charsEncoding === null ? self::TRIM_CHARS_CHARSET : mb_preferred_mime_name($charsEncoding); - public function indexOf(IString|string $text, int $offset = 0): int { - $text = (string)self::castEncoding($text, $this->encoding); - $pos = mb_strpos($this->value, $text, $offset, $this->encoding); - if($pos === false) - return -1; - return $pos; - } + // this fucks, i hate character sets + if($encoding !== $charsEncoding) { + $questionMarkCharsEnc = mb_convert_encoding('?', $charsEncoding, 'utf-8'); + $questionMarkStrEnc = mb_convert_encoding('?', $encoding, 'utf-8'); + $hasQuestionMark = mb_strpos($chars, $questionMarkCharsEnc, encoding: $charsEncoding) !== false; + $chars = mb_convert_encoding($chars, $encoding, $charsEncoding); - public function contains(IString|string $text): bool { - return str_contains($this->value, (string)$text); - } + if(!$hasQuestionMark) { + $charsSplit = mb_str_split($chars, encoding: $encoding); + $chars = []; + foreach($charsSplit as $char) { + if(in_array($char, $chars)) + continue; + $chars[] = $char; + } - public function substring(int $offset, int|null $length = null): IString { - return new WString(mb_substr($this->value, $offset, $length, $this->encoding), $this->encoding); - } + $chars = implode($chars); + } + } - public function startsWith(IString|string $text): bool { - $text = self::castEncoding($text, $this->encoding); - return $this->substring(0, $text->getLength())->equals($text); - } - - public function endsWith(IString|string $text): bool { - $text = self::castEncoding($text, $this->encoding); - return $this->substring(0 - $text->getLength())->equals($text); - } - - public function replace(IString|string $search, IString|string $replace): IString { - $search = (string)self::castEncoding($search, $this->encoding); - $replace = (string)self::castEncoding($replace, $this->encoding); - $parts = self::doRegex(fn() => mb_split(preg_quote($search), $this->value)); - $subject = implode($replace, $parts); - return new WString($subject, $this->encoding); - } - - public function append(IString|string $string): IString { - $string = self::castEncoding($string, $this->encoding); - return new WString($this->value . $string->value, $this->encoding); - } - - public function prepend(IString|string $string): IString { - $string = self::castEncoding($string, $this->encoding); - return new WString($string->value . $this->value, $this->encoding); - } - - public function split(IString|string $separator, int $limit = PHP_INT_MAX): array { - return XArray::select( - self::doRegex(fn() => mb_split(preg_quote((string)$separator), $this->value, $limit)), - fn($str) => new WString($str, $this->encoding) - ); - } - - public function chunk(int $chunkSize): array { - return XArray::select( - mb_str_split($this->value, $chunkSize, $this->encoding), - fn($str) => new WString($str, $this->encoding) - ); - } - - private function trimInternal(IString|string $characters, int $flags): IString { - if($flags & 0x01) { - if($characters instanceof WString) - $characters = (string)$characters->convertEncoding($this->encoding); - else - $characters = mb_convert_encoding((string)$characters, $this->encoding, 'ascii'); - } else - $characters = (string)$characters; + $string = (string)$string; + $split = mb_str_split($string, encoding: $encoding); + $length = mb_strlen($string, $encoding); $start = 0; - $end = $this->getLength() - 1; + $end = $length - 1; - if($flags & 0x02) - for(; $start < $this->getLength(); ++$start) - if(mb_strpos($characters, $this[$start], 0, $this->encoding) === false) + if($flags & self::TRIM_START) + for(; $start < $length; ++$start) + if(mb_strpos($chars, $split[$start], encoding: $encoding) === false) break; - if($flags & 0x04) + if($flags & self::TRIM_END) for(; $end > 0; --$end) - if(mb_strpos($characters, $this[$end], 0, $this->encoding) === false) + if(mb_strpos($chars, $split[$end], encoding: $encoding) === false) break; - return $this->substring($start, $end - $start + 1); + return mb_substr($string, $start, $end - $start + 1, $encoding); } - public function trim(IString|string $characters = IString::TRIM_CHARS, bool $convertChars = true): IString { - $flags = 0x06; - if($convertChars) $flags |= 0x01; - return $this->trimInternal($characters, $flags); + /** + * Strip whitespace (or other characters) from the start and end of a multibyte string. + * + * @param Stringable|string $string Input string. + * @param string $chars Characters to strip. List all characters you want. .. operator from trim is not supported. + * @param ?string $encoding String character encoding. null for mb_internal_encoding value. + * @return string Trimmed string. + */ + public static function trim(Stringable|string $string, string $chars = self::TRIM_CHARS, ?string $encoding = null, ?string $charsEncoding = null): string { + return self::trimInternal($string, $chars, $encoding, $charsEncoding, self::TRIM_ALL); } - public function trimStart(IString|string $characters = IString::TRIM_CHARS, bool $convertChars = true): IString { - $flags = 0x02; - if($convertChars) $flags |= 0x01; - return $this->trimInternal($characters, $flags); + /** + * Strip whitespace (or other characters) from the start of a multibyte string. + * + * @param Stringable|string $string Input string. + * @param string $chars Characters to strip. List all characters you want. .. operator from ltrim is not supported. + * @param ?string $encoding String character encoding. null for mb_internal_encoding value. + * @return string Trimmed string. + */ + public static function trimStart(Stringable|string $string, string $chars = self::TRIM_CHARS, ?string $encoding = null, ?string $charsEncoding = null): string { + return self::trimInternal($string, $chars, $encoding, $charsEncoding, self::TRIM_START); } - public function trimEnd(IString|string $characters = IString::TRIM_CHARS, bool $convertChars = true): IString { - $flags = 0x04; - if($convertChars) $flags |= 0x01; - return $this->trimInternal($characters, $flags); + /** + * Strip whitespace (or other characters) from the end of a multibyte string. + * + * @param Stringable|string $string Input string. + * @param string $chars Characters to strip. List all characters you want. .. operator from rtrim is not supported. + * @param ?string $encoding String character encoding. null for mb_internal_encoding value. + * @return string Trimmed string. + */ + public static function trimEnd(Stringable|string $string, string $chars = self::TRIM_CHARS, ?string $encoding = null, ?string $charsEncoding = null): string { + return self::trimInternal($string, $chars, $encoding, $charsEncoding, self::TRIM_END); } - public function toLower(): IString { - return new WString(mb_strtolower($this->value, $this->encoding), $this->encoding); + /** + * Reverses a multibyte string. + * + * @param Stringable|string $string String to reverse. + * @param ?string $encoding String character encoding. null for mb_internal_encoding value. + * @return string Reversed string. + */ + public static function reverse(Stringable|string $string, ?string $encoding = null): string { + return implode(array_reverse(mb_str_split((string)$string, encoding: $encoding))); } - public function toUpper(): IString { - return new WString(mb_strtoupper($this->value, $this->encoding), $this->encoding); - } - - public function reverse(): IString { - $chars = []; - foreach($this as $char) - $chars[] = $char; - return new WString(implode(array_reverse($chars)), $this->encoding); - } - - public function countUnique(): int { + /** + * Counts unique characters in a string. + * + * @param Stringable|string $string String to count unique characters of. + * @param ?string $encoding String character encoding. null for mb_internal_encoding value. + * @return int Unique character count. + */ + public static function countUnique(Stringable|string $string, ?string $encoding = null): int { + $string = mb_str_split((string)$string, encoding: $encoding); $chars = []; - foreach($this as $char) + foreach($string as $char) if(!in_array($char, $chars, true)) $chars[] = $char; return count($chars); } - public function __toString(): string { - return $this->value; - } - - public function toAString(bool $convert = true): AString { - $value = $this->value; - if($convert && self::sameEncoding('ascii', $this->encoding)) - $value = mb_convert_encoding($value, 'ascii', $this->encoding); - return new AString($value); - } - - public function getEncoding(): string { - return $this->encoding; - } - - public function convertEncoding(string $encoding): WString { - if(self::sameEncoding($encoding, $this->encoding)) - return $this; - $value = mb_convert_encoding($this->value, $encoding, $this->encoding); - return new WString($value, $encoding); - } - /** - * Checks if an offset exists in the string. + * Check if a multibyte string is null or whitespace. * - * You should call isset($string[$offset]) instead of $string->offsetExists($offset). - * - * @see https://www.php.net/manual/en/arrayaccess.offsetexists.php - * @param int $offset Character offset. - * @return bool true if it exists, false if not. + * @param Stringable|string|null $string String to check for whitespace. + * @param ?string $encoding String character encoding. null for mb_internal_encoding value. + * @return bool true if the string is whitespace, false if not. */ - public function offsetExists(mixed $offset): bool { - $offset = (int)$offset; - return $offset >= 0 && $offset < $this->getLength(); - } - - /** - * Gets an offset from the string. - * - * You should do $string[$offset] instead of $string->offsetGet($offset). - * - * @see https://www.php.net/manual/en/arrayaccess.offsetget.php - * @param int $offset Character offset. - * @return string Character at that offset. - */ - public function offsetGet(mixed $offset): mixed { - return mb_substr($this->value, (int)$offset, 1, $this->encoding); - } - - /** - * Gets an iterator object for this string. - * - * @return StringIterator An iterator for this string. - */ - public function getIterator(): Traversable { - return new StringIterator($this); - } - - /** - * Returns the data which should be serialized as json. - * - * @see https://www.php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed Data to be passed to json_encode. - */ - public function jsonSerialize(): mixed { - return $this->value; - } - - public function bencodeSerialise(): mixed { - return $this->value; - } - - /** - * Gets a serialized representation of this object. - * - * @return array Serialized data. - */ - public function __serialize(): array { - return [$this->encoding, $this->value]; - } - - /** - * Reconstructs an object from a serialized string. - * - * @param array $serialized Serialized data. - */ - public function __unserialize(array $serialized): void { - [$this->encoding, $this->value] = $serialized; - } - - /** - * Checks whether this string is identical to another. - * - * @param mixed $other An instance of IString or a PHP string. - * @return bool true if the strings have the same value, false if not. - */ - public function equals(mixed $other): bool { - return $this->compare($other) === 0; - } - - /** - * Compares whether this string is identical to another. - * - * @param mixed $other An instance of IString or a PHP string. - */ - public function compare(mixed $other): int { - $other = self::castEncoding($other, $this->encoding); - return strcmp($this->value, (string)$other); - } - - /** - * Creates a new identical WString instance. - * - * This method is somewhat pointless, given the immutable nature of this object, - * but rather people calling this instead of calling ->substring(0); - * - * @return WString A new identical instance of WString. - */ - public function clone(): mixed { - return new WString($this->value, $this->encoding); - } - - private function doRegex(callable $callback): mixed { - $encoding = self::getRegexEncoding(); - self::setRegexEncoding($this->encoding); - $result = $callback(); - self::setRegexEncoding($encoding); - return $result; - } - - private static function sameEncoding(string $name1, string $name2): bool { - return mb_preferred_mime_name($name1) === mb_preferred_mime_name($name2); - } - - /** - * Joins an iterable object together with a separator to create a string. - * - * @param iterable $source Source object. - * @param IString|string $separator Separator to use as glue. - * @param ?string $encoding Desired encoding, null for Index default. - * @return WString Resulting string. - */ - public static function join(iterable $source, IString|string $separator = '', ?string $encoding = null): WString { - // func probably doesn't work entirely as intended - $encoding ??= self::getDefaultEncoding(); - $separator = self::castEncoding($separator, $encoding); - if(!is_array($source)) { - $parts = []; - foreach($source as $value) - $parts[] = $value; - $source = $parts; - } - - return new WString(implode((string)$separator, $source), $encoding); - } - - public static function getDefaultEncoding(): string { - return self::$defaultEncoding; - } - - public static function setDefaultEncoding(string $encoding = ''): void { - if(empty($encoding)) - self::$defaultEncoding = self::DEFAULT_ENCODING; - else { - try { - if(mb_encoding_aliases($encoding) !== false) - self::$defaultEncoding = $encoding; - } catch(ValueError $ex) {} - } - } - - public static function getInternalEncoding(): string { - return mb_internal_encoding(); - } - - public static function setInternalEncoding(string $encoding): void { - mb_internal_encoding($encoding); - } - - public static function getRegexEncoding(): string { - return mb_regex_encoding(); - } - - public static function setRegexEncoding(string $encoding): void { - mb_regex_encoding($encoding); - } - - public static function fromRequest(string $raw): WString { - return new WString($raw, self::getInternalEncoding()); - } - - /** - * Returns a reusable empty string instance. - * - * @return WString An empty string. - */ - public static function empty(): WString { - static $empty = null; - $empty ??= new WString(''); - return $empty; - } - - public static function cast(mixed $string): WString { - if($string instanceof WString) - return $string; - if($string instanceof AString) - return $string->toWString('ascii', false); - return new WString((string)$string, 'ascii'); - } - - public static function castEncoding(mixed $string, string $encoding): WString { - return self::cast($string)->convertEncoding($encoding); + public static function nullOrWhitespace(Stringable|string|null $string, ?string $encoding = null): bool { + return $string === null || self::trim((string)$string, encoding: $encoding) === ''; } } diff --git a/src/XString.php b/src/XString.php index 9cd05e6..13b3134 100644 --- a/src/XString.php +++ b/src/XString.php @@ -1,7 +1,7 @@ 0) + $string .= $chars[random_int(0, $count)]; + + return $string; + } + /** * Converts special characters to HTML entities. * @@ -63,9 +85,7 @@ final class XString { * @return bool true if the string is empty, false if not. */ public static function nullOrEmpty(Stringable|string|null $string): bool { - if($string === null) - return true; - return empty((string)$string); + return $string === null || (string)$string === ''; } /** @@ -75,30 +95,6 @@ final class XString { * @return bool true if the string is whitespace, false if not. */ public static function nullOrWhitespace(Stringable|string|null $string): bool { - if($string === null) - return true; - return empty(trim((string)$string)); - } - - /** - * Generates a random string of user specified length. - * - * @param int $length Desired length of the string. - * @param string $chars Set of characters to pick from. Default set contains the alphabet in upper- and lowercase and numbers 0 thru 9. - * @return string The generated string. - */ - public static function random(int $length, string $chars = self::RANDOM_CHARS): string { - if($length < 1) - throw new InvalidArgumentException('$length must be at least 1.'); - if($chars === '') - throw new InvalidArgumentException('$chars may not be empty.'); - - $string = ''; - $count = strlen($chars) - 1; - - while($length-- > 0) - $string .= $chars[random_int(0, $count)]; - - return $string; + return $string === null || trim((string)$string) === ''; } } diff --git a/src/XStringTrait.php b/src/XStringTrait.php deleted file mode 100644 index e4aab53..0000000 --- a/src/XStringTrait.php +++ /dev/null @@ -1,65 +0,0 @@ -getLength(); - } - - /** - * Necessary to comply with the ArrayAccess interface. - * - * @internal - */ - public function offsetSet(mixed $offset, mixed $value): void { - throw new BadMethodCallException('Strings are immutable.'); - } - - /** - * Necessary to comply with the ArrayAccess interface. - * - * @internal - */ - public function offsetUnset(mixed $offset): void { - throw new BadMethodCallException('Strings are immutable.'); - } - - public function toBool(): bool { - return boolval((string)$this); - } - - public function toInt(int $base = 10): int { - return $base === 10 ? (int)(string)$this : intval((string)$this, $base); - } - - public function toFloat(): float { - return (float)(string)$this; - } - - public function escape( - int $flags = ENT_COMPAT | ENT_HTML5, - ?string $encoding = null, - bool $doubleEncoding = true - ): IString { - return new AString(XString::escape($this, $flags, $encoding, $doubleEncoding)); - } -} diff --git a/tests/StringTest.php b/tests/StringTest.php deleted file mode 100644 index b53949c..0000000 --- a/tests/StringTest.php +++ /dev/null @@ -1,287 +0,0 @@ -assertEquals(4, $ascii->getLength()); - $this->assertEquals(4, count($ascii)); - $this->assertFalse($ascii->isEmpty()); - - $utf8 = new WString('ßóúþ', 'utf-8'); - $this->assertEquals(4, $utf8->getLength()); - $this->assertEquals(4, count($utf8)); - $this->assertEquals(8, $utf8->getByteCount()); - $this->assertFalse($utf8->isEmpty()); - - $utf16be = new WString("\x00\xDF\x00\xF3\x00\xFA\x00\xFE", 'UTF-16BE'); - $this->assertEquals(4, $utf16be->getLength()); - $this->assertEquals(4, count($utf16be)); - $this->assertEquals(8, $utf16be->getByteCount()); - $this->assertFalse($utf16be->isEmpty()); - - $utf32le = new WString("\xDF\x00\x00\x00\xF3\x00\x00\x00\xFA\x00\x00\x00\xFE\x00\x00\x00", 'UTF-32LE'); - $this->assertEquals(4, $utf32le->getLength()); - $this->assertEquals(4, count($utf32le)); - $this->assertEquals(16, $utf32le->getByteCount()); - $this->assertFalse($utf32le->isEmpty()); - - $this->assertTrue($utf8->equals($utf16be)); - $this->assertTrue($utf16be->equals($utf32le)); - $this->assertTrue($utf32le->equals($utf8)); - } - - public function testToString(): void { - $ascii = new AString('Windows XP'); - $this->assertEquals('Windows XP', (string)$ascii); - - $utf16be = new WString("\x00\xDF\x00\xF3\x00\xFA\x00\xFE", 'UTF-16BE'); - $this->assertEquals("\x00\xDF\x00\xF3\x00\xFA\x00\xFE", (string)$utf16be); - - $utf32le = $utf16be->convertEncoding('utf-32le'); - $this->assertEquals("\xDF\x00\x00\x00\xF3\x00\x00\x00\xFA\x00\x00\x00\xFE\x00\x00\x00", (string)$utf32le); - } - - public function testArrayAccess(): void { - $ascii = new AString('Akai Haato'); - - $this->assertEquals('a', $ascii[2]); - $this->assertEquals('H', $ascii[5]); - - $this->assertTrue(isset($ascii[6])); - $this->assertFalse(isset($ascii[23])); - - $this->expectException(\BadMethodCallException::class); - $ascii[4] = '_'; - - $this->expectException(\BadMethodCallException::class); - unset($ascii[7]); - - $utf16le = $ascii->toWString('utf-16le'); - - $this->assertEquals('a', $utf16le[2]); - $this->assertEquals('H', $utf16le[5]); - - $this->assertTrue(isset($utf16le[6])); - $this->assertFalse(isset($utf16le[23])); - - $this->expectException(\BadMethodCallException::class); - $utf16le[4] = '_'; - - $this->expectException(\BadMethodCallException::class); - unset($utf16le[7]); - } - - public function testJsonSerialize(): void { - $ascii = new AString('Mewow'); - $this->assertEquals('Mewow', json_decode(json_encode($ascii))); - $this->assertEquals('Mewow', json_decode(json_encode($ascii->toWString('Shift_JIS')))); - } - - public function testSerializable(): void { - $ascii = new AString('Futami Mami'); - $asciiSerialised = serialize($ascii); - - $sjis = new WString("\x91\x6F\x8A\x43\x90\x5E\x94\xFC", 'Shift_JIS'); - $sjisSerialised = serialize($sjis); - - $asciiUnserialised = unserialize($asciiSerialised); - $this->assertTrue($ascii->equals($asciiUnserialised)); - $this->assertTrue($asciiUnserialised->equals('Futami Mami')); - - $sjisUnserialised = unserialize($sjisSerialised); - $this->assertTrue($sjis->equals($sjisUnserialised)); - $this->assertTrue($sjisUnserialised->equals(new WString('双海真美', 'utf-8'))); - } - - public function testEquals(): void { - $ascii = new AString('Misaka Mikoto'); - $this->assertTrue($ascii->equals($ascii)); - $this->assertTrue($ascii->equals('Misaka Mikoto')); - $this->assertTrue($ascii->equals(new AString('Misaka Mikoto'))); - $this->assertFalse($ascii->equals('Mewow')); - $this->assertFalse($ascii->equals(new AString('Ndx Mewow'))); - } - - public function testConcat(): void { - $ndxString1 = new AString('Akai '); - $ndxString2 = $ndxString1->append('Haato'); - $ndxString3 = $ndxString1->append(new AString('Haato')); - $ndxString4 = (new AString('Haato'))->prepend($ndxString1); - - $this->assertEquals('Akai Haato', (string)$ndxString2); - $this->assertEquals('Akai Haato', (string)$ndxString3); - $this->assertEquals('Akai Haato', (string)$ndxString4); - } - - public function testSplit(): void { - $ascii = new AString('Kasane Teto'); - $asciiArr = ['Kasane', 'Teto']; - $this->assertTrue(XArray::sequenceEquals($asciiArr, $ascii->split(' '))); - - $utf16le = new WString("\x42\x30\x02\x30\x44\x30\x02\x30\x46\x30\x02\x30\x48\x30\x02\x30\x4A\x30\x02\x30", 'utf-16le'); - $utf16leArr = [ - new WString("\x42\x30", 'utf-16le'), - new WString("\x44\x30", 'utf-16le'), - new WString("\x46\x30", 'utf-16le'), - new WString("\x48\x30", 'utf-16le'), - new WString("\x4A\x30", 'utf-16le'), - new WString("", 'utf-16le'), - ]; - $this->assertTrue(XArray::sequenceEquals($utf16leArr, $utf16le->split(new WString("\x02\x30", 'utf-16le')))); - - $utf8 = new WString('あ。い。う。え。お。', 'utf-8'); - $utf8Arr = [ - new Wstring('あ', 'utf-8'), - new Wstring('い', 'utf-8'), - new Wstring('う', 'utf-8'), - new Wstring('え', 'utf-8'), - new Wstring('お', 'utf-8'), - new Wstring('', 'utf-8'), - ]; - $this->assertTrue(XArray::sequenceEquals($utf8Arr, $utf8->split(new WString('。', 'utf-8')))); - } - - public function testChunk(): void { - $ndxString = new AString('abcdefghijklmnopqrstuvwxyz'); - $ndxArray1 = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vwx', 'yz']; - $ndxArray2 = ['ab', 'cd', 'ef', 'gh', 'ij', 'kl', 'mn', 'op', 'qr', 'st', 'uv', 'wx', 'yz']; - $ndxArray3 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; - - $this->assertTrue(XArray::sequenceEquals($ndxString->chunk(3), $ndxArray1)); - $this->assertTrue(XArray::sequenceEquals($ndxString->chunk(2), $ndxArray2)); - $this->assertTrue(XArray::sequenceEquals($ndxString->chunk(1), $ndxArray3)); - - $utf8 = new WString('君は実にばかだな', 'utf-8'); - $ndxArray4 = [ - new WString('君は実', 'utf-8'), - new WString('にばか', 'utf-8'), - new WString('だな', 'utf-8'), - ]; - $ndxArray5 = [ - new WString('君は', 'utf-8'), - new WString('実に', 'utf-8'), - new WString('ばか', 'utf-8'), - new WString('だな', 'utf-8'), - ]; - $ndxArray6 = [ - new WString('君', 'utf-8'), - new WString('は', 'utf-8'), - new WString('実', 'utf-8'), - new WString('に', 'utf-8'), - new WString('ば', 'utf-8'), - new WString('か', 'utf-8'), - new WString('だ', 'utf-8'), - new WString('な', 'utf-8'), - ]; - - $this->assertTrue(XArray::sequenceEquals($utf8->chunk(3), $ndxArray4)); - $this->assertTrue(XArray::sequenceEquals($utf8->chunk(2), $ndxArray5)); - $this->assertTrue(XArray::sequenceEquals($utf8->chunk(1), $ndxArray6)); - } - - public function testTrim(): void { - $ascii = new AString(' meow '); - $this->assertTrue($ascii->trim()->equals('meow')); - $this->assertTrue($ascii->trimStart()->equals('meow ')); - $this->assertTrue($ascii->trimEnd()->equals(' meow')); - - $utf16be = $ascii->toWString('utf-16be'); - $this->assertTrue($utf16be->trim()->equals('meow')); - $this->assertTrue($utf16be->trimStart()->equals('meow ')); - $this->assertTrue($utf16be->trimEnd()->equals(' meow')); - - $utf8 = new WString(' にゃ ', 'utf-8'); - $this->assertTrue($utf8->trim()->equals(new WString('にゃ', 'utf-8'))); - $this->assertTrue($utf8->trimStart()->equals(new WString('にゃ ', 'utf-8'))); - $this->assertTrue($utf8->trimEnd()->equals(new WString(' にゃ', 'utf-8'))); - } - - public function testCaseChange(): void { - $stringMixed = new AString('Sometimes'); - $stringUpper = new AString('SOMETIMES'); - $stringLower = new AString('sometimes'); - - $this->assertTrue($stringMixed->toUpper()->equals($stringUpper)); - $this->assertTrue($stringMixed->toLower()->equals($stringLower)); - } - - public function testEscape(): void { - $stringDirty = new AString(''); - $stringClean = new AString('<img onerror="alert(\'xss\')">'); - - $this->assertTrue($stringDirty->escape()->equals($stringClean)); - } - - public function testJoin(): void { - $ndxString1 = AString::join(['flash', 'wave']); - $ndxString2 = AString::join(['Misaka', 'Mikoto'], new AString(' ')); - - $this->assertEquals('flashwave', (string)$ndxString1); - $this->assertEquals('Misaka Mikoto', (string)$ndxString2); - } - - public function testEmpty(): void { - $this->assertTrue(XString::nullOrEmpty(null)); - $this->assertTrue(XString::nullOrEmpty('')); - $this->assertTrue(XString::nullOrEmpty(new AString(''))); - $this->assertFalse(XString::nullOrEmpty('soap')); - $this->assertFalse(XString::nullOrEmpty(new AString('boat'))); - $this->assertTrue(XString::nullOrWhitespace('')); - $this->assertTrue(XString::nullOrWhitespace(' ')); - $this->assertTrue(XString::nullOrWhitespace(new AString(' '))); - } - - public function testReverse(): void { - $ascii = new AString('Futami Mami'); - $sjis = new WString("\x91\x6F\x8A\x43\x90\x5E\x94\xFC", 'Shift_JIS'); - - $asciiRev = $ascii->reverse(); - $sjisRev = $sjis->reverse(); - - $this->assertEquals('imaM imatuF', (string)$asciiRev); - $this->assertEquals("\x94\xFC\x90\x5E\x8A\x43\x91\x6F", (string)$sjisRev); - } - - public function testReplace(): void { - $ascii = new AString('aiueo'); - $sjis = new WString("\x82\xA0\x82\xA2\x82\xA4\x82\xA6\x82\xA8", 'sjis'); - - $this->assertEquals('aikeo', $ascii->replace('u', 'k')); - $this->assertEquals('aeoeo', $ascii->replace('iu', 'eo')); - - $this->assertTrue( - $sjis->replace( - new WString("\x46\x30", 'utf-16le'), - new WString("\xE3\x81\x8B", 'utf-8') - )->equals(new WString("\x82\xA0\x82\xA2\x82\xA9\x82\xA6\x82\xA8", 'sjis')) - ); - $this->assertTrue( - $sjis->replace( - new WString("\xE3\x81\x84\xE3\x81\x86", 'utf-8'), - new WString("\x48\x30\x4A\x30", 'utf-16le') - )->equals(new WString("\x82\xA0\x82\xA6\x82\xA8\x82\xA6\x82\xA8", 'sjis')) - ); - } - - public function testCountUnique(): void { - $this->assertEquals(10, XString::countUnique('iaabbccddjjefghefghi')); - $this->assertEquals(11, (new AString('jeff has three apples'))->countUnique()); - $this->assertEquals(5, (new WString('みさかみこと'))->countUnique()); - } -} diff --git a/tests/WStringTest.php b/tests/WStringTest.php new file mode 100644 index 0000000..8da6bed --- /dev/null +++ b/tests/WStringTest.php @@ -0,0 +1,47 @@ +assertTrue(WString::startsWith('君は実にバカだな', '君は')); + } + + public function testEndsWith(): void { + $this->assertTrue(WString::endsWith('君は実にバカだな', 'バカだな')); + } + + public function testReverse(): void { + $this->assertEquals('なだカバに実は君', WString::reverse('君は実にバカだな')); + $this->assertEquals("\x82\xc8\x82\xbe\x83\x4a\x83\x6f\x82\xc9\x8e\xc0\x82\xcd\x8c\x4e", WString::reverse("\x8c\x4e\x82\xcd\x8e\xc0\x82\xc9\x83\x6f\x83\x4a\x82\xbe\x82\xc8", encoding: 'Shift_JIS')); + } + + public function testTrim(): void { + $utf8 = ' にゃ '; + $this->assertEquals('にゃ', WString::trim($utf8, encoding: 'utf-8')); + $this->assertEquals('にゃ ', WString::trimStart($utf8, encoding: 'utf-8')); + $this->assertEquals(' にゃ', WString::trimEnd($utf8, encoding: 'utf-8')); + } + + public function testEmpty(): void { + $this->assertTrue(WString::nullOrWhitespace('')); + $this->assertTrue(WString::nullOrWhitespace(' ')); + $this->assertTrue(WString::nullOrWhitespace(' ')); + $this->assertTrue(WString::nullOrWhitespace(' ')); + } + + public function testCountUnique(): void { + $this->assertEquals(10, WString::countUnique('iaabbccddjjefghefghi')); + $this->assertEquals(11, WString::countUnique('jeff has three apples')); + $this->assertEquals(5, WString::countUnique('みさかみこと')); + } +} diff --git a/tests/XArrayTest.php b/tests/XArrayTest.php index bb83882..45375d0 100644 --- a/tests/XArrayTest.php +++ b/tests/XArrayTest.php @@ -1,12 +1,11 @@ assertTrue(XArray::sequenceEquals($array1, $array2)); } - public function testConcat(): void { - $ndxString1 = new AString('abcde'); - $ndxString2 = new AString('a_b_c_d_e'); - $array = ['a', 'b', 'c', 'd', 'e']; - - $this->assertTrue(AString::join($array)->equals($ndxString1)); - $this->assertFalse(AString::join($array)->equals($ndxString2)); - $this->assertFalse(AString::join($array, '_')->equals($ndxString1)); - $this->assertTrue(AString::join($array, '_')->equals($ndxString2)); - } - public function testMerge(): void { $array1 = [0, 1, 2, 3, 4, 5, 6, 7]; $array2 = [0, 1, 2, 3]; diff --git a/tests/XStringTest.php b/tests/XStringTest.php new file mode 100644 index 0000000..93f3822 --- /dev/null +++ b/tests/XStringTest.php @@ -0,0 +1,34 @@ +'; + $clean = '<img onerror="alert(\'xss\')">'; + + $this->assertEquals($clean, XString::escape($dirty)); + } + + public function testEmpty(): void { + $this->assertTrue(XString::nullOrEmpty(null)); + $this->assertTrue(XString::nullOrEmpty('')); + $this->assertFalse(XString::nullOrEmpty('soap')); + $this->assertTrue(XString::nullOrWhitespace('')); + $this->assertTrue(XString::nullOrWhitespace(' ')); + } + + public function testCountUnique(): void { + $this->assertEquals(10, XString::countUnique('iaabbccddjjefghefghi')); + $this->assertEquals(11, XString::countUnique('jeff has three apples')); + } +}