diff --git a/Makefile b/Makefile index 92160c6..5d70775 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ lib-code-tests: - ./vendor/bin/phpbench run --config=bench_lib.json --report=str + ./vendor/bin/phpbench run --config=bench_lib.json --report=str --iterations=10 --revs=5000 rank: - ./vendor/bin/phpbench run --config=bench_lib.json --report=str | php benchmarks/score.php + ./vendor/bin/phpbench run --config=bench_lib.json --report=str --iterations=10 --revs=1000 | php benchmarks/score.php md: ./vendor/bin/phpbench run --config=bench_lib.json --report=str -o markdown > benchmark.md + +internal-tests: + ./vendor/bin/phpbench run --config=bench_internal.json --report=str --iterations=100 --revs=1000 diff --git a/README.md b/README.md index 1fcd8c8..5507e44 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,19 @@ _in dev, please do not use in production_ # str/str ```php -$s = new Str('Hello, 世界'); -$s->last(2); // 世界 -$s->chars(); // ['H','e','l','l','o',',',' ','世','界'] +$str = new Str('Hello, 世界'); +$str->last(2); // 世界 +$str->chars(); // ['世', '界'] -$s - ->ensureLeft('H') // Hello, 世界 +$str + ->ensureLeft('Hello, ') // Hello, 世界 ->ensureRight('!!!') // Hello, 世界!!! ->trimRight('!') // Hello, 世界 - ->append('Str say - '); // Str say - Hello, 世界 + ->prepend('Str say - '); // Str say - Hello, 世界 $send = function (string $s) {}; -$send((string)$s); // same -$send($s->getString()); // same +$send((string)$str); // same +$send($str->getString()); // same ``` A fast string manipulation library with multi-byte support. @@ -708,7 +708,7 @@ echo (string)$str->htmlEncode(); ``` ----- ### humanize -Capitalizes the first word of the string, replaces underscores with spaces, and strips '_id'. +Capitalizes the first word of the string, replaces underscores with spaces. - __return__ *Str* @@ -1192,7 +1192,7 @@ In case $destination is less than $length returns the string untouched. __Example:__ ```php $str = new Str('/Acme/'); -echo (string)$str->move(0, 2, 2); +echo (string)$str->move(0, 2, 4); // cm/Ae/ ``` ----- @@ -1350,7 +1350,7 @@ will consist of ASCII alphanumeric chars. - __param__ *int* $sizeMax If given and is > $size, the generated string will have random length between $size and $sizeMax. Defaults to -1. - __param__ *string* $possibleChars If given, specifies allowed characters to make the string of. -Defaults to empty string. +Defaults to ASCII alphanumeric chars. - __return__ *Str* __Example:__ @@ -1372,7 +1372,7 @@ random length between $size and $sizeMax to the original string. - __param__ *int* $sizeMax If given and is > $size, the generated string will have random length between $size and $sizeMax. Defaults to -1. - __param__ *string* $possibleChars If given, specifies allowed characters to make the string of. -Defaults to empty string. +Defaults to ASCII alphanumeric chars. - __return__ *Str* __Example:__ @@ -1456,7 +1456,7 @@ Returns the substring of the string from the last occurrence of $delimiter to th __Example:__ ```php $str = new Str('Acme/foo'); -echo $str->pop('/']); +echo $str->pop('/'); // foo ``` ----- @@ -1470,7 +1470,7 @@ of $delimiter. __Example:__ ```php $str = new Str('Acme/foo'); -echo $str->shift('/']); +echo $str->shift('/'); // Acme ``` ----- @@ -1483,7 +1483,7 @@ Returns the substring of the original string from the first occurrence of $delim __Example:__ ```php $str = new Str('Acme/foo/bar'); -echo $str->shiftReversed('/']); +echo $str->shiftReversed('/'); // foo/bar ``` ----- @@ -1497,7 +1497,7 @@ to the last occurrence of $delimiter. __Example:__ ```php $str = new Str('Acme/foo/bar'); -echo $str->popReversed('/']); +echo $str->popReversed('/'); // Acme/foo ``` ----- diff --git a/benchmarks/internal/ComparingArrayBench.php b/benchmarks/internal/ComparingArrayBench.php new file mode 100644 index 0000000..3d09c62 --- /dev/null +++ b/benchmarks/internal/ComparingArrayBench.php @@ -0,0 +1,28 @@ +arr1[] = \Ramsey\Uuid\Uuid::uuid4(); } + $this->arr = $this->arr1; + $this->arr2 = $this->arr1; + $this->count = \count($this->arr2); + } + + public function bench_while() + { + $t = $this->count; + while ($t--) { + $this->arr2[$t] = substr($this->arr2[$t], 0, 8); + } + } + + public function bench_for() + { + for ($i=0, $iMax=count($this->arr);$i<$iMax;$i++) { + $this->arr[$i] = substr($this->arr[$i], 0, 8); + } + } + + public function bench_foreach() + { + foreach ($this->arr1 as &$i) { + $i = substr($i, 0, 8); + } + unset($i); + } +} diff --git a/benchmarks/internal/ComparingStringBench.php b/benchmarks/internal/ComparingStringBench.php new file mode 100644 index 0000000..067b9dd --- /dev/null +++ b/benchmarks/internal/ComparingStringBench.php @@ -0,0 +1,34 @@ +replace('k', 'ky', 3); + (new Str('oink oink oink'))->replace('k', 'ky'); } public function bench_replace_Stringy() { diff --git a/benchmarks/lib/TrimBench.php b/benchmarks/lib/TrimBench.php index ef89947..64fb033 100644 --- a/benchmarks/lib/TrimBench.php +++ b/benchmarks/lib/TrimBench.php @@ -8,26 +8,26 @@ class TrimBench { public function bench_both_Str() { - (new Str(' hello world '))->trim(); + (new Str(' hello world '))->trim(' h'); } public function bench_both_Stringy() { - (new Stringy(' hello world '))->trim(); + (new Stringy(' hello world '))->trim(' h'); } public function bench_left_Str() { - (new Str(' hello world '))->trimLeft(); + (new Str(' hello world '))->trimLeft(' h'); } public function bench_left_Stringy() { - (new Stringy(' hello world '))->trimLeft(); + (new Stringy(' hello world '))->trimLeft(' h'); } public function bench_right_Str() { - (new Str(' hello world '))->trimRight(); + (new Str(' hello world '))->trimRight(' h'); } public function bench_right_Stringy() { - (new Stringy(' hello world '))->trimRight(); + (new Stringy(' hello world '))->trimRight(' h'); } } diff --git a/src/Str.php b/src/Str.php index 1f8bda9..a5fa011 100644 --- a/src/Str.php +++ b/src/Str.php @@ -6,1873 +6,806 @@ class Str { - /** - * @var string - * @internal - */ private $__str_buffer; - public function __construct($str) - { - $this->__str_buffer = $str; - } - - public function __toString(): string - { - return $this->__str_buffer; - } + const CHARS_ARRAY = [ + '0' => ['°', '₀', '۰', '0'], + '1' => ['¹', '₁', '۱', '1'], + '2' => ['²', '₂', '۲', '2'], + '3' => ['³', '₃', '۳', '3'], + '4' => ['⁴', '₄', '۴', '٤', '4'], + '5' => ['⁵', '₅', '۵', '٥', '5'], + '6' => ['⁶', '₆', '۶', '٦', '6'], + '7' => ['⁷', '₇', '۷', '7'], + '8' => ['⁸', '₈', '۸', '8'], + '9' => ['⁹', '₉', '۹', '9'], + 'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', + 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', + 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', + 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', + 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', + 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä'], + 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'], + 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'], + 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', + 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'], + 'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', + 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', + 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', + 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'], + 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'], + 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'], + 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'], + 'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', + 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', + 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', + 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', + 'इ', 'ی', 'i'], + 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'], + 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', + 'ک', 'k'], + 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'], + 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'], + 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'], + 'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', + 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', + 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', + 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', + 'ö'], + 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'], + 'q' => ['ყ', 'q'], + 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'], + 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'], + 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'], + 'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', + 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', + 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', + 'ў', 'ü'], + 'v' => ['в', 'ვ', 'ϐ', 'v'], + 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'], + 'x' => ['χ', 'ξ', 'x'], + 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', + 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'], + 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'], + 'aa' => ['ع', 'आ', 'آ'], + 'ae' => ['æ', 'ǽ'], + 'ai' => ['ऐ'], + 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'], + 'dj' => ['ђ', 'đ'], + 'dz' => ['џ', 'ძ'], + 'ei' => ['ऍ'], + 'gh' => ['غ', 'ღ'], + 'ii' => ['ई'], + 'ij' => ['ij'], + 'kh' => ['х', 'خ', 'ხ'], + 'lj' => ['љ'], + 'nj' => ['њ'], + 'oe' => ['œ', 'ؤ'], + 'oi' => ['ऑ'], + 'oii' => ['ऒ'], + 'ps' => ['ψ'], + 'sh' => ['ш', 'შ', 'ش'], + 'shch' => ['щ'], + 'ss' => ['ß'], + 'sx' => ['ŝ'], + 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'], + 'ts' => ['ц', 'ც', 'წ'], + 'uu' => ['ऊ'], + 'ya' => ['я'], + 'yu' => ['ю'], + 'zh' => ['ж', 'ჟ', 'ژ'], + '(c)' => ['©'], + 'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', + 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', + 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', + 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', + 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'], + 'B' => ['Б', 'Β', 'ब', 'B'], + 'C' => ['Ç','Ć', 'Č', 'Ĉ', 'Ċ', 'C'], + 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'], + 'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', + 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', + 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', + 'Є', 'Ə', 'E'], + 'F' => ['Ф', 'Φ', 'F'], + 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'], + 'H' => ['Η', 'Ή', 'Ħ', 'H'], + 'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', + 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', + 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', + 'I'], + 'J' => ['J'], + 'K' => ['К', 'Κ', 'K'], + 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'], + 'M' => ['М', 'Μ', 'M'], + 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'], + 'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', + 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', + 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', + 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'], + 'P' => ['П', 'Π', 'P'], + 'Q' => ['Q'], + 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'], + 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'], + 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'], + 'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', + 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', + 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'], + 'V' => ['В', 'V'], + 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'], + 'X' => ['Χ', 'Ξ', 'X'], + 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', + 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'], + 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'], + 'AE' => ['Æ', 'Ǽ'], + 'Ch' => ['Ч'], + 'Dj' => ['Ђ'], + 'Dz' => ['Џ'], + 'Gx' => ['Ĝ'], + 'Hx' => ['Ĥ'], + 'Ij' => ['IJ'], + 'Jx' => ['Ĵ'], + 'Kh' => ['Х'], + 'Lj' => ['Љ'], + 'Nj' => ['Њ'], + 'Oe' => ['Œ'], + 'Ps' => ['Ψ'], + 'Sh' => ['Ш'], + 'Shch' => ['Щ'], + 'Ss' => ['ẞ'], + 'Th' => ['Þ'], + 'Ts' => ['Ц'], + 'Ya' => ['Я'], + 'Yu' => ['Ю'], + 'Zh' => ['Ж'], + ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", + "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", + "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", + "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", + "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", + "\xEF\xBE\xA0"], + ]; + + public function __construct($str) { $this->__str_buffer = $str; } + public function __toString(): string { return $this->__str_buffer; } + public function getString(): string { return $this->__str_buffer; } - public function getString(): string - { - return $this->__str_buffer; - } - - /** - * Returns the substring beginning at $start with the specified $length. - * It differs from the mb_substr() function in that providing a $length of 0 - * will return the rest of the string, rather than an empty string. - * - * @param int $start Position of the first character to use - * @param int $length Maximum number of characters used - * @return Str - */ public function substr(int $start = 0, int $length = 0): Str { - if ($length === 0) { - $length = \mb_strlen($this->__str_buffer); - } - - $this->__str_buffer = \mb_substr($this->__str_buffer, $start, $length); - + $this->__str_buffer = \mb_substr($this->__str_buffer, $start, $length !== 0 ? $length : \mb_strlen($this->__str_buffer)); return $this; } - /** - * Check if the string has $prefix at the start - * - * @param string $prefix - * @return bool - */ public function hasPrefix(string $prefix): bool { - if ($this->__str_buffer === '' || $prefix === '') { return false; } - + if ('' === $this->__str_buffer || '' === $prefix) { return false; } return (0 === \mb_strpos($this->__str_buffer, $prefix)); } - /** - * Check if the string has $suffix at the end - * - * @param string $suffix - * @return bool - */ public function hasSuffix(string $suffix): bool { - if ($this->__str_buffer === '' || $suffix === '') { return false; } - + if ('' === $this->__str_buffer || '' === $suffix) { return false; } return \mb_substr($this->__str_buffer, -\mb_strlen($suffix)) === $suffix; } - /** - * Check whether $prefix exists in the string, and - * prepend $prefix to the string if it doesn't - * - * @param string $check - * @return Str - */ public function ensureLeft(string $check): Str { - if ($this->hasPrefix($check)) { return $this; } - - $this->prepend($check); - + if ('' !== $check && 0 === \mb_strpos($this->__str_buffer, $check)) { return $this; } + $this->__str_buffer = $check . $this->__str_buffer; return $this; } - /** - * Check whether $suffix exists in the string, and - * append $suffix to the string if it doesn't - * - * @param string $check - * @return Str - */ public function ensureRight(string $check): Str { - if ($this->hasSuffix($check)) { return $this; } - - $this->append($check); - + if ('' !== $check && \mb_substr($this->__str_buffer, -\mb_strlen($check)) === $check) { return $this; } + $this->__str_buffer .= $check; return $this; } - /** - * Check if $haystack contains $needle substring - * - * @param string $needle - * @param bool $caseSensitive - * - * @return bool - */ public function contains(string $needle, bool $caseSensitive = true): bool { if ($this->__str_buffer === '' || $needle === '') { return false; } + return $caseSensitive ? (false !== \mb_strpos($this->__str_buffer, $needle)) : (false !== \mb_stripos($this->__str_buffer, $needle)); + } - if ($caseSensitive) { - return false !== \mb_strpos($this->__str_buffer, $needle); - } + public function replace(string $old, string $new): Str + { + $this->__str_buffer = \mb_ereg_replace(\preg_quote($old, '/'), $new, $this->__str_buffer); + return $this; + } - return (false !== \mb_stripos($this->__str_buffer, $needle)); - } - - /** - * Replace returns a copy of the string s with the first - * n non-overlapping instances of old replaced by new. - * If old is empty, it matches at the beginning of the string and - * after each UTF-8 sequence, yielding up to k+1 replacements - * for a k-rune string. If n < 0, - * there is no limit on the number of replacements. - * - * @param string $old - * @param string $new - * @param int $limit - * - * @return Str - */ - public function replace(string $old, string $new, int $limit = -1): Str + public function replaceWithLimit(string $old, string $new, int $limit = -1): Str { if ($old === $new || $limit === 0) { return $this; } - - $oldCount = $this->countSubstr($old); - + $strLower = \mb_strtolower($this->__str_buffer); + $oldLower = \mb_strtolower($old); + $oldCount = \mb_substr_count($strLower, $oldLower); if ($oldCount === 0) { return $this; } - - if ($limit < 0 || $oldCount < $limit) { - $limit = $oldCount; - } - - $result = $this->__str_buffer; + if ($limit < 0 || $oldCount < $limit) { $limit = $oldCount; } $offset = 0; - - while (--$limit >= 0) { - $pos = \mb_strpos($result, $old, $offset); + while ($limit--) { + $pos = \mb_strpos($this->__str_buffer, $old, $offset); $offset = $pos + \mb_strlen($old); - $result = \mb_substr($result, 0, $pos) . $new . \mb_substr($result, $offset); + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $pos) . $new . \mb_substr($this->__str_buffer, $offset); } - - $this->__str_buffer = $result; return $this; } - /** - * Make a string lowercase - * - * @return Str - */ public function toLowerCase(): Str { $this->__str_buffer = \mb_strtolower($this->__str_buffer); - return $this; } - /** - * Make a string uppercase - * - * @return Str - */ public function toUpperCase(): Str { $this->__str_buffer = \mb_strtoupper($this->__str_buffer); - return $this; } - /** - * Returns a string with whitespace removed from the start and end of the - * string. Supports the removal of unicode whitespace. Accepts an optional - * string of characters to strip instead of the defaults. - * - * @param string $chars - * @return Str - */ public function trim(string $chars = ''): Str { - $chars = $chars ? \preg_quote($chars, '/') : '\s'; + $chars = '' === $chars ? '\s' : \preg_quote($chars, '/'); $this->__str_buffer = \mb_ereg_replace("^[$chars]+|[$chars]+\$", '', $this->__str_buffer); - return $this; } - /** - * Returns a string with whitespace removed from the start of the string. - * Supports the removal of unicode whitespace. Accepts an optional - * string of characters to strip instead of the defaults. - * - * @param string $chars Optional string of characters to strip - * @return Str - */ public function trimLeft(string $chars = ''): Str { - $chars = $chars ? \preg_quote($chars, '/') : '\s'; + $chars = '' === $chars ? '\s' : \preg_quote($chars, '/'); $this->__str_buffer = \mb_ereg_replace("^[$chars]+", '', $this->__str_buffer); - return $this; } - /** - * Returns a string with whitespace removed from the end of the string. - * Supports the removal of unicode whitespace. Accepts an optional - * string of characters to strip instead of the defaults. - * - * @param string $chars Optional string of characters to strip - * @return Str - */ public function trimRight(string $chars = ''): Str { - $chars = $chars ? \preg_quote($chars, '/') : '\s'; + $chars = '' === $chars ? '\s' : \preg_quote($chars, '/'); $this->__str_buffer = \mb_ereg_replace("[$chars]+\$", '', $this->__str_buffer); - return $this; } - /** - * Append $sub to the string - * - * @param string $sub - * @return Str - */ public function append(string $sub): Str { $this->__str_buffer .= $sub; - return $this; } - /** - * Prepend $sub to the string - * - * @param string $sub - * @return Str - */ public function prepend(string $sub): Str { $this->__str_buffer = $sub . $this->__str_buffer; - return $this; } - /** - * Returns the character at $pos, with indexes starting at 0. - * - * @param int $pos - * @return Str - */ public function at(int $pos): Str { - $this->substr($pos, 1); - + $this->__str_buffer = \mb_substr($this->__str_buffer, $pos, 1); return $this; } - /** - * Returns an array consisting of the characters in the string. - * - * @return array An array of string chars - */ public function chars(): array { if ($this->__str_buffer === '') { return []; } - $chars = []; - - for ($i = 0, $iMax = $this->length(); $i < $iMax; $i++) { - $tmp = $this->__str_buffer; - $chars[] = (string)$this->at($i); - $this->__str_buffer = $tmp; - } - + for ($i = 0, $iMax = \mb_strlen($this->__str_buffer); $i < $iMax; $i++) { $chars[] = \mb_substr($this->__str_buffer, $i, 1); } return $chars; } - /** - * Return string length - * - * @return int - */ public function length(): int { return \mb_strlen($this->__str_buffer); } - /** - * Returns the first $length characters of the string. - * - * @param int $length Number of characters to retrieve from the start - * @return Str - */ public function first(int $length = 1): Str { - if ($length <= 0) { - $this->__str_buffer = ''; - return $this; - } - - $this->substr(0, $length); - + if ($length <= 0) { $this->__str_buffer = ''; return $this; } + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $length); return $this; } - /** - * Returns the last $length characters of the string. - * - * @param int $length Number of characters to retrieve from the end - * @return Str - */ public function last(int $length = 1): Str { - if ($length <= 0) { - $this->__str_buffer = ''; - return $this; - } - - $this->substr(-$length); - + if ($length <= 0) { $this->__str_buffer = ''; return $this; } + $this->__str_buffer = \mb_substr($this->__str_buffer, -$length); return $this; } - /** - * Returns the index of the first occurrence of $needle in the string, - * and false if not found. Accepts an optional offset from which to begin - * the search. - * - * @param string $needle Substring to look for - * @param int $offset Offset from which to search - * @return int The occurrence's index if found, otherwise -1 - */ public function indexOf(string $needle, int $offset = 0): int { if ($needle === '' || $this->__str_buffer === '') { return -1; } - - $maxLen = \mb_strlen($this->__str_buffer); - - if ($offset < 0) { - $offset = $maxLen - (int)abs($offset); - } - - if ($offset > $maxLen) { return -1; } - - if ($offset < 0) { return -1; } - $pos = \mb_strpos($this->__str_buffer, $needle, $offset); - return false === $pos ? -1 : $pos; } - /** - * Returns the index of the last occurrence of $needle in the string, - * and false if not found. Accepts an optional offset from which to begin - * the search. Offsets may be negative to count from the last character - * in the string. - * - * @param string $needle Substring to look for - * @param int $offset Offset from which to search - * @return int The last occurrence's index if found, otherwise -1 - */ public function indexOfLast(string $needle, int $offset = 0): int { if ($needle === '' || $this->__str_buffer === '') { return -1; } - $maxLen = \mb_strlen($this->__str_buffer); - - if ($offset < 0) { - $offset = $maxLen - (int)abs($offset); - } - - if ($offset > $maxLen) { return -1; } - - if ($offset < 0) { return -1; } - + if ($offset < 0) { $offset = $maxLen - (int)abs($offset); } + if ($offset > $maxLen || $offset < 0) { return -1; } $pos = \mb_strrpos($this->__str_buffer, $needle, $offset); - return false === $pos ? -1 : $pos; } - /** - * Returns the number of occurrences of $substring in the given string. - * By default, the comparison is case-sensitive, but can be made insensitive - * by setting $caseSensitive to false. - * - * @param string $needle The substring to search for - * @param bool $caseSensitive Whether or not to enforce case-sensitivity - * @return int The number of $substring occurrences - */ public function countSubstr(string $needle, bool $caseSensitive = true): int { - if ($caseSensitive) { - return \mb_substr_count($this->__str_buffer, $needle); - } - - $tmp = $this->__str_buffer; - $this->__str_buffer = \mb_strtoupper($this->__str_buffer); - $needle = \mb_strtoupper($needle); - $result = \mb_substr_count($this->__str_buffer, $needle); - - $this->__str_buffer = $tmp; - - return $result; + if ($caseSensitive) { return \mb_substr_count($this->__str_buffer, $needle); } + return \mb_substr_count(\mb_strtolower($this->__str_buffer), \mb_strtolower($needle)); } - /** - * Returns true if the string contains all $needles, false otherwise. By - * default the comparison is case-sensitive, but can be made insensitive by - * setting $caseSensitive to false. - * - * @param array $needles Substrings to look for - * @param bool $caseSensitive Whether or not to enforce case-sensitivity - * @return bool Whether or not $str contains $needle - */ public function containsAll(array $needles, bool $caseSensitive = true): bool { - if (empty($needles)) { return false; } - - foreach ($needles as $needle) { - if (!$this->contains($needle, $caseSensitive)) { return false; } - } - + if ([] === $needles) { return false; } + foreach ($needles as $needle) { if (!$this->contains($needle, $caseSensitive)) { return false; } } return true; } - /** - * Returns true if the string contains any $needles, false otherwise. By - * default the comparison is case-sensitive, but can be made insensitive by - * setting $caseSensitive to false. - * - * @param array $needles Substrings to look for - * @param bool $caseSensitive Whether or not to enforce case-sensitivity - * @return bool Whether or not $str contains $needle - */ public function containsAny(array $needles, bool $caseSensitive = true): bool { - if (empty($needles)) { return false; } - - foreach ($needles as $needle) { - if ($this->contains($needle, $caseSensitive)) { return true; } - } - + foreach ($needles as $needle) { if ($this->contains($needle, $caseSensitive)) { return true; } } return false; } - /** - * Returns true if the string begins with $substring, false otherwise. By - * default, the comparison is case-sensitive, but can be made insensitive - * by setting $caseSensitive to false. - * - * @todo refactor - * - * @param string $substring The substring to look for - * @param bool $caseSensitive Whether or not to enforce case-sensitivity - * @return bool Whether or not $str starts with $substring - */ public function startsWith(string $substring, bool $caseSensitive = true): bool { - if ($caseSensitive) { - return $this->hasPrefix($substring); - } - - // need a tmp var to avoid mutating the original string. - // i know how ugly this looks. - $tmp = $this->__str_buffer; - $this->__str_buffer = \mb_strtoupper($this->__str_buffer); - - $result = $this->hasPrefix(\mb_strtoupper($substring)); - - $this->__str_buffer = $tmp; - - return $result; + if ($caseSensitive) { return 0 === \mb_strpos($this->__str_buffer, $substring); } + return 0 === \mb_stripos($this->__str_buffer, $substring); } - /** - * Returns true if the string begins with any of $substrings, false - * otherwise. By default the comparison is case-sensitive, but can be made - * insensitive by setting $caseSensitive to false. - * - * @param array $substrings Substrings to look for - * @param bool $caseSensitive Whether or not to enforce case-sensitivity - * @return bool Whether or not $str starts with $substring - */ public function startsWithAny(array $substrings, bool $caseSensitive = true): bool { - if (empty($substrings)) { return false; } - - foreach ($substrings as $substring) { - if ($this->startsWith($substring, $caseSensitive)) { return true; } - } - + foreach ($substrings as $substring) { if ($this->startsWith($substring, $caseSensitive)) { return true; } } return false; } - /** - * Returns true if the string ends with $substring, false otherwise. By - * default, the comparison is case-sensitive, but can be made insensitive - * by setting $caseSensitive to false. - * - * @todo refactor - * @todo NEED UNIT TEST WITH safeWrap - * - * @param string $substring The substring to look for - * @param bool $caseSensitive Whether or not to enforce case-sensitivity - * @return bool Whether or not $str ends with $substring - */ public function endsWith(string $substring, bool $caseSensitive = true): bool { - if ($caseSensitive) { - return $this->hasSuffix($substring); - } + if ($caseSensitive) { return \mb_substr($this->__str_buffer, -\mb_strlen($substring)) === $substring; } + return -1 !== \mb_strripos($this->__str_buffer, $substring); + } - return $this->safeWrap(function () use ($substring) { - $this->__str_buffer = \mb_strtoupper($this->__str_buffer); - return $this->hasSuffix(\mb_strtoupper($substring)); - }); - -// $this->__str_buffer = \mb_strtoupper($this->__str_buffer); -// return $this->hasSuffix($substring); -// -// // need a tmp var to avoid mutating the original string. -// // i know how ugly this looks. -// $tmp = $this->__str_buffer; -// $this->__str_buffer = \mb_strtoupper($this->__str_buffer); -// -// $result = $this->hasSuffix(\mb_strtoupper($substring)); -// -// $this->__str_buffer = $tmp; -// -// return $result; - } - - private function safeWrap(callable $f) { - $tmp = $this->__str_buffer; - $r = $f(); - $this->__str_buffer = $tmp; - return $r; - } - - /** - * Returns true if the string ends with any of $substrings, false otherwise. - * By default, the comparison is case-sensitive, but can be made insensitive - * by setting $caseSensitive to false. - * - * @param array $substrings Substrings to look for - * @param bool $caseSensitive Whether or not to enforce case-sensitivity - * @return bool Whether or not $str ends with $substring - */ public function endsWithAny(array $substrings, bool $caseSensitive = true): bool { - if (empty($substrings)) { return false; } - - foreach ($substrings as $substring) { - if ($this->endsWith($substring, $caseSensitive)) { return true; } - } - + foreach ($substrings as $substring) { if ($this->endsWith($substring, $caseSensitive)) { return true; } } return false; } - /** - * Returns a new string of a given length such that both sides of the - * string are padded. - * - * @param int $length Desired string length after padding - * @param string $padStr String used to pad, defaults to space - * @return Str - */ public function padBoth(int $length, string $padStr = ' '): Str { $padding = $length - \mb_strlen($this->__str_buffer); $this->applyPadding((int)floor($padding / 2), (int)ceil($padding / 2), $padStr); - return $this; } - /** - * Adds the specified amount of left and right padding to the given string. - * The default character used is a space. - * - * @param int $left Length of left padding - * @param int $right Length of right padding - * @param string $padStr String used to pad - * @return Str - * - * @internal - */ private function applyPadding(int $left = 0, int $right = 0, string $padStr = ' '): Str { - if ($right + $left <= 0) { return $this; } - if ('' === $padStr) { return $this;} - - if (1 === \mb_strlen($padStr)) { - $this->__str_buffer = str_repeat($padStr, $left) . $this->__str_buffer . str_repeat($padStr, $right); - return $this; - } - + if ('' === $padStr || $right + $left <= 0) { return $this; } $leftPadding = \mb_substr(str_repeat($padStr, $left), 0, $left); $rightPadding = \mb_substr(str_repeat($padStr, $right), 0, $right); - $this->__str_buffer = $leftPadding . $this->__str_buffer . $rightPadding; return $this; } - /** - * Returns a new string of a given length such that the beginning of the - * string is padded. - * - * @param int $length Desired string length after padding - * @param string $padStr String used to pad, defaults to space - * @return Str - */ public function padLeft(int $length, string $padStr = ' '): Str { $this->applyPadding($length - \mb_strlen($this->__str_buffer), 0, $padStr); - return $this; } - /** - * Returns a new string of a given length such that the end of the string - * is padded. - * - * @param int $length Desired string length after padding - * @param string $padStr String used to pad, defaults to space - * @return Str - */ public function padRight(int $length, string $padStr = ' '): Str { $this->applyPadding(0, $length - \mb_strlen($this->__str_buffer), $padStr); - return $this; } - /** - * Inserts $substring into the string at the $index provided. - * - * @param string $substring String to be inserted - * @param int $index The index at which to insert the substring - * @return Str - */ public function insert(string $substring, int $index): Str { - if (0 === $index) { - $this->__str_buffer = $substring . $this->__str_buffer; - return $this; - } - - if ($substring === '') { return $this; } - $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $index) . $substring . \mb_substr($this->__str_buffer, $index); - return $this; } - /** - * Returns a new string with the prefix $substring removed, if present. - * - * @param string $substring The prefix to remove - * @return Str - */ public function removeLeft(string $substring): Str { - if ('' !== $substring && $this->hasPrefix($substring)) { - $this->__str_buffer = \mb_substr($this->__str_buffer, \mb_strlen($substring)); - } - + if ('' !== $substring && 0 === \mb_strpos($this->__str_buffer, $substring)) { $this->__str_buffer = \mb_substr($this->__str_buffer, \mb_strlen($substring)); } return $this; } - /** - * Returns a new string with the suffix $substring removed, if present. - * - * @param string $substring The suffix to remove - * @return Str - */ public function removeRight(string $substring): Str { - if ('' !== $substring && $this->hasSuffix($substring)) { - $this->__str_buffer = \mb_substr($this->__str_buffer, 0, \mb_strlen($this->__str_buffer) - \mb_strlen($substring)); - } - + if ('' !== $substring && \mb_substr($this->__str_buffer, -\mb_strlen($substring)) === $substring) { $this->__str_buffer = \mb_substr($this->__str_buffer, 0, \mb_strlen($this->__str_buffer) - \mb_strlen($substring)); } return $this; } - /** - * Returns a repeated string given a multiplier. An alias for str_repeat. - * - * @param int $multiplier The number of times to repeat the string - * @return Str - */ public function repeat(int $multiplier): Str { $this->__str_buffer = \str_repeat($this->__str_buffer, $multiplier); - return $this; } - /** - * Returns a reversed string. A multi-byte version of strrev(). - * - * @return Str - */ public function reverse(): Str { - if ('' === $this->__str_buffer) { return $this; } - $reversed = ''; - - // Loop from last index of string to first $i = \mb_strlen($this->__str_buffer); - - while ($i--) { - $reversed .= \mb_substr($this->__str_buffer, $i, 1); - } - + while ($i--) { $reversed .= \mb_substr($this->__str_buffer, $i, 1); } $this->__str_buffer = $reversed; - return $this; } - /** - * A multi-byte str_shuffle() function. It returns a string with its - * characters in random order. - * - * @return Str - */ public function shuffle(): Str { $indexes = \range(0, \mb_strlen($this->__str_buffer) - 1); \shuffle($indexes); - $shuffledStr = ''; - - foreach ($indexes as $i) { - $shuffledStr .= \mb_substr($this->__str_buffer, $i, 1); - } - + foreach ($indexes as $i) { $shuffledStr .= \mb_substr($this->__str_buffer, $i, 1); } $this->__str_buffer = $shuffledStr; - return $this; } - /** - * Returns the substring between $start and $end, if found, or an empty - * string. An optional offset may be supplied from which to begin the - * search for the start string. - * - * @param string $start Delimiter marking the start of the substring - * @param string $end Delimiter marking the end of the substring - * @param int $offset Index from which to begin the search - * @return Str - */ public function between(string $start, string $end, int $offset = 0): Str { - $startIndex = $this->indexOf($start, $offset); - - if ($startIndex === -1) { - $this->__str_buffer = ''; - return $this; - } - - $substrIndex = $startIndex + \mb_strlen($start); - $endIndex = $this->indexOf($end, $substrIndex); - - if ($endIndex === -1 || $endIndex === $substrIndex) { - $this->__str_buffer = ''; - return $this; - } - - $this->substr($substrIndex, $endIndex - $substrIndex); - + $posStart = \mb_strpos($this->__str_buffer, $start, $offset); + if ($posStart === false) { $this->__str_buffer = ''; return $this; } + $substrIndex = $posStart + \mb_strlen($start); + $posEnd = \mb_strpos($this->__str_buffer, $end, $substrIndex); + if ($posEnd === false || $posEnd === $substrIndex) { $this->__str_buffer = ''; return $this; } + $this->__str_buffer = \mb_substr($this->__str_buffer, $substrIndex, $posEnd - $substrIndex); return $this; } - /** - * Returns a camelCase version of the string. Trims surrounding spaces, - * capitalizes letters following digits, spaces, dashes and underscores, - * and removes spaces, dashes, as well as underscores. - * - * @return Str - */ public function camelize(): Str { - $this - ->trim() - ->lowerCaseFirst(); - + $this->__str_buffer = \mb_ereg_replace("^['\s']+|['\s']+\$", '', $this->__str_buffer); + $this->__str_buffer = \mb_strtolower(\mb_substr($this->__str_buffer, 0, 1)) . \mb_substr($this->__str_buffer, 1); $this->__str_buffer = preg_replace('/^[-_]+/', '', $this->__str_buffer); - - $this->__str_buffer = preg_replace_callback('/[-_\s]+(.)?/u', function ($match) - { - if (isset($match[1])) { return \mb_strtoupper($match[1]); } - - return ''; - }, $this->__str_buffer); - - $this->__str_buffer = preg_replace_callback('/[\d]+(.)?/u', function ($match) { - return \mb_strtoupper($match[0]); - }, $this->__str_buffer); - + $this->__str_buffer = preg_replace_callback('/[-_\s]+(.)?/u', function ($match) { if (isset($match[1])) { return \mb_strtoupper($match[1]); } return ''; }, $this->__str_buffer); + $this->__str_buffer = preg_replace_callback('/[\d]+(.)?/u', function ($match) { return \mb_strtoupper($match[0]); }, $this->__str_buffer); return $this; } - /** - * Converts the first character of the string to lower case. - * - * @return Str - */ public function lowerCaseFirst(): Str { - if ('' === $this->__str_buffer) { return $this; } - - $first = \mb_substr($this->__str_buffer, 0, 1); - $rest = \mb_substr($this->__str_buffer, 1); - $this->__str_buffer = \mb_strtolower($first) . $rest; - + $this->__str_buffer = \mb_strtolower(\mb_substr($this->__str_buffer, 0, 1)) . \mb_substr($this->__str_buffer, 1); return $this; } - /** - * Converts the first character of the supplied string to upper case. - * - * @return Str - */ public function upperCaseFirst(): Str { - if ('' === $this->__str_buffer) { return $this; } - - $first = \mb_substr($this->__str_buffer, 0, 1); - $rest = \mb_substr($this->__str_buffer, 1); - $this->__str_buffer = \mb_strtoupper($first) . $rest; - + $this->__str_buffer = \mb_strtoupper(\mb_substr($this->__str_buffer, 0, 1)) . \mb_substr($this->__str_buffer, 1); return $this; } - /** - * Trims the string and replaces consecutive whitespace characters with a - * single space. This includes tabs and newline characters, as well as - * multi-byte whitespace such as the thin space and ideographic space. - * - * @return Str - */ public function collapseWhitespace(): Str { - $this - ->regexReplace('[[:space:]]+', ' ') - ->trim(); - + $this->__str_buffer = \mb_ereg_replace('[[:space:]]+', ' ', $this->__str_buffer); + $this->__str_buffer = \mb_ereg_replace("^['\s']+|['\s']+\$", '', $this->__str_buffer); return $this; } - /** - * Replaces all occurrences of $pattern in $str by $replacement. An alias - * for mb_ereg_replace(). Note that the 'i' option with multi-byte patterns - * in mb_ereg_replace() requires PHP 5.6+ for correct results. This is due - * to a lack of support in the bundled version of Oniguruma in PHP < 5.6, - * and current versions of HHVM (3.8 and below). - * - * @param string $pattern The regular expression pattern - * @param string $replacement The string to replace with - * @param string $options Matching conditions to be used - * @return Str - */ public function regexReplace(string $pattern, string $replacement, string $options = 'msr'): Str { $this->__str_buffer = \mb_ereg_replace($pattern, $replacement, $this->__str_buffer, $options); - return $this; } - /** - * Returns a lowercase and trimmed string separated by dashes. Dashes are - * inserted before uppercase characters (with the exception of the first - * character of the string), and in place of spaces as well as underscores. - * - * @return Str - */ public function dasherize(): Str { - $this->delimit('-'); - + $this->__str_buffer = \mb_ereg_replace("^['\s']+|['\s']+\$", '', $this->__str_buffer); + $this->__str_buffer = \mb_strtolower(\mb_ereg_replace('\B([A-Z])', '-\1', $this->__str_buffer)); + $this->__str_buffer = \mb_ereg_replace('[-_\s]+', '-', $this->__str_buffer); return $this; } - /** - * Returns a lowercase and trimmed string separated by the given delimiter. - * Delimiters are inserted before uppercase characters (with the exception - * of the first character of the string), and in place of spaces, dashes, - * and underscores. Alpha delimiters are not converted to lowercase. - * - * @param string $delimiter Sequence used to separate parts of the string - * @return Str - */ public function delimit($delimiter): Str { - $this->trim() - ->regexReplace('\B([A-Z])', '-\1') - ->toLowerCase() - ->regexReplace('[-_\s]+', $delimiter); - + $this->__str_buffer = \mb_ereg_replace("^['\s']+|['\s']+\$", '', $this->__str_buffer); + $this->__str_buffer = \mb_strtolower(\mb_ereg_replace('\B([A-Z])', '-\1', $this->__str_buffer)); + $this->__str_buffer = \mb_ereg_replace('[-_\s]+', $delimiter, $this->__str_buffer); return $this; } - /** - * Checks if the given string is a valid UUID v.4. - * It doesn't matter whether the given UUID has dashes. - * - * @return bool - */ public function isUUIDv4(): bool { - $l = '[a-f0-9]'; - $pattern = "/^{$l}{8}-?{$l}{4}-?4{$l}{3}-?[89ab]{$l}{3}-?{$l}{12}\Z/"; - - return (bool)\preg_match($pattern, $this->__str_buffer); + return (bool)\preg_match("/^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}\Z/", $this->__str_buffer); } - /** - * Returns true if the string contains a lower case char, false otherwise. - * - * @return bool Whether or not the string contains a lower case character. - */ public function hasLowerCase(): bool { - return $this->matchesPattern('.*[[:lower:]]'); + return \mb_ereg_match('.*[[:lower:]]', $this->__str_buffer); } - /** - * Returns true if the string contains an upper case char, false otherwise. - * - * @return bool Whether or not the string contains an upper case character. - */ public function hasUpperCase(): bool { - return $this->matchesPattern('.*[[:upper:]]'); + return \mb_ereg_match('.*[[:upper:]]', $this->__str_buffer); } - /** - * Returns true if $str matches the supplied pattern, false otherwise. - * - * @param string $pattern Regex pattern to match against - * @return bool Whether or not $str matches the pattern - */ public function matchesPattern(string $pattern): bool { return \mb_ereg_match($pattern, $this->__str_buffer); } - /** - * Convert all HTML entities to their applicable characters. An alias of - * html_entity_decode. For a list of flags, refer to - * http://php.net/manual/en/function.html-entity-decode.php - * - * @param int $flags Optional flags - * @return Str - */ public function htmlDecode(int $flags = ENT_COMPAT): Str { $this->__str_buffer = \html_entity_decode($this->__str_buffer, $flags); - return $this; } - /** - * Convert all applicable characters to HTML entities. An alias of - * htmlentities. Refer to http://php.net/manual/en/function.htmlentities.php - * for a list of flags. - * - * @param int $flags Optional flags - * @return Str - */ public function htmlEncode(int $flags = ENT_COMPAT): Str { $this->__str_buffer = \htmlentities($this->__str_buffer, $flags); - return $this; } - /** - * Capitalizes the first word of the string, replaces underscores with - * spaces, and strips '_id'. - * - * @return Str - */ public function humanize(): Str { - $this->__str_buffer = \str_replace(['_id', '_'], ['', ' '], $this->__str_buffer); - $this - ->trim() - ->upperCaseFirst(); - + $this->__str_buffer = \str_replace('_', ' ', $this->__str_buffer); + $this->__str_buffer = \mb_ereg_replace("^['\s']+|['\s']+\$", '', $this->__str_buffer); + $this->__str_buffer = \mb_strtoupper(\mb_substr($this->__str_buffer, 0, 1)) . \mb_substr($this->__str_buffer, 1); return $this; } - /** - * Returns true if the string contains only alphabetic chars, false otherwise. - * - * @return bool Whether or not $str contains only alphabetic chars - */ public function isAlpha(): bool { - return $this->matchesPattern('^[[:alpha:]]*$'); + return \mb_ereg_match('^[[:alpha:]]*$', $this->__str_buffer); } - /** - * Returns true if the string contains only alphabetic and numeric - * chars, false otherwise. - * - * @return bool Whether or not $str contains only alphanumeric chars - */ public function isAlphanumeric(): bool { - return $this->matchesPattern('^[[:alnum:]]*$'); + return \mb_ereg_match('^[[:alnum:]]*$', $this->__str_buffer); } - /** - * Returns true if the string is base64 encoded, false otherwise. - * - * @return bool Whether or not $str is base64 encoded - */ public function isBase64(): bool { return (base64_encode(base64_decode($this->__str_buffer)) === $this->__str_buffer); } - /** - * Returns true if the string contains only whitespace chars, false otherwise. - * - * @return bool Whether or not $str contains only whitespace characters - */ public function isBlank(): bool { - return $this->matchesPattern('^[[:space:]]*$'); + return \mb_ereg_match('^[[:space:]]*$', $this->__str_buffer); } - /** - * Returns true if the string contains only hexadecimal chars, false otherwise. - * - * @return bool Whether or not $str contains only hexadecimal chars - */ public function isHexadecimal(): bool { - return $this->matchesPattern('^[[:xdigit:]]*$'); + return \mb_ereg_match('^[[:xdigit:]]*$', $this->__str_buffer); } - /** - * Returns true if the string is JSON, false otherwise. Unlike json_decode - * in PHP 5.x, this method is consistent with PHP 7 and other JSON parsers, - * in that an empty string is not considered valid JSON. - * - * @return bool Whether or not $str is JSON - */ public function isJson(): bool { - if ('' === $this->__str_buffer) { return false; } - json_decode($this->__str_buffer); - return json_last_error() === JSON_ERROR_NONE; } - /** - * Returns true if the string contains only lower case chars, false otherwise. - * - * @return bool Whether or not $str contains only lower case characters - */ public function isLowerCase(): bool { - return $this->matchesPattern('^[[:lower:]]*$'); + return \mb_ereg_match('^[[:lower:]]*$', $this->__str_buffer); } - /** - * Returns true if the string is serialized, false otherwise. - * - * @return bool Whether or not $str is serialized - */ public function isSerialized(): bool { return ($this->__str_buffer === 'b:0;') || (@unserialize($this->__str_buffer, []) !== false); } - /** - * Returns true if the string contains only lower case chars, false otherwise. - * - * @return bool Whether or not $str contains only lower case characters - */ public function isUpperCase(): bool { - return $this->matchesPattern('^[[:upper:]]*$'); + return \mb_ereg_match('^[[:upper:]]*$', $this->__str_buffer); } - /** - * Splits on newlines and carriage returns, returning an array of strings corresponding to the lines in the string. - * - * We don't check for ascii here. - * - * @return array of strings - */ public function lines(): array { - return $this->split('[\r\n]{1,2}'); + if ('' === $this->__str_buffer) { return []; } + return \mb_split('[\r\n]{1,2}', $this->__str_buffer); } - /** - * Splits the string with the provided regular expression, returning an - * array of strings. An optional integer $limit will truncate the - * results. - * - * @param string $pattern The regex with which to split the string - * @param int $limit Optional maximum number of results to return - * @return array of strings - */ public function split(string $pattern, int $limit = -1): array { - if (0 === $limit || '' === $this->__str_buffer) { return []; } + if ($limit === 0) { return []; } if ($pattern === '') { return [$this->__str_buffer]; } - - // mb_split returns the remaining unsplit string in the last index when - // supplying a limit - $limit = ($limit > 0) ? $limit + 1 : -1; - - $array = \mb_split($pattern, $this->__str_buffer, $limit); - - if ($limit > 0 && \count($array) === $limit) { array_pop($array); } - - $result = []; - foreach ($array as $string) { - $result[] = $string; - } - - return $result; + if ($limit >= 0) { return array_filter(\mb_split($pattern, $this->__str_buffer), function () use (&$limit) { return --$limit >= 0; }); } + return \mb_split($pattern, $this->__str_buffer); } - /** - * Returns the longest common prefix between the string and $otherStr. - * - * @param string $otherStr Second string for comparison - * @return Str - */ public function longestCommonPrefix(string $otherStr): Str { $maxLength = min(\mb_strlen($this->__str_buffer), \mb_strlen($otherStr)); - $longestCommonPrefix = ''; - for ($i = 0; $i < $maxLength; $i++) { $char = \mb_substr($this->__str_buffer, $i, 1); - - if ($char === \mb_substr($otherStr, $i, 1)) { - $longestCommonPrefix .= $char; - } else { break; } + if ($char === \mb_substr($otherStr, $i, 1)) { $longestCommonPrefix .= $char; } else { break; } } - $this->__str_buffer = $longestCommonPrefix; - return $this; } - /** - * Returns the longest common suffix between the string and $otherStr. - * - * @param string $otherStr Second string for comparison - * @return Str - */ public function longestCommonSuffix(string $otherStr): Str { $maxLength = min(\mb_strlen($this->__str_buffer), \mb_strlen($otherStr)); - $longestCommonSuffix = ''; - for ($i = 1; $i <= $maxLength; $i++) { $char = \mb_substr($this->__str_buffer, -$i, 1); - - if ($char === \mb_substr($otherStr, -$i, 1)) { - $longestCommonSuffix = $char . $longestCommonSuffix; - } else { break; } + if ($char === \mb_substr($otherStr, -$i, 1)) { $longestCommonSuffix = $char . $longestCommonSuffix; } else { break; } } - $this->__str_buffer = $longestCommonSuffix; - return $this; } - /** - * Returns the longest common substring between the string and $otherStr. - * In the case of ties, it returns that which occurs first. - * - * @param string $otherStr Second string for comparison - * @return Str - */ public function longestCommonSubstring(string $otherStr): Str { - // Uses dynamic programming to solve - // http://en.wikipedia.org/wiki/Longest_common_substring_problem $strLength = \mb_strlen($this->__str_buffer); $otherLength = \mb_strlen($otherStr); - - // Return if either string is empty - if ($strLength === 0 || $otherLength === 0) { - $this->__str_buffer = ''; - return $this; - } - $len = 0; $end = 0; - $table = array_fill(0, $strLength + 1, - array_fill(0, $otherLength + 1, 0)); - + $table = \array_fill(0, $strLength, \array_fill(0, $otherLength, 0)); for ($i = 1; $i <= $strLength; $i++) { for ($j = 1; $j <= $otherLength; $j++) { $strChar = \mb_substr($this->__str_buffer, $i - 1, 1); $otherChar = \mb_substr($otherStr, $j - 1, 1); - if ($strChar === $otherChar) { $table[$i][$j] = $table[$i - 1][$j - 1] + 1; - if ($table[$i][$j] > $len) { - $len = $table[$i][$j]; - $end = $i; - } - } else { - $table[$i][$j] = 0; - } + if ($table[$i][$j] > $len) { $len = $table[$i][$j]; $end = $i; } } else { $table[$i][$j] = 0; } } } - $this->__str_buffer = \mb_substr($this->__str_buffer, $end - $len, $len); - return $this; } - /** - * Truncates the string to a given length, while ensuring that it does not - * split words. If $substring is provided, and truncating occurs, the - * string is further truncated so that the substring may be appended without - * exceeding the desired length. - * - * @param int $length Desired length of the truncated string - * @param string $substring The substring to append if it can fit - * @return Str - */ public function safeTruncate(int $length, string $substring = ''): Str { if ($length >= \mb_strlen($this->__str_buffer)) { return $this; } - - // Need to further trim the string so we can append the substring - $substringLength = \mb_strlen($substring); - $length -= $substringLength; - + $length -= \mb_strlen($substring); $truncated = \mb_substr($this->__str_buffer, 0, $length); - - // If the last word was truncated if (\mb_strpos($this->__str_buffer, ' ', $length - 1) !== $length) { - // Find pos of the last occurrence of a space, get up to that $lastPos = \mb_strrpos($truncated, ' ', 0); - if ($lastPos !== false) { - $truncated = \mb_substr($truncated, 0, $lastPos); - } + if ($lastPos !== false) { $truncated = \mb_substr($truncated, 0, $lastPos); } } - $this->__str_buffer = $truncated . $substring; - return $this; } - /** - * Converts the string into an URL slug. This includes replacing non-ASCII - * characters with their closest ASCII equivalents, removing remaining - * non-ASCII and non-alphanumeric characters, and replacing whitespace with - * $replacement. The replacement defaults to a single dash, and the string - * is also converted to lowercase. The language of the source string can - * also be supplied for language-specific transliteration. - * - * @param string $replacement The string used to replace whitespace - * @param string $language Language of the source string - * @return Str - */ public function slugify(string $replacement = '-', string $language = 'en'): Str { - $this->toAscii($language); - + $split = \preg_split('/[-_]/', $language); + $language = \strtolower($split[0]); + $languageSpecific = ['de' => [['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü' ], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE']], 'bg' => [['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'], ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y']]]; + if (!empty($languageSpecific[$language])) { $this->__str_buffer = \str_replace($languageSpecific[$language][0], $languageSpecific[$language][1], $this->__str_buffer); } + foreach (self::CHARS_ARRAY as $key => $value) { $this->__str_buffer = \str_replace($value, $key, $this->__str_buffer); } $this->__str_buffer = \str_replace('@', $replacement, $this->__str_buffer); - $quotedReplacement = \preg_quote($replacement, ''); + $quotedReplacement = \preg_quote($replacement, '/'); $pattern = "/[^a-zA-Z\d\s-_$quotedReplacement]/u"; $this->__str_buffer = \preg_replace($pattern, '', $this->__str_buffer); + $this->__str_buffer = \strtolower($this->__str_buffer); + $this->__str_buffer = \preg_replace("/^['\s']+|['\s']+\$/", '', $this->__str_buffer); + $this->__str_buffer = \preg_replace('/\B([A-Z])/', '/-\1/', $this->__str_buffer); + $this->__str_buffer = \preg_replace('/[-_\s]+/', $replacement, $this->__str_buffer); + $l = \strlen($replacement); + if (0 === \strpos($this->__str_buffer, $replacement)) { $this->__str_buffer = \substr($this->__str_buffer, $l); } + if (\substr($this->__str_buffer, -$l) === $replacement) { $this->__str_buffer = \substr($this->__str_buffer, 0, \strlen($this->__str_buffer) - $l); } + return $this; + } - $this - ->toLowerCase() - ->delimit($replacement) - ->removeLeft($replacement) - ->removeRight($replacement); - - return $this; - } - - /** - * Returns an ASCII version of the string. A set of non-ASCII characters are - * replaced with their closest ASCII counterparts, and the rest are removed - * by default. The language or locale of the source string can be supplied - * for language-specific transliteration in any of the following formats: - * en, en_GB, or en-GB. For example, passing "de" results in "äöü" mapping - * to "aeoeue" rather than "aou" as in other languages. - * - * @todo check if it still works. - * - * @param string $language Language of the source string - * @param bool $removeUnsupported Whether or not to remove the unsupported characters - * @return Str - */ public function toAscii(string $language = 'en', bool $removeUnsupported = true): Str { - $langSpecific = $this->langSpecificCharsArray($language); - - if (!empty($langSpecific)) { - $this->__str_buffer = \str_replace($langSpecific[0], $langSpecific[1], $this->__str_buffer); - } - - // @todo optimize - foreach ($this->charsArray() as $key => $value) { - /** @noinspection ForeachSourceInspection */ - foreach ($value as $item) { - $this->replace($item, (string)$key); - } - } - - if ($removeUnsupported) { - $this->__str_buffer = \preg_replace('/[^\x20-\x7E]/', '', $this->__str_buffer); - } - + $split = \preg_split('/[-_]/', $language); + $language = \strtolower($split[0]); + $languageSpecific = ['de' => [['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü' ], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE']], 'bg' => [['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'], ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y']]]; + if (!empty($languageSpecific[$language])) { $this->__str_buffer = \str_replace($languageSpecific[$language][0], $languageSpecific[$language][1], $this->__str_buffer); } + foreach (self::CHARS_ARRAY as $key => $value) { $this->__str_buffer = \str_replace($value, $key, $this->__str_buffer); } + if ($removeUnsupported) { $this->__str_buffer = \preg_replace('/[^\x20-\x7E]/', '', $this->__str_buffer); } return $this; } - /** - * Returns the replacements for the toAscii() method. - * - * @todo this shouldn't be a function. - * - * @return array An array of replacements. - */ - public function charsArray(): array - { - /** @noinspection UselessReturnInspection */ - return $charsArray = [ - '0' => ['°', '₀', '۰', '0'], - '1' => ['¹', '₁', '۱', '1'], - '2' => ['²', '₂', '۲', '2'], - '3' => ['³', '₃', '۳', '3'], - '4' => ['⁴', '₄', '۴', '٤', '4'], - '5' => ['⁵', '₅', '۵', '٥', '5'], - '6' => ['⁶', '₆', '۶', '٦', '6'], - '7' => ['⁷', '₇', '۷', '7'], - '8' => ['⁸', '₈', '۸', '8'], - '9' => ['⁹', '₉', '۹', '9'], - 'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', - 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', - 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', - 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', - 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', - 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä'], - 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'], - 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'], - 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', - 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'], - 'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', - 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', - 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', - 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e'], - 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'], - 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'], - 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'], - 'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', - 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', - 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', - 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', - 'इ', 'ی', 'i'], - 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'], - 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', - 'ک', 'k'], - 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'], - 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'], - 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'], - 'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', - 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', - 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', - 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', - 'ö'], - 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'], - 'q' => ['ყ', 'q'], - 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'], - 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'], - 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'], - 'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', - 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', - 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', - 'ў', 'ü'], - 'v' => ['в', 'ვ', 'ϐ', 'v'], - 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'], - 'x' => ['χ', 'ξ', 'x'], - 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', - 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'], - 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'], - 'aa' => ['ع', 'आ', 'آ'], - 'ae' => ['æ', 'ǽ'], - 'ai' => ['ऐ'], - 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'], - 'dj' => ['ђ', 'đ'], - 'dz' => ['џ', 'ძ'], - 'ei' => ['ऍ'], - 'gh' => ['غ', 'ღ'], - 'ii' => ['ई'], - 'ij' => ['ij'], - 'kh' => ['х', 'خ', 'ხ'], - 'lj' => ['љ'], - 'nj' => ['њ'], - 'oe' => ['œ', 'ؤ'], - 'oi' => ['ऑ'], - 'oii' => ['ऒ'], - 'ps' => ['ψ'], - 'sh' => ['ш', 'შ', 'ش'], - 'shch' => ['щ'], - 'ss' => ['ß'], - 'sx' => ['ŝ'], - 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'], - 'ts' => ['ц', 'ც', 'წ'], - 'uu' => ['ऊ'], - 'ya' => ['я'], - 'yu' => ['ю'], - 'zh' => ['ж', 'ჟ', 'ژ'], - '(c)' => ['©'], - 'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', - 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', - 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', - 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', - 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä'], - 'B' => ['Б', 'Β', 'ब', 'B'], - 'C' => ['Ç','Ć', 'Č', 'Ĉ', 'Ċ', 'C'], - 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'], - 'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', - 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', - 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', - 'Є', 'Ə', 'E'], - 'F' => ['Ф', 'Φ', 'F'], - 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'], - 'H' => ['Η', 'Ή', 'Ħ', 'H'], - 'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', - 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', - 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', - 'I'], - 'J' => ['J'], - 'K' => ['К', 'Κ', 'K'], - 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'], - 'M' => ['М', 'Μ', 'M'], - 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'], - 'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', - 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', - 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', - 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö'], - 'P' => ['П', 'Π', 'P'], - 'Q' => ['Q'], - 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'], - 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'], - 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'], - 'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', - 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', - 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü'], - 'V' => ['В', 'V'], - 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'], - 'X' => ['Χ', 'Ξ', 'X'], - 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', - 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'], - 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'], - 'AE' => ['Æ', 'Ǽ'], - 'Ch' => ['Ч'], - 'Dj' => ['Ђ'], - 'Dz' => ['Џ'], - 'Gx' => ['Ĝ'], - 'Hx' => ['Ĥ'], - 'Ij' => ['IJ'], - 'Jx' => ['Ĵ'], - 'Kh' => ['Х'], - 'Lj' => ['Љ'], - 'Nj' => ['Њ'], - 'Oe' => ['Œ'], - 'Ps' => ['Ψ'], - 'Sh' => ['Ш'], - 'Shch' => ['Щ'], - 'Ss' => ['ẞ'], - 'Th' => ['Þ'], - 'Ts' => ['Ц'], - 'Ya' => ['Я'], - 'Yu' => ['Ю'], - 'Zh' => ['Ж'], - ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", - "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", - "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", - "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", - "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", - "\xEF\xBE\xA0"], - ]; - } - - /** - * Returns language-specific replacements for the toAscii() method. - * For example, German will map 'ä' to 'ae', while other languages - * will simply return 'a'. - * - * @todo this shouldn't be a function. - * - * @param string $language Language of the source string - * @return array An array of replacements. - */ - public function langSpecificCharsArray(string $language = 'en'): array - { - $split = preg_split('/[-_]/', $language); - $language = strtolower($split[0]); - - $languageSpecific = [ - 'de' => [ - ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü' ], - ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], - ], - 'bg' => [ - ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'], - ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'] - ] - ]; - - if (isset($languageSpecific[$language])) { - return $languageSpecific[$language]; - } - - return []; - } - - /** - * Returns the substring beginning at $start, and up to, but not including - * the index specified by $end. If $end is omitted, the function extracts - * the remaining string. If $end is negative, it is computed from the end - * of the string. - * - * @param int $start Initial index from which to begin extraction - * @param int $end Optional index at which to end extraction - * @return Str - */ public function slice(int $start, int $end = null): Str { - if ($end === null) { - $length = \mb_strlen($this->__str_buffer); - } - elseif ($end >= 0 && $end <= $start) { - $this->__str_buffer = ''; - return $this; - } - elseif ($end < 0) { - $length = \mb_strlen($this->__str_buffer) + $end - $start; - } - else { - $length = $end - $start; - } - - $this->substr($start, $length); - + if ($end === null) { $length = \mb_strlen($this->__str_buffer); } + elseif ($end >= 0 && $end <= $start) { $this->__str_buffer = ''; return $this; } + elseif ($end < 0) { $length = \mb_strlen($this->__str_buffer) + $end - $start; } + else { $length = $end - $start; } + $this->__str_buffer = \mb_substr($this->__str_buffer, $start, $length); return $this; } - /** - * Strip all whitespace characters. This includes tabs and newline - * characters, as well as multi-byte whitespace such as the thin space - * and ideographic space. - * - * @return Str - */ public function stripWhitespace(): Str { - $this->regexReplace('[[:space:]]+', ''); - + $this->__str_buffer = \mb_ereg_replace('[[:space:]]+', '', $this->__str_buffer); return $this; } - /** - * Truncates the string to a given length. If $substring is provided, and - * truncating occurs, the string is further truncated so that the substring - * may be appended without exceeding the desired length. - * - * @param int $length Desired length of the truncated string - * @param string $substring The substring to append if it can fit - * @return Str - */ public function truncate(int $length, string $substring = ''): Str { if ($length >= \mb_strlen($this->__str_buffer)) { return $this; } - - // Need to further trim the string so we can append the substring - $substringLength = \mb_strlen($substring); - $length -= $substringLength; - - $truncated = \mb_substr($this->__str_buffer, 0, $length); - $this->__str_buffer = $truncated . $substring; - + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $length - \mb_strlen($substring)) . $substring; return $this; } - /** - * Returns an UpperCamelCase version of the supplied string. It trims - * surrounding spaces, capitalizes letters following digits, spaces, dashes - * and underscores, and removes spaces, dashes, underscores. - * - * @return Str - */ public function upperCamelize(): Str { - $this - ->camelize() - ->upperCaseFirst(); - + $this->__str_buffer = \mb_ereg_replace("^[\s]+|[\s]+\$", '', $this->__str_buffer); + $this->__str_buffer = preg_replace('/^[-_]+/', '', $this->__str_buffer); + $this->__str_buffer = preg_replace_callback('/[-_\s]+(.)?/u', function ($match) { if (isset($match[1])) { return \mb_strtoupper($match[1]); } return ''; }, $this->__str_buffer); + $this->__str_buffer = preg_replace_callback('/[\d]+(.)?/u', function ($match) { return \mb_strtoupper($match[0]); }, $this->__str_buffer); + $this->__str_buffer = \mb_strtoupper(\mb_substr($this->__str_buffer, 0, 1)) . \mb_substr($this->__str_buffer, 1); return $this; } - /** - * Surrounds $str with the given substring. - * - * @param string $substring The substring to add to both sides - * @return Str - */ public function surround(string $substring): Str { - $this->__str_buffer = implode('', [$substring, $this->__str_buffer, $substring]); - + $this->__str_buffer = $substring . $this->__str_buffer . $substring; return $this; } - /** - * Returns a case swapped version of the string. - * - * @return Str - */ public function swapCase(): Str { - $this->__str_buffer = preg_replace_callback( - '/[\S]/u', - function ($match) { - if ($match[0] === \mb_strtoupper($match[0])) { - return \mb_strtolower($match[0]); - } - - return \mb_strtoupper($match[0]); - }, - $this->__str_buffer - ); - + $this->__str_buffer = \preg_replace_callback('/[\S]/u', function ($match) { if ($match[0] === \mb_strtoupper($match[0])) { return \mb_strtolower($match[0]); } return \mb_strtoupper($match[0]); }, $this->__str_buffer); return $this; } - /** - * Returns a string with smart quotes, ellipsis characters, and dashes from - * Windows-1252 (commonly used in Word documents) replaced by their ASCII - * equivalents. - * - * @return Str - */ public function tidy(): Str { - $this->__str_buffer = preg_replace([ - '/\x{2026}/u', - '/[\x{201C}\x{201D}]/u', - '/[\x{2018}\x{2019}]/u', - '/[\x{2013}\x{2014}]/u', - ], [ - '...', - '"', - "'", - '-', - ], $this->__str_buffer); - - return $this; - } - - /** - * Returns a trimmed string with the first letter of each word capitalized. - * Also accepts an array, $ignore, allowing you to list words not to be - * capitalized. - * - * @todo check if it still works. - * - * @param array $ignore An array of words not to capitalize - * @return Str - */ + $this->__str_buffer = \preg_replace(['/\x{2026}/u', '/[\x{201C}\x{201D}]/u', '/[\x{2018}\x{2019}]/u', '/[\x{2013}\x{2014}]/u'], ['...', '"', "'", '-'], $this->__str_buffer); + return $this; + } + public function titleize(array $ignore = []): Str { - $this->trim(); - - $this->__str_buffer = preg_replace_callback( - '/([\S]+)/u', - function ($match) use ($ignore) { - if ($ignore && \in_array($match[0], $ignore, true)) { - return $match[0]; - } - - $this->__str_buffer = $match[0]; - $this - ->toLowerCase() - ->upperCaseFirst(); - - return $this->__str_buffer; - }, - $this->__str_buffer - ); - + $this->__str_buffer = \mb_ereg_replace("^[\s]+|[\s]+\$", '', $this->__str_buffer); + $this->__str_buffer = preg_replace_callback('/([\S]+)/u', function ($match) use ($ignore) { + if ($ignore && \in_array($match[0], $ignore, true)) { return $match[0]; } + $this->__str_buffer = \mb_strtoupper(\mb_substr($match[0], 0, 1)) . \mb_strtolower(\mb_substr($match[0], 1)); + return $this->__str_buffer; }, $this->__str_buffer); return $this; } - /** - * Returns a boolean representation of the given logical string value. - * For example, 'true', '1', 'on' and 'yes' will return true. 'false', '0', - * 'off', and 'no' will return false. In all instances, case is ignored. - * For other numeric strings, their sign will determine the return value. - * In addition, blank strings consisting of only whitespace will return - * false. For all other strings, the return value is a result of a - * boolean cast. - * - * @return bool A boolean value for the string - */ public function toBoolean(): bool { - $key = (string)$this->toLowerCase(); - $map = [ - 'true' => true, - '1' => true, - 'on' => true, - 'yes' => true, - 'false' => false, - '0' => false, - 'off' => false, - 'no' => false - ]; - - if (\array_key_exists($key, $map)) { - return $map[$key]; - } - - if (\is_numeric($this->__str_buffer)) { - return ((int)$this->__str_buffer > 0); - } - - $this->regexReplace('[[:space:]]+', ''); - - return (bool)$this->__str_buffer; + $key = \strtolower($this->__str_buffer); + $map = ['true' => true, '1' => true, 'on' => true, 'yes' => true, 'false' => false, '0' => false, 'off' => false, 'no' => false]; + if (\array_key_exists($key, $map)) { return $map[$key]; } + if (\is_numeric($this->__str_buffer)) { return $this->__str_buffer + 0 > 0; } + return (bool)\mb_ereg_replace('[[:space:]]+', '', $this->__str_buffer); } - /** - * Converts each tab in the string to some number of spaces, as defined by - * $tabLength. By default, each tab is converted to 4 consecutive spaces. - * - * We don't check for ascii here. - * - * @param int $tabLength Number of spaces to replace each tab with - * @return Str - */ public function toSpaces(int $tabLength = 4): Str { - $spaces = \str_repeat(' ', $tabLength); - $this->__str_buffer = \str_replace("\t", $spaces, $this->__str_buffer); - + $this->__str_buffer = \str_replace("\t", \str_repeat(' ', $tabLength), $this->__str_buffer); return $this; } - /** - * Converts each occurrence of some consecutive number of spaces, as - * defined by $tabLength, to a tab. By default, each 4 consecutive spaces - * are converted to a tab. - * - * @param int $tabLength Number of spaces to replace with a tab - * @return Str - */ public function toTabs(int $tabLength = 4): Str { - $spaces = \str_repeat(' ', $tabLength); - $this->__str_buffer = \str_replace($spaces, "\t", $this->__str_buffer); - + $this->__str_buffer = \str_replace(\str_repeat(' ', $tabLength), "\t", $this->__str_buffer); return $this; } - /** - * Converts the first character of each word in the string to uppercase. - * - * NB! In this function we don't check if the string is ascii. - * - * @return Str - */ public function toTitleCase(): Str { $this->__str_buffer = \mb_convert_case($this->__str_buffer, \MB_CASE_TITLE); - return $this; } - /** - * Returns a lowercase and trimmed string separated by underscores. - * Underscores are inserted before uppercase characters (with the exception - * of the first character of the string), and in place of spaces as well as - * dashes. - * - * @return Str - */ public function underscored(): Str { - $this->delimit('_'); - + $this->__str_buffer = \mb_ereg_replace("^['\s']+|['\s']+\$", '', $this->__str_buffer); + $this->__str_buffer = \mb_strtolower(\mb_ereg_replace('\B([A-Z])', '-\1', $this->__str_buffer)); + $this->__str_buffer = \mb_ereg_replace('[-_\s]+', '_', $this->__str_buffer); return $this; } - /** - * Move substring of desired $length to $destination index of the original string. - * In case $destination is less than $length returns $str untouched. - * - * @param int $start - * @param int $length - * @param int $destination - * @return Str - */ public function move(int $start, int $length, int $destination): Str { if ($destination <= $length) { return $this; } - $substr = \mb_substr($this->__str_buffer, $start, $length); - - $this - ->insert($substr, $destination) - ->replace($substr, '', 1); - + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $destination) . $substr . \mb_substr($this->__str_buffer, $destination); + $pos = \mb_strpos($this->__str_buffer, $substr, 0); + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $pos) . \mb_substr($this->__str_buffer, $pos + \mb_strlen($substr)); return $this; } - /** - * Replaces substring in the original string of $length with given $substr. - * - * @param int $start - * @param int $length - * @param string $substr - * @return Str - */ public function overwrite(int $start, int $length, string $substr): Str { if ($length <= 0) { return $this; } - $sub = \mb_substr($this->__str_buffer, $start, $length); - - $this->replace($sub, $substr, 1); - + $pos = \mb_strpos($this->__str_buffer, $sub, 0); + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $pos) . $substr . \mb_substr($this->__str_buffer, $pos + \mb_strlen($sub)); return $this; } - /** - * Returns a snake_case version of the string. - * - * @return Str - */ public function snakeize(): Str { $this->__str_buffer = \mb_ereg_replace('::', '/', $this->__str_buffer); @@ -1882,387 +815,156 @@ public function snakeize(): Str $this->__str_buffer = \mb_ereg_replace('\s+', '_', $this->__str_buffer); $this->__str_buffer = \mb_ereg_replace('^\s+|\s+$', '', $this->__str_buffer); $this->__str_buffer = \mb_ereg_replace('-', '_', $this->__str_buffer); - - $this->toLowerCase(); - - $this->__str_buffer = \mb_ereg_replace_callback( - '([\d|A-Z])', - function ($matches) { - $match = $matches[1]; - if ((string)(int)$match === $match) { - return '_' . $match . '_'; - } - }, - $this->__str_buffer - ); - + $this->__str_buffer = \mb_strtolower($this->__str_buffer); + $this->__str_buffer = \mb_ereg_replace_callback('([\d|A-Z])', function ($matches) { $match = $matches[1]; if ((string)(int)$match === $match) { return '_' . $match . '_'; }}, $this->__str_buffer); $this->__str_buffer = \mb_ereg_replace('_+', '_', $this->__str_buffer); - - $this->trim('_'); + $this->__str_buffer = \preg_replace('/^[_]+|[_]+$/', '', $this->__str_buffer); return $this; } - /** - * Inserts given $substr $times times into the original string after - * the first occurrence of $needle. - * - * @param string $needle - * @param string $substr - * @param int $times - * @return Str - */ public function afterFirst(string $needle, string $substr, int $times = 1): Str { - $idx = $this->indexOf($needle); - $needleLen = \mb_strlen($needle); - $idxEnd = $idx + $needleLen; - $innerSubstr = str_repeat($substr, $times); - - $this->insert($innerSubstr, $idxEnd); - + $idxEnd = \mb_strpos($this->__str_buffer, $needle) + \mb_strlen($needle); + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $idxEnd) . \str_repeat($substr, $times) . \mb_substr($this->__str_buffer, $idxEnd); return $this; } - /** - * Inserts given $substr $times times into the original string before - * the first occurrence of $needle. - * - * @param string $needle - * @param string $substr - * @param int $times - * @return Str - */ public function beforeFirst(string $needle, string $substr, int $times = 1): Str { - $idx = $this->indexOf($needle); - $innerSubstr = str_repeat($substr, $times); - - $this->insert($innerSubstr, $idx); - + $idx = \mb_strpos($this->__str_buffer, $needle); + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $idx) . \str_repeat($substr, $times) . \mb_substr($this->__str_buffer, $idx); return $this; } - /** - * Inserts given $substr $times times into the original string after - * the last occurrence of $needle. - * - * @param string $needle - * @param string $substr - * @param int $times - * @return Str - */ public function afterLast(string $needle, string $substr, int $times = 1): Str { - $idx = $this->indexOfLast($needle); - $needleLen = \mb_strlen($needle); - $idxEnd = $idx + $needleLen; - $innerSubstr = str_repeat($substr, $times); - - $this->insert($innerSubstr, $idxEnd); - + $idxEnd = \mb_strrpos($this->__str_buffer, $needle) + \mb_strlen($needle); + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $idxEnd) . \str_repeat($substr, $times) . \mb_substr($this->__str_buffer, $idxEnd); return $this; } - /** - * Inserts given $substr $times times into the original string before - * the last occurrence of $needle. - * - * @param string $needle - * @param string $substr - * @param int $times - * @return Str - */ public function beforeLast(string $needle, string $substr, int $times = 1): Str { - $idx = $this->indexOfLast($needle); - $innerSubstr = str_repeat($substr, $times); - - $this->insert($innerSubstr, $idx); - + $idx = \mb_strrpos($this->__str_buffer, $needle); + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, $idx) . \str_repeat($substr, $times) . \mb_substr($this->__str_buffer, $idx); return $this; } - /** - * Splits the original string in pieces by '@' delimiter and returns - * true in case the resulting array consists of 2 parts. - * - * @return bool - */ public function isEmail(): bool { - $split = $this->split('@'); - - return \count($split) === 2; + return \count(\mb_split('@', $this->__str_buffer)) === 2; } - /** - * Checks whether given $str is a valid ip v4. - * - * @return bool - */ public function isIpV4(): bool { - $regex = '\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}\b'; - - return $this->matchesPattern($regex); + return (bool)\preg_match('/\b((25[0-5]|2[0-4][\d]|[01]?[\d][\d]?)(\.|$)){4}\b/', $this->__str_buffer); } - /** - * Checks whether given $str is a valid ip v6. - * - * @return bool - */ public function isIpV6(): bool { - $regex = '^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$'; - - return $this->matchesPattern($regex); + return (bool)\preg_match('/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/', $this->__str_buffer); } - /** - * Generates a random string consisting of $possibleChars, if specified, of given $size or - * random length between $size and $sizeMax. - * - * @param int $size The desired length of the string - * @param string $possibleChars If given, specifies allowed characters to make the string of - * @param int $sizeMax If given and is > $size, the generated string will have random length - * between $size and $sizeMax - * @return string - */ - public function random(int $size, int $sizeMax = -1, string $possibleChars = ''): string + public function random(int $size, int $sizeMax = -1, string $possibleChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'): Str { - if ($size <= 0 || $sizeMax === 0) { return ''; } - if ($sizeMax > 0 && $sizeMax < $size) { return ''; } - - $allowedChars = $possibleChars ?: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - + if ($size <= 0 || $sizeMax === 0) { $this->__str_buffer = ''; return $this; } + if ($sizeMax > 0 && $sizeMax < $size) { $this->__str_buffer = ''; return $this; } $maxLen = $sizeMax > 0 ? $sizeMax : $size; - /** @noinspection RandomApiMigrationInspection */ $actualLen = \rand($size, $maxLen); - $allowedCharsLen = \mb_strlen($allowedChars) - 1; - + $allowedCharsLen = \mb_strlen($possibleChars) - 1; $result = ''; - - while ($actualLen--) { - /** @noinspection RandomApiMigrationInspection */ - $char = \mb_substr($allowedChars, \rand(0, $allowedCharsLen), 1); - $result .= $char; - } - - return $result; + while ($actualLen--) { $char = \mb_substr($possibleChars, \rand(0, $allowedCharsLen), 1); $result .= $char; } + $this->__str_buffer = $result; + return $this; } - /** - * Appends a random string consisting of $possibleChars, if specified, of given $size or - * random length between $size and $sizeMax to the original string. - * - * @param int $size The desired length of the string. Defaults to 4 - * @param string $possibleChars If given, specifies allowed characters to make the string of - * @param int $sizeMax If given and is > $size, the generated string will have random length - * between $size and $sizeMax - * @return Str - */ - public function appendUniqueIdentifier(int $size = 4, int $sizeMax = -1, string $possibleChars = ''): Str + public function appendUniqueIdentifier(int $size = 4, int $sizeMax = -1, string $possibleChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'): Str { - $identifier = $this->random($size, $sizeMax, $possibleChars); - - $this->__str_buffer .= $identifier; - + if ($size <= 0 || $sizeMax === 0) { $this->__str_buffer = ''; return $this; } + if ($sizeMax > 0 && $sizeMax < $size) { $this->__str_buffer = ''; return $this; } + $maxLen = $sizeMax > 0 ? $sizeMax : $size; + $actualLen = \rand($size, $maxLen); + $allowedCharsLen = \mb_strlen($possibleChars) - 1; + $result = ''; + while ($actualLen--) { $char = \mb_substr($possibleChars, \rand(0, $allowedCharsLen), 1); $result .= $char; } + $this->__str_buffer .= $result; return $this; } - /** - * Splits on whitespace, returning an array of strings corresponding to the words in the string. - * - * @return array of strings - */ public function words(): array { - return $this->split('[[:space:]]+'); + if ('' === $this->__str_buffer) { return []; } + return \mb_split('[[:space:]]+', $this->__str_buffer); } - /** - * Wraps each word in the original string with specified $quote. - * - * @param string $quote Defaults to ". - * @return Str - */ public function quote(string $quote = '"'): Str { - $words = $this->words(); + $words = \mb_split('[[:space:]]+', $this->__str_buffer); $result = []; - - foreach ($words as $word) { - $result[] = $quote . $word . $quote; - } - + foreach ($words as $word) { $result[] = $quote . $word . $quote; } $this->__str_buffer = \implode(' ', $result); - return $this; } - /** - * Unwraps each word in the original string, deleting the specified $quote. - * - * @param string $quote Defaults to ". - * @return Str - */ public function unquote(string $quote = '"'): Str { - $words = $this->words(); + $words = \mb_split('[[:space:]]+', $this->__str_buffer); $result = []; - foreach ($words as $word) { $this->__str_buffer = $word; - $this->trim($quote); + $this->__str_buffer = \mb_ereg_replace("^[$quote]+|[$quote]+\$", '', $this->__str_buffer); $result[] = $this->__str_buffer; } - $this->__str_buffer = \implode(' ', $result); - return $this; } - /** - * Cuts the original string in pieces of $step size. - * - * @param int $step - * @return array - */ public function chop(int $step): array { $result = []; $len = \mb_strlen($this->__str_buffer); - if ($this->__str_buffer === '' || $step <= 0) { return []; } - if ($step >= $len) { return [$this->__str_buffer]; } - $startPos = 0; - - for ($i = 0; $i < $len; $i+=$step) { - $result[] = \mb_substr($this->__str_buffer, $startPos, $step); - $startPos += $step; - } - + for ($i = 0; $i < $len; $i+=$step) { $result[] = \mb_substr($this->__str_buffer, $startPos, $step); $startPos += $step; } return $result; } - /** - * Joins the original string with an array of other strings. - * - * @param string $separator - * @param array $otherStrings - * @return Str - */ public function join(string $separator, array $otherStrings = []): Str { - if (empty($otherStrings)) { return $this; } - - foreach ($otherStrings as $otherString) { - if ($otherString) { - $this->__str_buffer .= ($separator . $otherString); - } - } - + if ('' === $this->__str_buffer) { return $this; } + foreach ($otherStrings as $otherString) { $this->__str_buffer .= ($separator . $otherString); } return $this; } - /** - * Returns the substring of the original string from beginning to - * the first occurrence of $delimiter. - * - * @param string $delimiter - * - * @return Str - */ public function shift(string $delimiter): Str { - if (!$this->__str_buffer || !$delimiter) { - $this->__str_buffer = ''; - return $this; - } - - $idx = $this->indexOf($delimiter); - - if ($idx === -1) { return $this; } - - $this->substr(0, $idx); - + if ('' === $delimiter) { $this->__str_buffer = ''; return $this; } + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, \mb_strpos($this->__str_buffer, $delimiter) ?: \mb_strlen($this->__str_buffer)); return $this; } - /** - * Returns the substring of the original string from the first - * occurrence of $delimiter to the end. - * - * @param string $delimiter - * - * @return Str - */ public function shiftReversed(string $delimiter): Str { - if (!$this->__str_buffer || !$delimiter) { - $this->__str_buffer = ''; - return $this; - } - - $idx = $this->indexOf($delimiter) + 1; - - if ($idx === -1) { return $this; } - - $this->substr($idx); - + if ('' === $delimiter) { $this->__str_buffer = ''; return $this; } + $idx = \mb_strpos($this->__str_buffer, $delimiter); + $this->__str_buffer = \mb_substr($this->__str_buffer, $idx ? $idx + 1 : 0); return $this; } - /** - * Returns the substring of the original string from the last - * occurrence of $delimiter to the end. - * - * @param string $delimiter - * - * @return Str - */ public function pop(string $delimiter): Str { - if (!$this->__str_buffer || !$delimiter) { - $this->__str_buffer = ''; - return $this; - } - - $idx = $this->indexOfLast($delimiter) + 1; - - if ($idx === -1) { return $this; } - - $this->substr($idx); - + if ('' === $delimiter) { $this->__str_buffer = ''; return $this; } + $idx = \mb_strrpos($this->__str_buffer, $delimiter); + $this->__str_buffer = \mb_substr($this->__str_buffer, $idx ? $idx + 1 : 0); return $this; } - - /** - * Returns the substring of the original string from the beginning - * to the last occurrence of $delimiter. - * - * @param string $delimiter - * - * @return Str - */ public function popReversed(string $delimiter): Str { - if (!$this->__str_buffer || !$delimiter) { - $this->__str_buffer = ''; - return $this; - } - - $idx = $this->indexOfLast($delimiter); - - if ($idx === -1) { return $this; } - - $this->substr(0, $idx); - + if ('' === $delimiter) { $this->__str_buffer = ''; return $this; } + $this->__str_buffer = \mb_substr($this->__str_buffer, 0, \mb_strrpos($this->__str_buffer, $delimiter) ?: \mb_strlen($this->__str_buffer)); return $this; } } diff --git a/tests/FS/StrAllTest.php b/tests/FS/StrFullTest.php similarity index 97% rename from tests/FS/StrAllTest.php rename to tests/FS/StrFullTest.php index c820a2b..4cfca22 100644 --- a/tests/FS/StrAllTest.php +++ b/tests/FS/StrFullTest.php @@ -6,15 +6,8 @@ use PHPUnit\Framework\TestCase; -class StrAllTest extends TestCase +class StrFullTest extends TestCase { - public function testGetString() - { - $s = new Str('Hello'); - $this->assertEquals('Hello', (string)$s); - $this->assertEquals('Hello', $s->getString()); - } - /** * @dataProvider HasPrefixProvider * @param array $inp @@ -134,25 +127,126 @@ public function HasSuffixProvider() } /** - * @dataProvider lengthProvider() + * @dataProvider SubstrProvider * @param $expected * @param $str + * @param int $start + * @param int $length */ - public function testLength($expected, $str) + public function testSubstr($expected, $str, $start = 0, $length = 1) { $s = new Str($str); - $this->assertEquals($expected, $s->length()); + $this->assertEquals($expected, $s->substr($start, $length)); } - public function lengthProvider() + + public function SubstrProvider() { return [ - [11, ' foo bar '], - [1, 'f'], - [0, ''], - [7, 'fòô bàř'] + ['Hel', 'Hello world', 0, 3], + ['H世', 'H世ello world', 0, 2], + [' H世', ' H世', 1, 4], + ['123', '000123000', 3, 3], ]; } + /** + * @dataProvider EnsureRightProvider + * @param array $inp + * @param string $out + */ + public function testEnsureRight(array $inp, string $out) + { + $s = new Str($inp[0]); + $affix = $inp[1]; + $this->assertEquals($out, $s->ensureRight($affix)); + } + + public function EnsureRightProvider() + { + return + [ + [ + ['Hello world', '<<_right'], + 'Hello world<<_right', + ], + [ + ['Hello world<<_right', '<<_right'], + 'Hello world<<_right', + ], + [ + ['q', 'q'], + 'q', + ], + [ + ['qq', 'q'], + 'qq', + ], + [ + ['Hello, 世界', '世'], + 'Hello, 世界世', + ], + [ + ['Hello, 世界界', '界'], + 'Hello, 世界界', + ], + [ + ['世', '世'], + '世', + ], + ]; + } + + /** + * @dataProvider EnsureLeftProvider + * @param array $inp + * @param string $out + */ + public function testEnsureLeft(array $inp, string $out) + { + $s = new Str($inp[0]); + $affix = $inp[1]; + $this->assertEquals($out, $s->ensureLeft($affix)); + } + + public function EnsureLeftProvider() + { + return + [ + [ + ['Hello world', '_left>>'], + '_left>>Hello world', + ], + [ + ['_left>>Hello world', '_left>>'], + '_left>>Hello world', + ], + [ + ['q', 'q'], + 'q', + ], + [ + ['qq', 'q'], + 'qq', + ], + [ + ['Hello, 世界', '界'], + '界Hello, 世界', + ], + [ + ['界Hello, 世界', '界'], + '界Hello, 世界', + ], + [ + ['世界', 'Hello, '], + 'Hello, 世界', + ], + [ + ['世', '世'], + '世', + ], + ]; + } + /** * @dataProvider ContainsProvider * @param $expected @@ -204,1703 +298,1610 @@ public function ContainsProvider(): array } /** - * @dataProvider indexOfProvider() + * @dataProvider ReplaceWithLimitProvider + * @param $params * @param $expected - * @param $haystack - * @param $needle - * @param int $offset */ - public function testIndexOf($expected, $haystack, $needle, $offset = 0) + public function testReplaceWithLimit($params, $expected) { - $s = new Str($haystack); - $this->assertEquals($expected, $s->indexOf($needle, $offset)); + $s = new Str($params[0]); + $old = $params[1]; + $new = $params[2]; + $limit = $params[3]; + $this->assertEquals($expected, $s->replaceWithLimit($old, $new, $limit)); } - - public function indexOfProvider() + public function ReplaceWithLimitProvider() { - return [ - [6, 'foo & bar', 'bar'], - [6, 'foo & bar', 'bar', 0], - [-1, 'foo & bar', 'baz'], - [-1, 'foo & bar', 'baz', 0], - [0, 'foo & bar & foo', 'foo', 0], - [12, 'foo & bar & foo', 'foo', 5], - [12, 'foo & bar & foo', 'foo', -5], - [6, 'fòô & bàř', 'bàř', 0], - [-1, 'fòô & bàř', 'baz', 0], - [0, 'fòô & bàř & fòô', 'fòô', 0], - [12, 'fòô & bàř & fòô', 'fòô', 5], - [12, 'fòô & bàř & fòô', 'fòô', -5], - [-1, 'q', 'q', -5], - [-1, '', '', 0], - [-1, 'q', '', 0], - [-1, '', 'q', 0], - [-1, 'q', 'q', 5], - ]; + return + [ + [ + ['oink oink oinkk', 'k', 'ky', 2], + 'oinky oinky oinkk' + ], + [ + ['oink oink oink', 'k', 'ky', 4], + 'oinky oinky oinky' + ], + [ + ['oink oink oink', 'oink', 'moo', -1], + 'moo moo moo' + ], + [ + ['hello world, hello universe', 'hello', 'привет', 1], + 'привет world, hello universe' + ], + [ + ['banana', 'a', 'e', 4], + 'benene' + ], + [ + ['ban 世 a!', ' 世', ' foo', -1], + 'ban foo a!' + ], + [ + ['世世世世世', '世', '界', 2], + '界界世世世' + ], + [ + ['世世世世世', '世', '界', 0], + '世世世世世' + ], + [ + ['世q世q世', 'q', 'q', 5], + '世q世q世' + ], + [ + ['世q世q世', 'z', 'zz', 2], + '世q世q世' + ], + [ + ['', 'a', 'b', -1], + '' + ], + ]; } /** - * @dataProvider indexOfLastProvider() + * @dataProvider ReplaceProvider + * * @param $expected - * @param $haystack - * @param $needle - * @param int $offset + * @param $str + * @param $search + * @param $replacement */ - public function testIndexOfLast($expected, $haystack, $needle, $offset = 0) + public function testReplace($expected, $str, $search, $replacement) { - $s = new Str($haystack); - $this->assertEquals($expected, $s->indexOfLast($needle, $offset)); + $s = new Str($str); + $this->assertEquals($expected, $s->replace($search, $replacement)); + } + public function ReplaceProvider() + { + return + [ + ['', '', '', ''], + ['foo', '', '', 'foo'], + ['foo', '\s', '\s', 'foo'], + ['foo bar', 'foo bar', '', ''], + ['foo bar', 'foo bar', 'f(o)o', '\1'], + ['\1 bar', 'foo bar', 'foo', '\1'], + ['bar', 'foo bar', 'foo ', ''], + ['far bar', 'foo bar', 'foo', 'far'], + ['bar bar', 'foo bar foo bar', 'foo ', ''], + ['', '', '', ''], + ['fòô', '', '', 'fòô'], + ['fòô', '\s', '\s', 'fòô'], + ['fòô bàř', 'fòô bàř', '', ''], + ['bàř', 'fòô bàř', 'fòô ', ''], + ['far bàř', 'fòô bàř', 'fòô', 'far'], + ['bàř bàř', 'fòô bàř fòô bàř', 'fòô ', ''], + ]; } - public function indexOfLastProvider() + /** + * @dataProvider toLowerCaseProvider() + * @param $expected + * @param $str + */ + public function testToLowerCase($expected, $str) + { + $s = new Str($str); + $this->assertEquals($expected, $s->toLowerCase()); + } + public function toLowerCaseProvider() { return [ - [6, 'foo & bar', 'bar'], - [6, 'foo & bar', 'bar', 0], - [-1, 'foo & bar', 'baz'], - [-1, 'foo & bar', 'baz', 0], - [12, 'foo & bar & foo', 'foo', 0], - [12, 'foo & bar & foo', 'foo', -5], - [6, 'fòô & bàř', 'bàř', 0], - [-1, 'fòô & bàř', 'baz', 0], - [12, 'fòô & bàř & fòô', 'fòô', 0], - [12, 'fòô & bàř & fòô', 'fòô', -5], - [-1, 'q', 'q', -5], - [-1, '', '', 0], - [-1, 'q', '', 0], - [-1, '', 'q', 0], - [-1, 'q', 'q', 5], + ['foo bar', 'FOO BAR'], + [' foo_bar ', ' FOO_bar '], + ['fòô bàř', 'FÒÔ BÀŘ'], + [' fòô_bàř ', ' FÒÔ_bàř '], + ['αυτοκίνητο', 'ΑΥΤΟΚΊΝΗΤΟ'], ]; } /** - * @dataProvider countSubstrProvider() + * @dataProvider toUpperCaseProvider() * @param $expected * @param $str - * @param $substring - * @param bool $caseSensitive */ - public function testCountSubstr($expected, $str, $substring, $caseSensitive = true) + public function testToUpperCase($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->countSubstr($substring, $caseSensitive)); + $this->assertEquals($expected, $s->toUpperCase()); } - - public function countSubstrProvider() + public function toUpperCaseProvider() { return [ - [0, '', 'foo'], - [0, 'foo', 'bar'], - [1, 'foo bar', 'foo'], - [2, 'foo bar', 'o'], - [0, '', 'fòô'], - [0, 'fòô', 'bàř'], - [1, 'fòô bàř', 'fòô'], - [2, 'fôòô bàř', 'ô'], - [0, 'fÔÒÔ bàř', 'ô'], - [0, 'foo', 'BAR', false], - [1, 'foo bar', 'FOo', false], - [2, 'foo bar', 'O', false], - [1, 'fòô bàř', 'fÒÔ', false], - [2, 'fôòô bàř', 'Ô', false], - [2, 'συγγραφέας', 'Σ', false] + ['FOO BAR', 'foo bar'], + [' FOO_BAR ', ' FOO_bar '], + ['FÒÔ BÀŘ', 'fòô bàř'], + [' FÒÔ_BÀŘ ', ' FÒÔ_bàř '], + ['ΑΥΤΟΚΊΝΗΤΟ', 'αυτοκίνητο'], ]; } /** - * @dataProvider containsAllProvider() + * @dataProvider trimProvider() * @param $expected - * @param $haystack - * @param $needles - * @param bool $caseSensitive + * @param $str + * @param string $chars */ - public function testContainsAll($expected, $haystack, $needles, $caseSensitive = true) + public function testTrim($expected, $str, $chars = '') { - $s = new Str($haystack); - $this->assertEquals($expected, $s->containsAll($needles, $caseSensitive), $haystack); + $s = new Str($str); + $this->assertEquals($expected, $s->trim($chars)); } - - public function containsAllProvider() + public function trimProvider() { - // One needle - $singleNeedle = array_map(function ($array) { - $array[2] = [$array[2]]; - return $array; - }, $this->ContainsProvider()); - $provider = [ - // One needle - [false, 'Str contains foo bar', []], - // Multiple needles - [true, 'Str contains foo bar', ['foo', 'bar']], - [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*', '&^%']], - [true, 'Ο συγγραφέας είπε', ['συγγρ', 'αφέας']], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å´¥', '©'], true], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å˚ ', '∆'], true], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['øœ', '¬'], true], - [false, 'Str contains foo bar', ['Foo', 'bar']], - [false, 'Str contains foo bar', ['foobar', 'bar']], - [false, 'Str contains foo bar', ['foo bar ', 'bar']], - [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' συγγραφ '], true], - [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßå˚', ' ß '], true], - [true, 'Str contains foo bar', ['Foo bar', 'bar'], false], - [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*&^%', '*&^%'], false], - [true, 'Ο συγγραφέας είπε', ['ΣΥΓΓΡΑΦΈΑΣ', 'ΑΦΈΑ'], false], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å´¥©', '¥©'], false], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å˚ ∆', ' ∆'], false], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['ØŒ¬', 'Œ'], false], - [false, 'Str contains foo bar', ['foobar', 'none'], false], - [false, 'Str contains foo bar', ['foo bar ', ' ba'], false], - [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' ραφέ '], false], - [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßÅ˚', ' Å˚ '], false], + return [ + ['foo bar', ' foo bar '], + ['foo bar', ' foo bar'], + ['foo bar', 'foo bar '], + ['foo bar', "\n\t foo bar \n\t"], + ['fòô bàř', ' fòô bàř '], + ['fòô bàř', ' fòô bàř'], + ['fòô bàř', 'fòô bàř '], + [' foo bar ', "\n\t foo bar \n\t", "\n\t"], + ['fòô bàř', "\n\t fòô bàř \n\t", ''], + ['fòô', ' fòô ', ''], // narrow no-break space (U+202F) + ['fòô', '  fòô  ', ''], // medium mathematical space (U+205F) + ['fòô', '           fòô', ''] // spaces U+2000 to U+200A ]; - return array_merge($singleNeedle, $provider); } /** - * @dataProvider containsAnyProvider() + * @dataProvider trimLeftProvider() * @param $expected - * @param $haystack - * @param $needles - * @param bool $caseSensitive + * @param $str + * @param string $chars */ - public function testContainsAny($expected, $haystack, $needles, $caseSensitive = true) + public function testTrimLeft($expected, $str, $chars = '') { - $s = new Str($haystack); - $this->assertEquals($expected, $s->containsAny($needles, $caseSensitive), $haystack); + $s = new Str($str); + $this->assertEquals($expected, $s->trimLeft($chars)); } - public function containsAnyProvider() + public function trimLeftProvider() { - // One needle - $singleNeedle = array_map(function ($array) { - $array[2] = [$array[2]]; - return $array; - }, $this->ContainsProvider()); - $provider = [ - // No needles - [false, 'Str contains foo bar', []], - // Multiple needles - [true, 'Str contains foo bar', ['foo', 'bar']], - [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*', '&^%']], - [true, 'Ο συγγραφέας είπε', ['συγγρ', 'αφέας']], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å´¥', '©'], true], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å˚ ', '∆'], true], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['øœ', '¬'], true], - [false, 'Str contains foo bar', ['Foo', 'Bar']], - [false, 'Str contains foo bar', ['foobar', 'bar ']], - [false, 'Str contains foo bar', ['foo bar ', ' foo']], - [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' συγγραφ '], true], - [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßå˚', ' ß '], true], - [true, 'Str contains foo bar', ['Foo bar', 'bar'], false], - [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*&^%', '*&^%'], false], - [true, 'Ο συγγραφέας είπε', ['ΣΥΓΓΡΑΦΈΑΣ', 'ΑΦΈΑ'], false], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å´¥©', '¥©'], false], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å˚ ∆', ' ∆'], false], - [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['ØŒ¬', 'Œ'], false], - [false, 'Str contains foo bar', ['foobar', 'none'], false], - [false, 'Str contains foo bar', ['foo bar ', ' ba '], false], - [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' ραφέ '], false], - [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßÅ˚', ' Å˚ '], false], + return [ + ['foo bar ', ' foo bar '], + ['foo bar', ' foo bar'], + ['foo bar ', 'foo bar '], + ["foo bar \n\t", "\n\t foo bar \n\t"], + ['fòô bàř ', ' fòô bàř '], + ['fòô bàř', ' fòô bàř'], + ['fòô bàř ', 'fòô bàř '], + ['foo bar', '--foo bar', '-'], + ['fòô bàř', 'òòfòô bàř', 'ò'], + ["fòô bàř \n\t", "\n\t fòô bàř \n\t", ''], + ['fòô ', ' fòô ', ''], // narrow no-break space (U+202F) + ['fòô  ', '  fòô  ', ''], // medium mathematical space (U+205F) + ['fòô', '           fòô', ''] // spaces U+2000 to U+200A ]; - return array_merge($singleNeedle, $provider); } /** - * @dataProvider startsWithProvider() + * @dataProvider trimRightProvider() * @param $expected * @param $str - * @param $substring - * @param bool $caseSensitive + * @param $chars */ - public function testStartsWith($expected, $str, $substring, $caseSensitive = true) + public function testTrimRight($expected, $str, $chars = '') { $s = new Str($str); - $this->assertEquals($expected, $s->startsWith($substring, $caseSensitive), $str); + $this->assertEquals($expected, $s->trimRight($chars)); } - public function startsWithProvider() + public function trimRightProvider() { return [ - [true, 'foo bars', 'foo bar'], - [true, 'FOO bars', 'foo bar', false], - [true, 'FOO bars', 'foo BAR', false], - [true, 'FÒÔ bàřs', 'fòô bàř', false], - [true, 'fòô bàřs', 'fòô BÀŘ', false], - [false, 'foo bar', 'bar'], - [false, 'foo bar', 'foo bars'], - [false, 'FOO bar', 'foo bars'], - [false, 'FOO bars', 'foo BAR'], - [false, 'FÒÔ bàřs', 'fòô bàř', true], - [false, 'fòô bàřs', 'fòô BÀŘ', true], + [' foo bar', ' foo bar '], + ['foo bar', 'foo bar '], + [' foo bar', ' foo bar'], + ["\n\t foo bar", "\n\t foo bar \n\t"], + [' fòô bàř', ' fòô bàř '], + ['fòô bàř', 'fòô bàř '], + [' fòô bàř', ' fòô bàř'], + ['foo bar', 'foo bar--', '-'], + ['fòô bàř', 'fòô bàřòò', 'ò'], + ["\n\t fòô bàř", "\n\t fòô bàř \n\t", ''], + [' fòô', ' fòô ', ''], // narrow no-break space (U+202F) + ['  fòô', '  fòô  ', ''], // medium mathematical space (U+205F) + ['fòô', 'fòô           ', ''] // spaces U+2000 to U+200A ]; } /** - * @dataProvider startsWithProviderAny() + * @dataProvider appendProvider() * @param $expected * @param $str - * @param $substrings - * @param bool $caseSensitive + * @param $string */ - public function testStartsWithAny($expected, $str, $substrings, $caseSensitive = true) + public function testAppend($expected, $str, $string) { $s = new Str($str); - $this->assertEquals($expected, $s->startsWithAny($substrings, $caseSensitive), $str); + $this->assertEquals($expected, $s->append($string)); } - public function startsWithProviderAny() + public function appendProvider() { return [ - [true, 'foo bars', ['foo bar']], - [true, 'FOO bars', ['foo bar'], false], - [true, 'FOO bars', ['foo bar', 'foo BAR'], false], - [true, 'FÒÔ bàřs', ['foo bar', 'fòô bàř'], false], - [true, 'fòô bàřs', ['foo bar', 'fòô BÀŘ'], false], - [false, 'foo bar', ['bar']], - [false, 'foo bar', ['foo bars']], - [false, 'FOO bar', ['foo bars']], - [false, 'FOO bars', ['foo BAR']], - [false, 'FÒÔ bàřs', ['fòô bàř'], true], - [false, 'fòô bàřs', ['fòô BÀŘ'], true], - [false, 'anything', []] + ['foobar', 'foo', 'bar'], + ['fòôbàř', 'fòô', 'bàř'] ]; } /** - * @dataProvider endsWithProvider() + * @dataProvider prependProvider() * @param $expected * @param $str - * @param $substring - * @param bool $caseSensitive + * @param $string */ - public function testEndsWith($expected, $str, $substring, $caseSensitive = true) + public function testPrepend($expected, $str, $string) { $s = new Str($str); - $this->assertEquals($expected, $s->endsWith($substring, $caseSensitive), $str); + $this->assertEquals($expected, $s->prepend($string)); } - public function endsWithProvider() + public function prependProvider() { return [ - [true, 'foo bars', 'o bars'], - [true, 'FOO bars', 'o bars', false], - [true, 'FOO bars', 'o BARs', false], - [true, 'FÒÔ bàřs', 'ô bàřs', false], - [true, 'fòô bàřs', 'ô BÀŘs', false], - [false, 'foo bar', 'foo'], - [false, 'foo bar', 'foo bars'], - [false, 'FOO bar', 'foo bars'], - [false, 'FOO bars', 'foo BARS'], - [false, 'FÒÔ bàřs', 'fòô bàřs', true], - [false, 'fòô bàřs', 'fòô BÀŘS', true], + ['foobar', 'bar', 'foo'], + ['fòôbàř', 'bàř', 'fòô'] ]; } /** - * @dataProvider endsWithAnyProvider() + * @dataProvider AtProvider * @param $expected * @param $str - * @param $substrings - * @param bool $caseSensitive + * @param $pos */ - public function testEndsWithAny($expected, $str, $substrings, $caseSensitive = true) + public function testAt($expected, $str, $pos) { $s = new Str($str); - $this->assertEquals($expected, $s->endsWithAny($substrings, $caseSensitive), $str); + $this->assertEquals($expected, $s->at($pos)); } - public function endsWithAnyProvider() + + public function AtProvider() { return [ - [true, 'foo bars', ['foo', 'o bars']], - [true, 'FOO bars', ['foo', 'o bars'], false], - [true, 'FOO bars', ['foo', 'o BARs'], false], - [true, 'FÒÔ bàřs', ['foo', 'ô bàřs'], false], - [true, 'fòô bàřs', ['foo', 'ô BÀŘs'], false], - [false, 'foo bar', ['foo']], - [false, 'foo bar', ['foo', 'foo bars']], - [false, 'FOO bar', ['foo', 'foo bars']], - [false, 'FOO bars', ['foo', 'foo BARS']], - [false, 'FÒÔ bàřs', ['fòô', 'fòô bàřs'], true], - [false, 'fòô bàřs', ['fòô', 'fòô BÀŘS'], true], - [false, 'anything', []] - ]; - } - - /** - * real uuid4 generator test - */ - public function testIsUUIDv4_Real() - { - for ($i=0;$i<=255;$i++) { - $uuid = (string)\Ramsey\Uuid\Uuid::uuid4(); - $s = new Str($uuid); - $this->assertTrue($s->isUUIDv4(), $uuid); - } + ['H', 'Hello world', 0], + ['e', 'Hello world', 1], + ['d', 'Hello world', -1], + ['世', '世', -1], + ['世', '世', 0], + ['', '', 0], + ['', '', -1], + ['', '', 1], + ]; } + /** - * @dataProvider isUUIDv4Provider() + * @dataProvider charsProvider() * @param $expected * @param $str */ - public function testIsUUIDv4($expected, $str) + public function testChars($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->isUUIDv4(), $str); + $this->assertInternalType('array', $expected); + $this->assertEquals($expected, $s->chars()); } - public function isUUIDv4Provider() + + public function charsProvider() { return [ - [true, 'ae815123-537f-4eb3-a9b8-35881c29e1ac'], - [true, '4724393e-b496-4d94-bdae-96004abbc5e4'], - [true, '9c360134-770d-4284-abab-6e1257dee973'], - [false, '76d7cac8-1bd7-11e8-accf-0ed5f89f718b'], - [false, 'f89cdf3a-1bd7-11e8-accf-0ed5f89f718b'], - [false, 'b3467be4-1bd7-11e8-accf-0ed5f89f718b'], + [[], ''], + [['T', 'e', 's', 't'], 'Test'], + [['F', 'ò', 'ô', ' ', 'B', 'à', 'ř'], 'Fòô Bàř'] ]; } /** - * @dataProvider hasLowerCaseProvider() + * @dataProvider lengthProvider() * @param $expected * @param $str */ - public function testHasLowerCase($expected, $str) + public function testLength($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->hasLowerCase(), $str); + $this->assertEquals($expected, $s->length()); } - public function hasLowerCaseProvider() + public function lengthProvider() { return [ - [false, ''], - [true, 'foobar'], - [false, 'FOO BAR'], - [true, 'fOO BAR'], - [true, 'foO BAR'], - [true, 'FOO BAr'], - [true, 'Foobar'], - [false, 'FÒÔBÀŘ'], - [true, 'fòôbàř'], - [true, 'fòôbàř2'], - [true, 'Fòô bàř'], - [true, 'fòôbÀŘ'], + [11, ' foo bar '], + [1, 'f'], + [0, ''], + [7, 'fòô bàř'] ]; } /** - * @dataProvider hasUpperCaseProvider() + * @dataProvider firstProvider() * @param $expected * @param $str + * @param $n */ - public function testHasUpperCase($expected, $str) + public function testFirst($expected, $str, $n) { $s = new Str($str); - $this->assertEquals($expected, $s->hasUpperCase(), $str); + $this->assertEquals($expected, $s->first($n)); } - public function hasUpperCaseProvider() + + public function firstProvider() { return [ - [false, ''], - [true, 'FOOBAR'], - [false, 'foo bar'], - [true, 'Foo bar'], - [true, 'FOo bar'], - [true, 'foo baR'], - [true, 'fOOBAR'], - [false, 'fòôbàř'], - [true, 'FÒÔBÀŘ'], - [true, 'FÒÔBÀŘ2'], - [true, 'fÒÔ BÀŘ'], - [true, 'FÒÔBàř'], + ['', 'foo bar', -5], + ['', 'foo bar', 0], + ['f', 'foo bar', 1], + ['foo', 'foo bar', 3], + ['foo bar', 'foo bar', 7], + ['foo bar', 'foo bar', 8], + ['', 'fòô bàř', -5], + ['', 'fòô bàř', 0], + ['f', 'fòô bàř', 1], + ['fòô', 'fòô bàř', 3], + ['fòô bàř', 'fòô bàř', 7], + ['fòô bàř', 'fòô bàř', 8], ]; } /** - * @dataProvider matchesPatternProvider() + * @dataProvider lastProvider() * @param $expected * @param $str - * @param $pattern + * @param $n */ - public function testMatchesPattern($expected, $str, $pattern) + public function testLast($expected, $str, $n) { $s = new Str($str); - $this->assertEquals($expected, $s->matchesPattern($pattern), $str); + $this->assertEquals($expected, $s->last($n)); } - public function matchesPatternProvider() + + public function lastProvider() { return [ - [true, 'FOOBAR', '.*FOO'], - [false, 'foo bar', '.* bar'], - [true, 'Foo bar', '.* ba'], - [true, 'FOo bar', '.*Oo'], - [true, 'foo baR', '.*aR'], - [true, 'fOOBAR', '.*OBA'], - [false, 'fòôbàř', '.*foo'], + ['', 'foo bar', -5], + ['', 'foo bar', 0], + ['r', 'foo bar', 1], + ['bar', 'foo bar', 3], + ['foo bar', 'foo bar', 7], + ['foo bar', 'foo bar', 8], + ['', 'fòô bàř', -5], + ['', 'fòô bàř', 0], + ['ř', 'fòô bàř', 1], + ['bàř', 'fòô bàř', 3], + ['fòô bàř', 'fòô bàř', 7], + ['fòô bàř', 'fòô bàř', 8], ]; } /** - * @dataProvider isAlphaProvider() + * @dataProvider indexOfProvider() * @param $expected - * @param $str + * @param $haystack + * @param $needle + * @param int $offset */ - public function testIsAlpha($expected, $str) + public function testIndexOf($expected, $haystack, $needle, $offset = 0) { - $s = new Str($str); - $this->assertEquals($expected, $s->isAlpha(), $str); + $s = new Str($haystack); + $this->assertEquals($expected, $s->indexOf($needle, $offset)); } - public function isAlphaProvider() + + public function indexOfProvider() { return [ - [true, ''], - [true, 'foobar'], - [false, 'foo bar'], - [false, 'foobar2'], - [true, 'fòôbàř'], - [false, 'fòô bàř'], - [false, 'fòôbàř2'], - [true, 'ҠѨњфгШ'], - [false, 'ҠѨњ¨ˆфгШ'], - [true, '丹尼爾'] + [6, 'foo & bar', 'bar'], + [6, 'foo & bar', 'bar', 0], + [-1, 'foo & bar', 'baz'], + [-1, 'foo & bar', 'baz', 0], + [0, 'foo & bar & foo', 'foo', 0], + [12, 'foo & bar & foo', 'foo', 5], + [12, 'foo & bar & foo', 'foo', -5], + [6, 'fòô & bàř', 'bàř', 0], + [-1, 'fòô & bàř', 'baz', 0], + [0, 'fòô & bàř & fòô', 'fòô', 0], + [12, 'fòô & bàř & fòô', 'fòô', 5], + [12, 'fòô & bàř & fòô', 'fòô', -5], ]; } /** - * @dataProvider isAlphanumericProvider() + * @dataProvider indexOfLastProvider() * @param $expected - * @param $str + * @param $haystack + * @param $needle + * @param int $offset */ - public function testIsAlphanumeric($expected, $str) + public function testIndexOfLast($expected, $haystack, $needle, $offset = 0) { - $s = new Str($str); - $this->assertEquals($expected, $s->isAlphanumeric(), $str); + $s = new Str($haystack); + $this->assertEquals($expected, $s->indexOfLast($needle, $offset)); } - public function isAlphanumericProvider() + + public function indexOfLastProvider() { return [ - [true, ''], - [true, 'foobar1'], - [false, 'foo bar'], - [false, 'foobar2"'], - [false, "\nfoobar\n"], - [true, 'fòôbàř1'], - [false, 'fòô bàř'], - [false, 'fòôbàř2"'], - [true, 'ҠѨњфгШ'], - [false, 'ҠѨњ¨ˆфгШ'], - [true, '丹尼爾111'], - [true, 'دانيال1'], - [false, 'دانيال1 '] + [6, 'foo & bar', 'bar'], + [6, 'foo & bar', 'bar', 0], + [-1, 'foo & bar', 'baz'], + [-1, 'foo & bar', 'baz', 0], + [12, 'foo & bar & foo', 'foo', 0], + [12, 'foo & bar & foo', 'foo', -5], + [6, 'fòô & bàř', 'bàř', 0], + [-1, 'fòô & bàř', 'baz', 0], + [12, 'fòô & bàř & fòô', 'fòô', 0], + [12, 'fòô & bàř & fòô', 'fòô', -5], + [-1, 'q', 'q', -5], + [-1, '', '', 0], + [-1, 'q', '', 0], + [-1, '', 'q', 0], + [-1, 'q', 'q', 5], ]; } /** - * @dataProvider isBase64Provider() + * @dataProvider countSubstrProvider() * @param $expected * @param $str + * @param $substring + * @param bool $caseSensitive */ - public function testIsBase64($expected, $str) + public function testCountSubstr($expected, $str, $substring, $caseSensitive = true) { $s = new Str($str); - $this->assertEquals($expected, $s->isBase64(), $str); + $this->assertEquals($expected, $s->countSubstr($substring, $caseSensitive)); } - public function isBase64Provider() + + public function countSubstrProvider() { return [ - [false, ' '], - [true, ''], - [true, base64_encode('FooBar') ], - [true, base64_encode(' ') ], - [true, base64_encode('FÒÔBÀŘ') ], - [true, base64_encode('συγγραφέας') ], - [false, 'Foobar'], + [0, '', 'foo'], + [0, 'foo', 'bar'], + [1, 'foo bar', 'foo'], + [2, 'foo bar', 'o'], + [0, '', 'fòô'], + [0, 'fòô', 'bàř'], + [1, 'fòô bàř', 'fòô'], + [2, 'fôòô bàř', 'ô'], + [0, 'fÔÒÔ bàř', 'ô'], + [0, 'foo', 'BAR', false], + [1, 'foo bar', 'FOo', false], + [2, 'foo bar', 'O', false], + [1, 'fòô bàř', 'fÒÔ', false], + [2, 'fôòô bàř', 'Ô', false], + [1, 'συγγραφέας', 'Σ', false] ]; } /** - * @dataProvider isBlankProvider() + * @dataProvider containsAllProvider() * @param $expected - * @param $str + * @param $haystack + * @param $needles + * @param bool $caseSensitive */ - public function testIsBlank($expected, $str) + public function testContainsAll($expected, $haystack, $needles, $caseSensitive = true) { - $s = new Str($str); - $this->assertEquals($expected, $s->isBlank(), $str); + $s = new Str($haystack); + $this->assertEquals($expected, $s->containsAll($needles, $caseSensitive), $haystack); } - public function isBlankProvider() + + public function containsAllProvider() { - return [ - [true, ''], - [true, ' '], - [true, "\n\t "], - [true, "\n\t \v\f"], - [false, "\n\t a \v\f"], - [false, "\n\t ' \v\f"], - [false, "\n\t 2 \v\f"], - [true, ''], - [true, ' '], // no-break space (U+00A0) - [true, '           '], // spaces U+2000 to U+200A - [true, ' '], // narrow no-break space (U+202F) - [true, ' '], // medium mathematical space (U+205F) - [true, ' '], // ideographic space (U+3000) - [false, ' z'], - [false, ' 1'], + // One needle + $singleNeedle = array_map(function ($array) { + $array[2] = [$array[2]]; + return $array; + }, $this->ContainsProvider()); + $provider = [ + // One needle + [false, 'Str contains foo bar', []], + // Multiple needles + [true, 'Str contains foo bar', ['foo', 'bar']], + [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*', '&^%']], + [true, 'Ο συγγραφέας είπε', ['συγγρ', 'αφέας']], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å´¥', '©'], true], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å˚ ', '∆'], true], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['øœ', '¬'], true], + [false, 'Str contains foo bar', ['Foo', 'bar']], + [false, 'Str contains foo bar', ['foobar', 'bar']], + [false, 'Str contains foo bar', ['foo bar ', 'bar']], + [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' συγγραφ '], true], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßå˚', ' ß '], true], + [true, 'Str contains foo bar', ['Foo bar', 'bar'], false], + [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*&^%', '*&^%'], false], + [true, 'Ο συγγραφέας είπε', ['ΣΥΓΓΡΑΦΈΑΣ', 'ΑΦΈΑ'], false], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å´¥©', '¥©'], false], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å˚ ∆', ' ∆'], false], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['ØŒ¬', 'Œ'], false], + [false, 'Str contains foo bar', ['foobar', 'none'], false], + [false, 'Str contains foo bar', ['foo bar ', ' ba'], false], + [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' ραφέ '], false], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßÅ˚', ' Å˚ '], false], ]; + return array_merge($singleNeedle, $provider); } /** - * @dataProvider isHexadecimalProvider() + * @dataProvider containsAnyProvider() * @param $expected - * @param $str + * @param $haystack + * @param $needles + * @param bool $caseSensitive */ - public function testIsHexadecimal($expected, $str) + public function testContainsAny($expected, $haystack, $needles, $caseSensitive = true) { - $s = new Str($str); - $this->assertEquals($expected, $s->isHexadecimal(), $str); + $s = new Str($haystack); + $this->assertEquals($expected, $s->containsAny($needles, $caseSensitive), $haystack); } - public function isHexadecimalProvider() + public function containsAnyProvider() { - return [ - [true, ''], - [true, 'abcdef'], - [true, 'ABCDEF'], - [true, '0123456789'], - [true, '0123456789AbCdEf'], - [false, '0123456789x'], - [false, 'ABCDEFx'], - [true, 'abcdef'], - [true, 'ABCDEF'], - [true, '0123456789'], - [true, '0123456789AbCdEf'], - [false, '0123456789x'], - [false, 'ABCDEFx'], + // One needle + $singleNeedle = array_map(function ($array) { + $array[2] = [$array[2]]; + return $array; + }, $this->ContainsProvider()); + $provider = [ + // No needles + [false, 'Str contains foo bar', []], + // Multiple needles + [true, 'Str contains foo bar', ['foo', 'bar']], + [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*', '&^%']], + [true, 'Ο συγγραφέας είπε', ['συγγρ', 'αφέας']], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å´¥', '©'], true], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['å˚ ', '∆'], true], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['øœ', '¬'], true], + [false, 'Str contains foo bar', ['Foo', 'Bar']], + [false, 'Str contains foo bar', ['foobar', 'bar ']], + [false, 'Str contains foo bar', ['foo bar ', ' foo']], + [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' συγγραφ '], true], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßå˚', ' ß '], true], + [true, 'Str contains foo bar', ['Foo bar', 'bar'], false], + [true, '12398!@(*%!@# @!%#*&^%', [' @!%#*&^%', '*&^%'], false], + [true, 'Ο συγγραφέας είπε', ['ΣΥΓΓΡΑΦΈΑΣ', 'ΑΦΈΑ'], false], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å´¥©', '¥©'], false], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['Å˚ ∆', ' ∆'], false], + [true, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', ['ØŒ¬', 'Œ'], false], + [false, 'Str contains foo bar', ['foobar', 'none'], false], + [false, 'Str contains foo bar', ['foo bar ', ' ba '], false], + [false, 'Ο συγγραφέας είπε', [' συγγραφέας ', ' ραφέ '], false], + [false, 'å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬', [' ßÅ˚', ' Å˚ '], false], ]; + return array_merge($singleNeedle, $provider); } /** - * @dataProvider isJsonProvider() - * @param $str + * @dataProvider startsWithProvider() * @param $expected + * @param $str + * @param $substring + * @param bool $caseSensitive */ - public function testIsJson($expected, $str) + public function testStartsWith($expected, $str, $substring, $caseSensitive = true) { $s = new Str($str); - $this->assertEquals($expected, $s->isJson(), $str); + $this->assertEquals($expected, $s->startsWith($substring, $caseSensitive), $str); } - public function isJsonProvider() + public function startsWithProvider() { return [ - [false, ''], - [false, ' '], - [true, 'null'], - [true, 'true'], - [true, 'false'], - [true, '[]'], - [true, '{}'], - [true, '123'], - [true, '{"foo": "bar"}'], - [false, '{"foo":"bar",}'], - [false, '{"foo"}'], - [true, '["foo"]'], - [false, '{"foo": "bar"]'], - [true, '123'], - [true, '{"fòô": "bàř"}'], - [false, '{"fòô":"bàř",}'], - [false, '{"fòô"}'], - [false, '["fòô": "bàř"]'], - [true, '["fòô"]'], - [false, '{"fòô": "bàř"]'], + [true, 'foo bars', 'foo bar'], + [true, 'FOO bars', 'foo bar', false], + [true, 'FOO bars', 'foo BAR', false], + [true, 'FÒÔ bàřs', 'fòô bàř', false], + [true, 'fòô bàřs', 'fòô BÀŘ', false], + [false, 'foo bar', 'bar'], + [false, 'foo bar', 'foo bars'], + [false, 'FOO bar', 'foo bars'], + [false, 'FOO bars', 'foo BAR'], + [false, 'FÒÔ bàřs', 'fòô bàř', true], + [false, 'fòô bàřs', 'fòô BÀŘ', true], ]; } /** - * @dataProvider isLowerCaseProvider() + * @dataProvider startsWithProviderAny() * @param $expected * @param $str + * @param $substrings + * @param bool $caseSensitive */ - public function testIsLowerCase($expected, $str) + public function testStartsWithAny($expected, $str, $substrings, $caseSensitive = true) { $s = new Str($str); - $this->assertEquals($expected, $s->isLowerCase(), $str); + $this->assertEquals($expected, $s->startsWithAny($substrings, $caseSensitive), $str); } - public function isLowerCaseProvider() + public function startsWithProviderAny() { return [ - [true, ''], - [true, 'foobar'], - [false, 'foo bar'], - [false, 'Foobar'], - [true, 'fòôbàř'], - [false, 'fòôbàř2'], - [false, 'fòô bàř'], - [false, 'fòôbÀŘ'], + [true, 'foo bars', ['foo bar']], + [true, 'FOO bars', ['foo bar'], false], + [true, 'FOO bars', ['foo bar', 'foo BAR'], false], + [true, 'FÒÔ bàřs', ['foo bar', 'fòô bàř'], false], + [true, 'fòô bàřs', ['foo bar', 'fòô BÀŘ'], false], + [false, 'foo bar', ['bar']], + [false, 'foo bar', ['foo bars']], + [false, 'FOO bar', ['foo bars']], + [false, 'FOO bars', ['foo BAR']], + [false, 'FÒÔ bàřs', ['fòô bàř'], true], + [false, 'fòô bàřs', ['fòô BÀŘ'], true], + [false, 'anything', []] ]; } /** - * @dataProvider isSerializedProvider() + * @dataProvider endsWithProvider() * @param $expected * @param $str + * @param $substring + * @param bool $caseSensitive */ - public function testIsSerialized($expected, $str) + public function testEndsWith($expected, $str, $substring, $caseSensitive = true) { $s = new Str($str); - $this->assertEquals($expected, $s->isSerialized(), $str); + $this->assertEquals($expected, $s->endsWith($substring, $caseSensitive), $str); } - public function isSerializedProvider() + public function endsWithProvider() { return [ - [false, ''], - [true, 'a:1:{s:3:"foo";s:3:"bar";}'], - [false, 'a:1:{s:3:"foo";s:3:"bar"}'], - [true, serialize(['foo' => 'bar'])], - [true, 'a:1:{s:5:"fòô";s:5:"bàř";}'], - [false, 'a:1:{s:5:"fòô";s:5:"bàř"}'], - [true, serialize(['fòô' => 'bár'])], + [true, 'foo bars', 'o bars'], + [true, 'FOO bars', 'o bars', false], + [true, 'FOO bars', 'o BARs', false], + [true, 'FÒÔ bàřs', 'ô bàřs', false], + [true, 'fòô bàřs', 'ô BÀŘs', false], + [false, 'foo bar', 'foo'], + [false, 'foo bar', 'foo bars'], + [false, 'FOO bar', 'foo bars'], + [false, 'FOO bars', 'foo BARS'], + [false, 'FÒÔ bàřs', 'fòô bàřs', true], + [false, 'fòô bàřs', 'fòô BÀŘS', true], ]; } /** - * @dataProvider isUpperCaseProvider() + * @dataProvider endsWithAnyProvider() * @param $expected * @param $str + * @param $substrings + * @param bool $caseSensitive */ - public function testIsUpperCase($expected, $str) + public function testEndsWithAny($expected, $str, $substrings, $caseSensitive = true) { $s = new Str($str); - $this->assertEquals($expected, $s->isUpperCase(), $str); + $this->assertEquals($expected, $s->endsWithAny($substrings, $caseSensitive), $str); } - public function isUpperCaseProvider() + public function endsWithAnyProvider() { return [ - [true, ''], - [true, 'FOOBAR'], - [false, 'FOO BAR'], - [false, 'fOOBAR'], - [true, 'FÒÔBÀŘ'], - [false, 'FÒÔBÀŘ2'], - [false, 'FÒÔ BÀŘ'], - [false, 'FÒÔBàř'], + [true, 'foo bars', ['foo', 'o bars']], + [true, 'FOO bars', ['foo', 'o bars'], false], + [true, 'FOO bars', ['foo', 'o BARs'], false], + [true, 'FÒÔ bàřs', ['foo', 'ô bàřs'], false], + [true, 'fòô bàřs', ['foo', 'ô BÀŘs'], false], + [false, 'foo bar', ['foo']], + [false, 'foo bar', ['foo', 'foo bars']], + [false, 'FOO bar', ['foo', 'foo bars']], + [false, 'FOO bars', ['foo', 'foo BARS']], + [false, 'FÒÔ bàřs', ['fòô', 'fòô bàřs'], true], + [false, 'fòô bàřs', ['fòô', 'fòô BÀŘS'], true], + [false, 'anything', []] ]; } /** - * @dataProvider toBooleanProvider() + * @dataProvider padLeftProvider() * @param $expected * @param $str + * @param $length + * @param $padStr */ - public function testToBoolean($expected, $str) + public function testPadLeft($expected, $str, $length, $padStr = ' ') { $s = new Str($str); - $this->assertEquals($expected, $s->toBoolean(), $str); + $this->assertEquals($expected, $s->padLeft($length, $padStr)); } - public function toBooleanProvider() + public function padLeftProvider() { return [ - [true, 'true'], - [true, '1'], - [true, 'on'], - [true, 'ON'], - [true, 'yes'], - [true, '999'], - [false, 'false'], - [false, '0'], - [false, 'off'], - [false, 'OFF'], - [false, 'no'], - [false, '-999'], - [false, ''], - [false, ' '], - [false, '  '] // narrow no-break space (U+202F) + [' foo bar', 'foo bar', 9], + ['_*foo bar', 'foo bar', 9, '_*'], + ['_*_foo bar', 'foo bar', 10, '_*'], + [' fòô bàř', 'fòô bàř', 9, ' '], + ['¬øfòô bàř', 'fòô bàř', 9, '¬ø'], + ['¬ø¬fòô bàř', 'fòô bàř', 10, '¬ø'], + ['¬ø¬øfòô bàř', 'fòô bàř', 11, '¬ø'], ]; } /** - * @dataProvider SubstrProvider + * @dataProvider padRightProvider() * @param $expected * @param $str - * @param int $start - * @param int $length + * @param $length + * @param $padStr */ - public function testSubstr($expected, $str, $start = 0, $length = 1) + public function testPadRight($expected, $str, $length, $padStr = ' ') { $s = new Str($str); - $this->assertEquals($expected, $s->substr($start, $length)); + $this->assertEquals($expected, $s->padRight($length, $padStr)); } - - public function SubstrProvider() + public function padRightProvider() { return [ - ['Hel', 'Hello world', 0, 3], - ['H世', 'H世ello world', 0, 2], - [' H世', ' H世', 1, 4], - ['123', '000123000', 3, 3], + ['foo bar ', 'foo bar', 9], + ['foo bar_*', 'foo bar', 9, '_*'], + ['foo bar_*_', 'foo bar', 10, '_*'], + ['fòô bàř ', 'fòô bàř', 9, ' '], + ['fòô bàř¬ø', 'fòô bàř', 9, '¬ø'], + ['fòô bàř¬ø¬', 'fòô bàř', 10, '¬ø'], + ['fòô bàř¬ø¬ø', 'fòô bàř', 11, '¬ø'], ]; } /** - * @dataProvider EnsureRightProvider - * @param array $inp - * @param string $out - */ - public function testEnsureRight(array $inp, string $out) - { - $s = new Str($inp[0]); - $affix = $inp[1]; - $this->assertEquals($out, $s->ensureRight($affix)); - } - - public function EnsureRightProvider() - { - return - [ - [ - ['Hello world', '<<_right'], - 'Hello world<<_right', - ], - [ - ['Hello world<<_right', '<<_right'], - 'Hello world<<_right', - ], - [ - ['q', 'q'], - 'q', - ], - [ - ['qq', 'q'], - 'qq', - ], - [ - ['Hello, 世界', '世'], - 'Hello, 世界世', - ], - [ - ['Hello, 世界界', '界'], - 'Hello, 世界界', - ], - [ - ['世', '世'], - '世', - ], - ]; - } - - /** - * @dataProvider EnsureLeftProvider - * @param array $inp - * @param string $out - */ - public function testEnsureLeft(array $inp, string $out) - { - $s = new Str($inp[0]); - $affix = $inp[1]; - $this->assertEquals($out, $s->ensureLeft($affix)); - } - - public function EnsureLeftProvider() - { - return - [ - [ - ['Hello world', '_left>>'], - '_left>>Hello world', - ], - [ - ['_left>>Hello world', '_left>>'], - '_left>>Hello world', - ], - [ - ['q', 'q'], - 'q', - ], - [ - ['qq', 'q'], - 'qq', - ], - [ - ['Hello, 世界', '界'], - '界Hello, 世界', - ], - [ - ['界Hello, 世界', '界'], - '界Hello, 世界', - ], - [ - ['世', '世'], - '世', - ], - ]; - } - - /** - * @dataProvider AtProvider + * @dataProvider padBothProvider() * @param $expected * @param $str - * @param $pos + * @param $length + * @param $padStr */ - public function testAt($expected, $str, $pos) + public function testPadBoth($expected, $str, $length, $padStr = ' ') { $s = new Str($str); - $this->assertEquals($expected, $s->at($pos)); + $this->assertEquals($expected, $s->padBoth($length, $padStr)); } - - public function AtProvider() + public function padBothProvider() { return [ - ['H', 'Hello world', 0], - ['e', 'Hello world', 1], - ['d', 'Hello world', -1], - ['世', '世', -1], - ['世', '世', 0], - ['', '', 0], - ['', '', -1], - ['', '', 1], + ['foo bar ', 'foo bar', 8], + [' foo bar ', 'foo bar', 9, ' '], + ['fòô bàř ', 'fòô bàř', 8, ' '], + [' fòô bàř ', 'fòô bàř', 9, ' '], + ['fòô bàř¬', 'fòô bàř', 8, '¬ø'], + ['¬fòô bàř¬', 'fòô bàř', 9, '¬ø'], + ['¬fòô bàř¬ø', 'fòô bàř', 10, '¬ø'], + ['¬øfòô bàř¬ø', 'fòô bàř', 11, '¬ø'], + ['¬fòô bàř¬ø', 'fòô bàř', 10, '¬øÿ'], + ['¬øfòô bàř¬ø', 'fòô bàř', 11, '¬øÿ'], + ['¬øfòô bàř¬øÿ', 'fòô bàř', 12, '¬øÿ'] ]; } /** - * @dataProvider charsProvider() + * @dataProvider insertProvider() * @param $expected * @param $str + * @param $substring + * @param $index */ - public function testChars($expected, $str) + public function testInsert($expected, $str, $substring, $index) { $s = new Str($str); - $this->assertInternalType('array', $expected); - $this->assertEquals($expected, $s->chars()); + $this->assertEquals($expected, $s->insert($substring, $index)); } - - public function charsProvider() + public function insertProvider() { return [ - [[], ''], - [['T', 'e', 's', 't'], 'Test'], - [['F', 'ò', 'ô', ' ', 'B', 'à', 'ř'], 'Fòô Bàř'] + ['foo bar', 'oo bar', 'f', 0], + ['foo bar', 'f bar', 'oo', 1], + ['f baroo', 'f bar', 'oo', 20], + ['foo bar', 'foo ba', 'r', 6], + ['fòôbàř', 'fòôbř', 'à', 4], + ['fòô bàř', 'òô bàř', 'f', 0], + ['fòô bàř', 'f bàř', 'òô', 1], + ['fòô bàř', 'fòô bà', 'ř', 6] ]; } /** - * @dataProvider firstProvider() + * @dataProvider removeLeftProvider() * @param $expected * @param $str - * @param $n + * @param $substring */ - public function testFirst($expected, $str, $n) + public function testRemoveLeft($expected, $str, $substring) { $s = new Str($str); - $this->assertEquals($expected, $s->first($n)); + $this->assertEquals($expected, $s->removeLeft($substring)); } - - public function firstProvider() + public function removeLeftProvider() { + $s = new Str('foo bar'); return [ - ['', 'foo bar', -5], - ['', 'foo bar', 0], - ['f', 'foo bar', 1], - ['foo', 'foo bar', 3], - ['foo bar', 'foo bar', 7], - ['foo bar', 'foo bar', 8], - ['', 'fòô bàř', -5], - ['', 'fòô bàř', 0], - ['f', 'fòô bàř', 1], - ['fòô', 'fòô bàř', 3], - ['fòô bàř', 'fòô bàř', 7], - ['fòô bàř', 'fòô bàř', 8], + ['foo bar', 'foo bar', ''], + ['oo bar', 'foo bar', 'f'], + ['bar', 'foo bar', 'foo '], + ['foo bar', 'foo bar', 'oo'], + ['foo bar', 'foo bar', 'oo bar'], + ['oo bar', 'foo bar', (string)$s->at(0)], + ['fòô bàř', 'fòô bàř', ''], + ['òô bàř', 'fòô bàř', 'f'], + ['bàř', 'fòô bàř', 'fòô '], + ['fòô bàř', 'fòô bàř', 'òô'], + ['fòô bàř', 'fòô bàř', 'òô bàř'] ]; } /** - * @dataProvider lastProvider() + * @dataProvider removeRightProvider() * @param $expected * @param $str - * @param $n + * @param $substring */ - public function testLast($expected, $str, $n) + public function testRemoveRight($expected, $str, $substring) { $s = new Str($str); - $this->assertEquals($expected, $s->last($n)); + $this->assertEquals($expected, $s->removeRight($substring)); } - - public function lastProvider() + public function removeRightProvider() { + $s = new Str('foo bar'); return [ - ['', 'foo bar', -5], - ['', 'foo bar', 0], - ['r', 'foo bar', 1], - ['bar', 'foo bar', 3], - ['foo bar', 'foo bar', 7], - ['foo bar', 'foo bar', 8], - ['', 'fòô bàř', -5], - ['', 'fòô bàř', 0], - ['ř', 'fòô bàř', 1], - ['bàř', 'fòô bàř', 3], - ['fòô bàř', 'fòô bàř', 7], - ['fòô bàř', 'fòô bàř', 8], + ['foo bar', 'foo bar', ''], + ['foo ba', 'foo bar', 'r'], + ['foo', 'foo bar', ' bar'], + ['foo bar', 'foo bar', 'ba'], + ['foo bar', 'foo bar', 'foo ba'], + ['foo ba', 'foo bar', (string)$s->last()], + ['fòô bàř', 'fòô bàř', ''], + ['fòô bà', 'fòô bàř', 'ř'], + ['fòô', 'fòô bàř', ' bàř'], + ['fòô bàř', 'fòô bàř', 'bà'], + ['fòô bàř', 'fòô bàř', 'fòô bà'] ]; } /** - * @dataProvider toLowerCaseProvider() + * @dataProvider repeatProvider() * @param $expected * @param $str + * @param $multiplier */ - public function testToLowerCase($expected, $str) + public function testRepeat($expected, $str, $multiplier) { $s = new Str($str); - $this->assertEquals($expected, $s->toLowerCase()); + $this->assertEquals($expected, $s->repeat($multiplier)); } - public function toLowerCaseProvider() + public function repeatProvider() { return [ - ['foo bar', 'FOO BAR'], - [' foo_bar ', ' FOO_bar '], - ['fòô bàř', 'FÒÔ BÀŘ'], - [' fòô_bàř ', ' FÒÔ_bàř '], - ['αυτοκίνητο', 'ΑΥΤΟΚΊΝΗΤΟ'], + ['', 'foo', 0], + ['foo', 'foo', 1], + ['foofoo', 'foo', 2], + ['foofoofoo', 'foo', 3], + ['fòô', 'fòô', 1], + ['fòôfòô', 'fòô', 2], + ['fòôfòôfòô', 'fòô', 3] ]; } /** - * @dataProvider toUpperCaseProvider() + * @dataProvider reverseProvider() * @param $expected * @param $str */ - public function testToUpperCase($expected, $str) + public function testReverse($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->toUpperCase()); + $this->assertEquals($expected, $s->reverse()); } - public function toUpperCaseProvider() + public function reverseProvider() { return [ - ['FOO BAR', 'foo bar'], - [' FOO_BAR ', ' FOO_bar '], - ['FÒÔ BÀŘ', 'fòô bàř'], - [' FÒÔ_BÀŘ ', ' FÒÔ_bàř '], - ['ΑΥΤΟΚΊΝΗΤΟ', 'αυτοκίνητο'], + ['', ''], + ['raboof', 'foobar'], + ['řàbôòf', 'fòôbàř'], + ['řàb ôòf', 'fòô bàř'], + ['∂∆ ˚åß', 'ßå˚ ∆∂'] ]; } /** - * @dataProvider appendProvider() - * @param $expected + * @dataProvider shuffleProvider() * @param $str - * @param $string */ - public function testAppend($expected, $str, $string) + public function testShuffle($str) { $s = new Str($str); - $this->assertEquals($expected, $s->append($string)); + $oldValues = $s->chars(); + + + $result = $s->shuffle(); + $newValues = $s->chars(); + + $countOld = array_count_values($oldValues); + $countNew = array_count_values($newValues); + + // We'll make sure that the chars are present after shuffle + $this->assertEquals($countOld, $countNew); + $this->assertEmpty(array_diff($countOld, $countNew)); } - public function appendProvider() + public function shuffleProvider() { return [ - ['foobar', 'foo', 'bar'], - ['fòôbàř', 'fòô', 'bàř'] + ['foo bar'], + ['∂∆ ˚åß'], + ['å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬'] ]; } /** - * @dataProvider prependProvider() + * @dataProvider betweenProvider() * @param $expected * @param $str - * @param $string + * @param $start + * @param $end + * @param int $offset */ - public function testPrepend($expected, $str, $string) + public function testBetween($expected, $str, $start, $end, $offset = 0) { $s = new Str($str); - $this->assertEquals($expected, $s->prepend($string)); + $this->assertEquals($expected, $s->between($start, $end, $offset), $str); } - public function prependProvider() + public function betweenProvider() { return [ - ['foobar', 'bar', 'foo'], - ['fòôbàř', 'bàř', 'fòô'] + ['Acme', '/Acme/', '/', '/'], + ['', 'foo', '{', '}'], + ['', '{foo', '{', '}'], + ['foo', '{foo}', '{', '}'], + ['{foo', '{{foo}', '{', '}'], + ['', '{}foo}', '{', '}'], + ['foo', '}{foo}', '{', '}'], + ['foo', 'A description of {foo} goes here', '{', '}'], + ['bar', '{foo} and {bar}', '{', '}', 1], + ['', 'fòô', '{', '}', 0], + ['', '{fòô', '{', '}', 0], + ['fòô', '{fòô}', '{', '}', 0], + ['{fòô', '{{fòô}', '{', '}', 0], + ['', '{}fòô}', '{', '}', 0], + ['fòô', '}{fòô}', '{', '}', 0], + ['fòô', 'A description of {fòô} goes here', '{', '}', 0], + ['bàř', '{fòô} and {bàř}', '{', '}', 1], + ['\w\N ^$%#', '\b\w\N ^$%#{fòô} . and {bàř}', 'b', '{', 1], ]; } /** - * @dataProvider ReplaceProvider - * @param $params - * @param $expected - */ - public function testReplace($params, $expected) - { - $s = new Str($params[0]); - $old = $params[1]; - $new = $params[2]; - $limit = $params[3]; - $this->assertEquals($expected, $s->replace($old, $new, $limit)); - } - public function ReplaceProvider() - { - return - [ - [ - ['oink oink oinkk', 'k', 'ky', 2], - 'oinky oinky oinkk' - ], - [ - ['oink oink oink', 'k', 'ky', 4], - 'oinky oinky oinky' - ], - [ - ['oink oink oink', 'oink', 'moo', -1], - 'moo moo moo' - ], - [ - ['hello world, hello universe', 'hello', 'привет', 1], - 'привет world, hello universe' - ], - [ - ['banana', 'a', 'e', 4], - 'benene' - ], - [ - ['ban 世 a!', ' 世', ' foo', -1], - 'ban foo a!' - ], - [ - ['世世世世世', '世', '界', 2], - '界界世世世' - ], - [ - ['世世世世世', '世', '界', 0], - '世世世世世' - ], - [ - ['世q世q世', 'q', 'q', 5], - '世q世q世' - ], - [ - ['世q世q世', 'z', 'zz', 2], - '世q世q世' - ], - [ - ['', 'a', 'b', -1], - '' - ], - ]; - } - - /** - * @dataProvider trimProvider() + * @dataProvider camelizeProvider() * @param $expected * @param $str - * @param string $chars */ - public function testTrim($expected, $str, $chars = '') + public function testCamelize($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->trim($chars)); + $this->assertEquals($expected, $s->camelize(), $str); } - public function trimProvider() + public function camelizeProvider() { return [ - ['foo bar', ' foo bar '], - ['foo bar', ' foo bar'], - ['foo bar', 'foo bar '], - ['foo bar', "\n\t foo bar \n\t"], - ['fòô bàř', ' fòô bàř '], - ['fòô bàř', ' fòô bàř'], - ['fòô bàř', 'fòô bàř '], - [' foo bar ', "\n\t foo bar \n\t", "\n\t"], - ['fòô bàř', "\n\t fòô bàř \n\t", ''], - ['fòô', ' fòô ', ''], // narrow no-break space (U+202F) - ['fòô', '  fòô  ', ''], // medium mathematical space (U+205F) - ['fòô', '           fòô', ''] // spaces U+2000 to U+200A + ['camelCase', 'CamelCase'], + ['camelCase', 'Camel-Case'], + ['camelCase', 'camel case'], + ['camelCase', 'camel -case'], + ['camelCase', 'camel - case'], + ['camelCase', 'camel_case'], + ['camelCTest', 'camel c test'], + ['stringWith1Number', 'string_with1number'], + ['stringWith22Numbers', 'string-with-2-2 numbers'], + ['dataRate', 'data_rate'], + ['backgroundColor', 'background-color'], + ['yesWeCan', 'yes_we_can'], + ['mozSomething', '-moz-something'], + ['carSpeed', '_car_speed_'], + ['serveHTTP', 'ServeHTTP'], + ['1Camel2Case', '1camel2case'], + ['camelΣase', 'camel σase'], + ['στανιλCase', 'Στανιλ case'], + ['σamelCase', 'σamel Case'] ]; } /** - * @dataProvider trimLeftProvider() + * @dataProvider upperCaseFirstProvider() * @param $expected * @param $str - * @param string $chars */ - public function testTrimLeft($expected, $str, $chars = '') + public function testUpperCaseFirst($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->trimLeft($chars)); + $this->assertEquals($expected, $s->upperCaseFirst(), $str); } - public function trimLeftProvider() + public function upperCaseFirstProvider() { return [ - ['foo bar ', ' foo bar '], - ['foo bar', ' foo bar'], - ['foo bar ', 'foo bar '], - ["foo bar \n\t", "\n\t foo bar \n\t"], - ['fòô bàř ', ' fòô bàř '], - ['fòô bàř', ' fòô bàř'], - ['fòô bàř ', 'fòô bàř '], - ['foo bar', '--foo bar', '-'], - ['fòô bàř', 'òòfòô bàř', 'ò'], - ["fòô bàř \n\t", "\n\t fòô bàř \n\t", ''], - ['fòô ', ' fòô ', ''], // narrow no-break space (U+202F) - ['fòô  ', '  fòô  ', ''], // medium mathematical space (U+205F) - ['fòô', '           fòô', ''] // spaces U+2000 to U+200A + ['Test', 'Test'], + ['', ''], + ['Test', 'test'], + ['1a', '1a'], + ['Σ test', 'σ test'], + [' σ test', ' σ test'] ]; } /** - * @dataProvider trimRightProvider() + * @dataProvider lowerCaseFirstProvider() * @param $expected * @param $str - * @param $chars */ - public function testTrimRight($expected, $str, $chars = '') + public function testLowerCaseFirst($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->trimRight($chars)); + $this->assertEquals($expected, $s->lowerCaseFirst(), $str); } - public function trimRightProvider() + public function lowerCaseFirstProvider() { return [ - [' foo bar', ' foo bar '], - ['foo bar', 'foo bar '], - [' foo bar', ' foo bar'], - ["\n\t foo bar", "\n\t foo bar \n\t"], - [' fòô bàř', ' fòô bàř '], - ['fòô bàř', 'fòô bàř '], - [' fòô bàř', ' fòô bàř'], - ['foo bar', 'foo bar--', '-'], - ['fòô bàř', 'fòô bàřòò', 'ò'], - ["\n\t fòô bàř", "\n\t fòô bàř \n\t", ''], - [' fòô', ' fòô ', ''], // narrow no-break space (U+202F) - ['  fòô', '  fòô  ', ''], // medium mathematical space (U+205F) - ['fòô', 'fòô           ', ''] // spaces U+2000 to U+200A + ['test', 'Test'], + ['', ''], + ['test', 'test'], + ['1a', '1a'], + ['σ test', 'Σ test'], + [' Σ test', ' Σ test'] ]; } /** - * @dataProvider padLeftProvider() + * @dataProvider collapseWhitespaceProvider() * @param $expected * @param $str - * @param $length - * @param $padStr */ - public function testPadLeft($expected, $str, $length, $padStr = ' ') + public function testCollapseWhitespace($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->padLeft($length, $padStr)); + $this->assertEquals($expected, $s->collapseWhitespace(), $str); } - public function padLeftProvider() + public function collapseWhitespaceProvider() { return [ - [' foo bar', 'foo bar', 9], - ['_*foo bar', 'foo bar', 9, '_*'], - ['_*_foo bar', 'foo bar', 10, '_*'], - [' fòô bàř', 'fòô bàř', 9, ' '], - ['¬øfòô bàř', 'fòô bàř', 9, '¬ø'], - ['¬ø¬fòô bàř', 'fòô bàř', 10, '¬ø'], - ['¬ø¬øfòô bàř', 'fòô bàř', 11, '¬ø'], + ['foo bar', ' foo bar '], + ['test string', 'test string'], + ['Ο συγγραφέας', ' Ο συγγραφέας '], + ['123', ' 123 '], + ['', ' '], // no-break space (U+00A0) + ['', '           '], // spaces U+2000 to U+200A + ['', ' '], // narrow no-break space (U+202F) + ['', ' '], // medium mathematical space (U+205F) + ['', ' '], // ideographic space (U+3000) + ['1 2 3', ' 1  2  3  '], + ['', ' '], + ['', ''], ]; } /** - * @dataProvider padRightProvider() + * @dataProvider regexReplaceProvider() * @param $expected * @param $str - * @param $length - * @param $padStr + * @param $pattern + * @param $replacement + * @param string $options */ - public function testPadRight($expected, $str, $length, $padStr = ' ') + public function testRegexReplace($expected, $str, $pattern, $replacement, $options = 'msr') { $s = new Str($str); - $this->assertEquals($expected, $s->padRight($length, $padStr)); + $this->assertEquals($expected, $s->regexReplace($pattern, $replacement, $options), $str); } - public function padRightProvider() + public function regexReplaceProvider() { return [ - ['foo bar ', 'foo bar', 9], - ['foo bar_*', 'foo bar', 9, '_*'], - ['foo bar_*_', 'foo bar', 10, '_*'], - ['fòô bàř ', 'fòô bàř', 9, ' '], - ['fòô bàř¬ø', 'fòô bàř', 9, '¬ø'], - ['fòô bàř¬ø¬', 'fòô bàř', 10, '¬ø'], - ['fòô bàř¬ø¬ø', 'fòô bàř', 11, '¬ø'], + ['', '', '', ''], + ['bar', 'foo', 'f[o]+', 'bar'], + ['o bar', 'foo bar', 'f(o)o', '\1'], + ['bar', 'foo bar', 'f[O]+\s', '', 'i'], + ['foo', 'bar', '[[:alpha:]]{3}', 'foo'], + ['', '', '', '', 'msr'], + ['bàř', 'fòô ', 'f[òô]+\s', 'bàř', 'msr'], + ['fòô', 'fò', '(ò)', '\\1ô', 'msr'], + ['fòô', 'bàř', '[[:alpha:]]{3}', 'fòô', 'msr'] ]; } /** - * @dataProvider padBothProvider() + * @dataProvider dasherizeProvider() * @param $expected * @param $str - * @param $length - * @param $padStr */ - public function testPadBoth($expected, $str, $length, $padStr = ' ') + public function testDasherize($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->padBoth($length, $padStr)); + $this->assertEquals($expected, $s->dasherize(), $str); } - public function padBothProvider() + public function dasherizeProvider() { return [ - ['foo bar ', 'foo bar', 8], - [' foo bar ', 'foo bar', 9, ' '], - ['fòô bàř ', 'fòô bàř', 8, ' '], - [' fòô bàř ', 'fòô bàř', 9, ' '], - ['fòô bàř¬', 'fòô bàř', 8, '¬ø'], - ['¬fòô bàř¬', 'fòô bàř', 9, '¬ø'], - ['¬fòô bàř¬ø', 'fòô bàř', 10, '¬ø'], - ['¬øfòô bàř¬ø', 'fòô bàř', 11, '¬ø'], - ['¬fòô bàř¬ø', 'fòô bàř', 10, '¬øÿ'], - ['¬øfòô bàř¬ø', 'fòô bàř', 11, '¬øÿ'], - ['¬øfòô bàř¬øÿ', 'fòô bàř', 12, '¬øÿ'] + ['test-case', 'testCase'], + ['test-case', 'Test-Case'], + ['test-case', 'test case'], + ['-test-case', '-test -case'], + ['test-case', 'test - case'], + ['test-case', 'test_case'], + ['test-c-test', 'test c test'], + ['test-d-case', 'TestDCase'], + ['test-c-c-test', 'TestCCTest'], + ['string-with1number', 'string_with1number'], + ['string-with-2-2-numbers', 'String-with_2_2 numbers'], + ['1test2case', '1test2case'], + ['data-rate', 'dataRate'], + ['car-speed', 'CarSpeed'], + ['yes-we-can', 'yesWeCan'], + ['background-color', 'backgroundColor'], + ['dash-σase', 'dash Σase'], + ['στανιλ-case', 'Στανιλ case'], + ['σash-case', 'Σash Case'] ]; } /** - * @dataProvider insertProvider() + * @dataProvider delimitProvider() * @param $expected * @param $str - * @param $substring - * @param $index + * @param $delimiter */ - public function testInsert($expected, $str, $substring, $index) + public function testDelimit($expected, $str, $delimiter) { $s = new Str($str); - $this->assertEquals($expected, $s->insert($substring, $index)); + $this->assertEquals($expected, $s->delimit($delimiter), $str); } - public function insertProvider() + public function delimitProvider() { return [ - ['foo bar', 'oo bar', 'f', 0], - ['foo bar', 'f bar', 'oo', 1], - ['f baroo', 'f bar', 'oo', 20], - ['foo bar', 'foo ba', 'r', 6], - ['fòôbàř', 'fòôbř', 'à', 4], - ['fòô bàř', 'òô bàř', 'f', 0], - ['fòô bàř', 'f bàř', 'òô', 1], - ['fòô bàř', 'fòô bà', 'ř', 6] + ['test*case', 'testCase', '*'], + ['test&case', 'Test-Case', '&'], + ['test#case', 'test case', '#'], + ['test**case', 'test -case', '**'], + ['~!~test~!~case', '-test - case', '~!~'], + ['test*case', 'test_case', '*'], + ['test%c%test', ' test c test', '%'], + ['test+u+case', 'TestUCase', '+'], + ['test=c=c=test', 'TestCCTest', '='], + ['string#>with1number', 'string_with1number', '#>'], + ['1test2case', '1test2case', '*'], + ['test ύα σase', 'test Σase', ' ύα ',], + ['στανιλαcase', 'Στανιλ case', 'α',], + ['σashΘcase', 'Σash Case', 'Θ'] ]; } /** - * @dataProvider removeLeftProvider() + * real uuid4 generator test + */ + public function testIsUUIDv4_Real() + { + for ($i=0;$i<=255;$i++) { + $uuid = (string)\Ramsey\Uuid\Uuid::uuid4(); + $s = new Str($uuid); + $this->assertTrue($s->isUUIDv4(), $uuid); + } + } + /** + * @dataProvider isUUIDv4Provider() * @param $expected * @param $str - * @param $substring */ - public function testRemoveLeft($expected, $str, $substring) + public function testIsUUIDv4($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->removeLeft($substring)); - } - public function removeLeftProvider() - { - $s = new Str('foo bar'); - return [ - ['foo bar', 'foo bar', ''], - ['oo bar', 'foo bar', 'f'], - ['bar', 'foo bar', 'foo '], - ['foo bar', 'foo bar', 'oo'], - ['foo bar', 'foo bar', 'oo bar'], - ['oo bar', 'foo bar', (string)$s->at(0)], - ['fòô bàř', 'fòô bàř', ''], - ['òô bàř', 'fòô bàř', 'f'], - ['bàř', 'fòô bàř', 'fòô '], - ['fòô bàř', 'fòô bàř', 'òô'], - ['fòô bàř', 'fòô bàř', 'òô bàř'] + $this->assertEquals($expected, $s->isUUIDv4(), $str); + } + public function isUUIDv4Provider() + { + return [ + [true, 'ae815123-537f-4eb3-a9b8-35881c29e1ac'], + [true, '4724393e-b496-4d94-bdae-96004abbc5e4'], + [true, '9c360134-770d-4284-abab-6e1257dee973'], + [false, '76d7cac8-1bd7-11e8-accf-0ed5f89f718b'], + [false, 'f89cdf3a-1bd7-11e8-accf-0ed5f89f718b'], + [false, 'b3467be4-1bd7-11e8-accf-0ed5f89f718b'], ]; } /** - * @dataProvider removeRightProvider() + * @dataProvider hasLowerCaseProvider() * @param $expected * @param $str - * @param $substring */ - public function testRemoveRight($expected, $str, $substring) + public function testHasLowerCase($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->removeRight($substring)); + $this->assertEquals($expected, $s->hasLowerCase(), $str); } - public function removeRightProvider() + public function hasLowerCaseProvider() { - $s = new Str('foo bar'); return [ - ['foo bar', 'foo bar', ''], - ['foo ba', 'foo bar', 'r'], - ['foo', 'foo bar', ' bar'], - ['foo bar', 'foo bar', 'ba'], - ['foo bar', 'foo bar', 'foo ba'], - ['foo ba', 'foo bar', (string)$s->last()], - ['fòô bàř', 'fòô bàř', ''], - ['fòô bà', 'fòô bàř', 'ř'], - ['fòô', 'fòô bàř', ' bàř'], - ['fòô bàř', 'fòô bàř', 'bà'], - ['fòô bàř', 'fòô bàř', 'fòô bà'] + [false, ''], + [true, 'foobar'], + [false, 'FOO BAR'], + [true, 'fOO BAR'], + [true, 'foO BAR'], + [true, 'FOO BAr'], + [true, 'Foobar'], + [false, 'FÒÔBÀŘ'], + [true, 'fòôbàř'], + [true, 'fòôbàř2'], + [true, 'Fòô bàř'], + [true, 'fòôbÀŘ'], ]; } /** - * @dataProvider repeatProvider() + * @dataProvider hasUpperCaseProvider() * @param $expected * @param $str - * @param $multiplier */ - public function testRepeat($expected, $str, $multiplier) + public function testHasUpperCase($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->repeat($multiplier)); + $this->assertEquals($expected, $s->hasUpperCase(), $str); } - public function repeatProvider() + public function hasUpperCaseProvider() { return [ - ['', 'foo', 0], - ['foo', 'foo', 1], - ['foofoo', 'foo', 2], - ['foofoofoo', 'foo', 3], - ['fòô', 'fòô', 1], - ['fòôfòô', 'fòô', 2], - ['fòôfòôfòô', 'fòô', 3] + [false, ''], + [true, 'FOOBAR'], + [false, 'foo bar'], + [true, 'Foo bar'], + [true, 'FOo bar'], + [true, 'foo baR'], + [true, 'fOOBAR'], + [false, 'fòôbàř'], + [true, 'FÒÔBÀŘ'], + [true, 'FÒÔBÀŘ2'], + [true, 'fÒÔ BÀŘ'], + [true, 'FÒÔBàř'], ]; } /** - * @dataProvider reverseProvider() + * @dataProvider matchesPatternProvider() * @param $expected * @param $str + * @param $pattern */ - public function testReverse($expected, $str) + public function testMatchesPattern($expected, $str, $pattern) { $s = new Str($str); - $this->assertEquals($expected, $s->reverse()); + $this->assertEquals($expected, $s->matchesPattern($pattern), $str); } - public function reverseProvider() + public function matchesPatternProvider() { return [ - ['', ''], - ['raboof', 'foobar'], - ['řàbôòf', 'fòôbàř'], - ['řàb ôòf', 'fòô bàř'], - ['∂∆ ˚åß', 'ßå˚ ∆∂'] + [true, 'FOOBAR', '.*FOO'], + [false, 'foo bar', '.* bar'], + [true, 'Foo bar', '.* ba'], + [true, 'FOo bar', '.*Oo'], + [true, 'foo baR', '.*aR'], + [true, 'fOOBAR', '.*OBA'], + [false, 'fòôbàř', '.*foo'], ]; } /** - * @dataProvider shuffleProvider() + * @dataProvider htmlEncodeProvider() + * @param $expected * @param $str + * @param int $flags */ - public function testShuffle($str) + public function testHtmlEncode($expected, $str, $flags = ENT_COMPAT) { $s = new Str($str); - $oldValues = $s->chars(); - - - $result = $s->shuffle(); - $newValues = $s->chars(); - - $countOld = array_count_values($oldValues); - $countNew = array_count_values($newValues); - - // We'll make sure that the chars are present after shuffle - $this->assertEquals($countOld, $countNew); - $this->assertEmpty(array_diff($countOld, $countNew)); + $this->assertEquals($expected, $s->htmlEncode($flags), $str); } - public function shuffleProvider() + public function htmlEncodeProvider() { return [ - ['foo bar'], - ['∂∆ ˚åß'], - ['å´¥©¨ˆßå˚ ∆∂˙©å∑¥øœ¬'] + ['&', '&'], + ['"', '"'], + [''', "'", ENT_QUOTES], + ['<', '<'], + ['>', '>'], ]; } /** - * @dataProvider betweenProvider() + * @dataProvider htmlDecodeProvider() * @param $expected * @param $str - * @param $start - * @param $end - * @param int $offset + * @param int $flags */ - public function testBetween($expected, $str, $start, $end, $offset = 0) + public function testHtmlDecode($expected, $str, $flags = ENT_COMPAT) { $s = new Str($str); - $this->assertEquals($expected, $s->between($start, $end, $offset), $str); + $this->assertEquals($expected, $s->htmlDecode($flags), $str); } - public function betweenProvider() + public function htmlDecodeProvider() { return [ - ['', 'foo', '{', '}'], - ['', '{foo', '{', '}'], - ['foo', '{foo}', '{', '}'], - ['{foo', '{{foo}', '{', '}'], - ['', '{}foo}', '{', '}'], - ['foo', '}{foo}', '{', '}'], - ['foo', 'A description of {foo} goes here', '{', '}'], - ['bar', '{foo} and {bar}', '{', '}', 1], - ['', 'fòô', '{', '}', 0], - ['', '{fòô', '{', '}', 0], - ['fòô', '{fòô}', '{', '}', 0], - ['{fòô', '{{fòô}', '{', '}', 0], - ['', '{}fòô}', '{', '}', 0], - ['fòô', '}{fòô}', '{', '}', 0], - ['fòô', 'A description of {fòô} goes here', '{', '}', 0], - ['bàř', '{fòô} and {bàř}', '{', '}', 1] + ['&', '&'], + ['"', '"'], + ["'", ''', ENT_QUOTES], + ['<', '<'], + ['>', '>'], ]; } /** - * @dataProvider camelizeProvider() + * @dataProvider humanizeProvider() * @param $expected * @param $str */ - public function testCamelize($expected, $str) + public function testHumanize($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->camelize(), $str); + $this->assertEquals($expected, $s->humanize(), $str); } - public function camelizeProvider() + public function humanizeProvider() { return [ - ['camelCase', 'CamelCase'], - ['camelCase', 'Camel-Case'], - ['camelCase', 'camel case'], - ['camelCase', 'camel -case'], - ['camelCase', 'camel - case'], - ['camelCase', 'camel_case'], - ['camelCTest', 'camel c test'], - ['stringWith1Number', 'string_with1number'], - ['stringWith22Numbers', 'string-with-2-2 numbers'], - ['dataRate', 'data_rate'], - ['backgroundColor', 'background-color'], - ['yesWeCan', 'yes_we_can'], - ['mozSomething', '-moz-something'], - ['carSpeed', '_car_speed_'], - ['serveHTTP', 'ServeHTTP'], - ['1Camel2Case', '1camel2case'], - ['camelΣase', 'camel σase'], - ['στανιλCase', 'Στανιλ case'], - ['σamelCase', 'σamel Case'] + ['Authorid', 'authorid'], + ['Author id', 'author_id'], + ['Test user', ' _test_user_'], + ['Συγγραφέας id', ' συγγραφέας_id '] ]; } /** - * @dataProvider upperCaseFirstProvider() + * @dataProvider isAlphaProvider() * @param $expected * @param $str */ - public function testUpperCaseFirst($expected, $str) + public function testIsAlpha($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->upperCaseFirst(), $str); + $this->assertEquals($expected, $s->isAlpha(), $str); } - public function upperCaseFirstProvider() + public function isAlphaProvider() { return [ - ['Test', 'Test'], - ['', ''], - ['Test', 'test'], - ['1a', '1a'], - ['Σ test', 'σ test'], - [' σ test', ' σ test'] + [true, ''], + [true, 'foobar'], + [false, 'foo bar'], + [false, 'foobar2'], + [true, 'fòôbàř'], + [false, 'fòô bàř'], + [false, 'fòôbàř2'], + [true, 'ҠѨњфгШ'], + [false, 'ҠѨњ¨ˆфгШ'], + [true, '丹尼爾'] ]; } /** - * @dataProvider lowerCaseFirstProvider() + * @dataProvider isAlphanumericProvider() * @param $expected * @param $str */ - public function testLowerCaseFirst($expected, $str) + public function testIsAlphanumeric($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->lowerCaseFirst(), $str); + $this->assertEquals($expected, $s->isAlphanumeric(), $str); } - public function lowerCaseFirstProvider() + public function isAlphanumericProvider() { return [ - ['test', 'Test'], - ['', ''], - ['test', 'test'], - ['1a', '1a'], - ['σ test', 'Σ test'], - [' Σ test', ' Σ test'] + [true, ''], + [true, 'foobar1'], + [false, 'foo bar'], + [false, 'foobar2"'], + [false, "\nfoobar\n"], + [true, 'fòôbàř1'], + [false, 'fòô bàř'], + [false, 'fòôbàř2"'], + [true, 'ҠѨњфгШ'], + [false, 'ҠѨњ¨ˆфгШ'], + [true, '丹尼爾111'], + [true, 'دانيال1'], + [false, 'دانيال1 '] ]; } /** - * @dataProvider collapseWhitespaceProvider() + * @dataProvider isBase64Provider() * @param $expected * @param $str */ - public function testCollapseWhitespace($expected, $str) + public function testIsBase64($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->collapseWhitespace(), $str); + $this->assertEquals($expected, $s->isBase64(), $str); } - public function collapseWhitespaceProvider() + public function isBase64Provider() { return [ - ['foo bar', ' foo bar '], - ['test string', 'test string'], - ['Ο συγγραφέας', ' Ο συγγραφέας '], - ['123', ' 123 '], - ['', ' '], // no-break space (U+00A0) - ['', '           '], // spaces U+2000 to U+200A - ['', ' '], // narrow no-break space (U+202F) - ['', ' '], // medium mathematical space (U+205F) - ['', ' '], // ideographic space (U+3000) - ['1 2 3', ' 1  2  3  '], - ['', ' '], - ['', ''], + [false, ' '], + [true, ''], + [true, base64_encode('FooBar') ], + [true, base64_encode(' ') ], + [true, base64_encode('FÒÔBÀŘ') ], + [true, base64_encode('συγγραφέας') ], + [false, 'Foobar'], ]; } /** - * @dataProvider regexReplaceProvider() + * @dataProvider isBlankProvider() * @param $expected * @param $str - * @param $pattern - * @param $replacement - * @param string $options */ - public function testRegexReplace($expected, $str, $pattern, $replacement, $options = 'msr') + public function testIsBlank($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->regexReplace($pattern, $replacement, $options), $str); + $this->assertEquals($expected, $s->isBlank(), $str); } - public function regexReplaceProvider() + public function isBlankProvider() { return [ - ['', '', '', ''], - ['bar', 'foo', 'f[o]+', 'bar'], - ['o bar', 'foo bar', 'f(o)o', '\1'], - ['bar', 'foo bar', 'f[O]+\s', '', 'i'], - ['foo', 'bar', '[[:alpha:]]{3}', 'foo'], - ['', '', '', '', 'msr'], - ['bàř', 'fòô ', 'f[òô]+\s', 'bàř', 'msr'], - ['fòô', 'fò', '(ò)', '\\1ô', 'msr'], - ['fòô', 'bàř', '[[:alpha:]]{3}', 'fòô', 'msr'] + [true, ''], + [true, ' '], + [true, "\n\t "], + [true, "\n\t \v\f"], + [false, "\n\t a \v\f"], + [false, "\n\t ' \v\f"], + [false, "\n\t 2 \v\f"], + [true, ''], + [true, ' '], // no-break space (U+00A0) + [true, '           '], // spaces U+2000 to U+200A + [true, ' '], // narrow no-break space (U+202F) + [true, ' '], // medium mathematical space (U+205F) + [true, ' '], // ideographic space (U+3000) + [false, ' z'], + [false, ' 1'], ]; } /** - * @dataProvider dasherizeProvider() + * @dataProvider isHexadecimalProvider() * @param $expected * @param $str */ - public function testDasherize($expected, $str) + public function testIsHexadecimal($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->dasherize(), $str); + $this->assertEquals($expected, $s->isHexadecimal(), $str); } - public function dasherizeProvider() + public function isHexadecimalProvider() { return [ - ['test-case', 'testCase'], - ['test-case', 'Test-Case'], - ['test-case', 'test case'], - ['-test-case', '-test -case'], - ['test-case', 'test - case'], - ['test-case', 'test_case'], - ['test-c-test', 'test c test'], - ['test-d-case', 'TestDCase'], - ['test-c-c-test', 'TestCCTest'], - ['string-with1number', 'string_with1number'], - ['string-with-2-2-numbers', 'String-with_2_2 numbers'], - ['1test2case', '1test2case'], - ['data-rate', 'dataRate'], - ['car-speed', 'CarSpeed'], - ['yes-we-can', 'yesWeCan'], - ['background-color', 'backgroundColor'], - ['dash-σase', 'dash Σase'], - ['στανιλ-case', 'Στανιλ case'], - ['σash-case', 'Σash Case'] + [true, ''], + [true, 'abcdef'], + [true, 'ABCDEF'], + [true, '0123456789'], + [true, '0123456789AbCdEf'], + [false, '0123456789x'], + [false, 'ABCDEFx'], + [true, 'abcdef'], + [true, 'ABCDEF'], + [true, '0123456789'], + [true, '0123456789AbCdEf'], + [false, '0123456789x'], + [false, 'ABCDEFx'], ]; } /** - * @dataProvider delimitProvider() - * @param $expected + * @dataProvider isJsonProvider() * @param $str - * @param $delimiter + * @param $expected */ - public function testDelimit($expected, $str, $delimiter) + public function testIsJson($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->delimit($delimiter), $str); + $this->assertEquals($expected, $s->isJson(), $str); } - public function delimitProvider() + public function isJsonProvider() { return [ - ['test*case', 'testCase', '*'], - ['test&case', 'Test-Case', '&'], - ['test#case', 'test case', '#'], - ['test**case', 'test -case', '**'], - ['~!~test~!~case', '-test - case', '~!~'], - ['test*case', 'test_case', '*'], - ['test%c%test', ' test c test', '%'], - ['test+u+case', 'TestUCase', '+'], - ['test=c=c=test', 'TestCCTest', '='], - ['string#>with1number', 'string_with1number', '#>'], - ['1test2case', '1test2case', '*'], - ['test ύα σase', 'test Σase', ' ύα ',], - ['στανιλαcase', 'Στανιλ case', 'α',], - ['σashΘcase', 'Σash Case', 'Θ'] + [false, ''], + [false, ' '], + [true, 'null'], + [true, 'true'], + [true, 'false'], + [true, '[]'], + [true, '{}'], + [true, '123'], + [true, '{"foo": "bar"}'], + [false, '{"foo":"bar",}'], + [false, '{"foo"}'], + [true, '["foo"]'], + [false, '{"foo": "bar"]'], + [true, '123'], + [true, '{"fòô": "bàř"}'], + [false, '{"fòô":"bàř",}'], + [false, '{"fòô"}'], + [false, '["fòô": "bàř"]'], + [true, '["fòô"]'], + [false, '{"fòô": "bàř"]'], ]; } /** - * @dataProvider htmlEncodeProvider() + * @dataProvider isLowerCaseProvider() * @param $expected * @param $str - * @param int $flags */ - public function testHtmlEncode($expected, $str, $flags = ENT_COMPAT) + public function testIsLowerCase($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->htmlEncode($flags), $str); + $this->assertEquals($expected, $s->isLowerCase(), $str); } - public function htmlEncodeProvider() + public function isLowerCaseProvider() { return [ - ['&', '&'], - ['"', '"'], - [''', "'", ENT_QUOTES], - ['<', '<'], - ['>', '>'], + [true, ''], + [true, 'foobar'], + [false, 'foo bar'], + [false, 'Foobar'], + [true, 'fòôbàř'], + [false, 'fòôbàř2'], + [false, 'fòô bàř'], + [false, 'fòôbÀŘ'], ]; } /** - * @dataProvider htmlDecodeProvider() + * @dataProvider isSerializedProvider() * @param $expected * @param $str - * @param int $flags */ - public function testHtmlDecode($expected, $str, $flags = ENT_COMPAT) + public function testIsSerialized($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->htmlDecode($flags), $str); + $this->assertEquals($expected, $s->isSerialized(), $str); } - public function htmlDecodeProvider() + public function isSerializedProvider() { return [ - ['&', '&'], - ['"', '"'], - ["'", ''', ENT_QUOTES], - ['<', '<'], - ['>', '>'], + [false, ''], + [false, 'Acme'], + [true, 'a:1:{s:3:"foo";s:3:"bar";}'], + [false, 'a:1:{s:3:"foo";s:3:"bar"}'], + [true, serialize(['foo' => 'bar'])], + [true, 'a:1:{s:5:"fòô";s:5:"bàř";}'], + [false, 'a:1:{s:5:"fòô";s:5:"bàř"}'], + [true, serialize(['fòô' => 'bár'])], ]; } /** - * @dataProvider humanizeProvider() + * @dataProvider isUpperCaseProvider() * @param $expected * @param $str */ - public function testHumanize($expected, $str) + public function testIsUpperCase($expected, $str) { $s = new Str($str); - $this->assertEquals($expected, $s->humanize(), $str); + $this->assertEquals($expected, $s->isUpperCase(), $str); } - public function humanizeProvider() + public function isUpperCaseProvider() { return [ - ['Author', 'author_id'], - ['Test user', ' _test_user_'], - ['Συγγραφέας', ' συγγραφέας_id '] + [true, ''], + [true, 'FOOBAR'], + [false, 'FOO BAR'], + [false, 'fOOBAR'], + [true, 'FÒÔBÀŘ'], + [false, 'FÒÔBÀŘ2'], + [false, 'FÒÔ BÀŘ'], + [false, 'FÒÔBàř'], ]; } @@ -1953,13 +1954,7 @@ public function testSplit($expected, $str, $pattern, $limit = -1) { $s = new Str($str); $result = $s->split($pattern, $limit); - $expectedLen = count($expected); - - if ($expectedLen === 0) { $this->assertEmpty($result); } - - for ($i = 0; $i < $expectedLen; $i++) { - $this->assertEquals($expected[$i], $result[$i]); - } + $this->assertEquals($expected, $result, (string)print_r($expected, true) . (string)print_r($result, true)); } public function splitProvider() { @@ -2064,6 +2059,7 @@ public function longestCommonSubstringProvider() [' bàř', 'fòô bàř', 'fòr bàř'], [' ', 'toy car', 'fòô bàř'], ['', 'fòô bàř', ''], + ['', '', 'fòô bàř'], ]; } @@ -2392,12 +2388,42 @@ public function titleizeProvider() ['Title Case', 'TITLE CASE'], ['Testing The Method', 'testing the method'], ['Testing the Method', 'testing the method', $ignore], - ['I Like to Watch Dvds at Home', 'i like to watch DVDs at home', - $ignore], + ['I Like to Watch Dvds at Home', 'i like to watch DVDs at home', $ignore], ['Θα Ήθελα Να Φύγει', ' Θα ήθελα να φύγει ', []] ]; } + /** + * @dataProvider toBooleanProvider() + * @param $expected + * @param $str + */ + public function testToBoolean($expected, $str) + { + $s = new Str($str); + $this->assertEquals($expected, $s->toBoolean(), $str); + } + public function toBooleanProvider() + { + return [ + [true, 'true'], + [true, '1'], + [true, 'on'], + [true, 'ON'], + [true, 'yes'], + [true, '999'], + [false, 'false'], + [false, '0'], + [false, 'off'], + [false, 'OFF'], + [false, 'no'], + [false, '-999'], + [false, ''], + [false, ' '], + [false, '  '] // narrow no-break space (U+202F) + ]; + } + /** * @dataProvider toSpacesProvider() * @param $expected @@ -2513,6 +2539,7 @@ public function moveProvider() { return [ ['stte_case', 'test_case', 0, 2, 4], + ['cm/Ae/', '/Acme/', 0, 2, 4], ['Στανιλ case', 'Στανιλ case', 0, 4, 1], ['ιλΣταν case', 'Στανιλ case', 0, 4, 6], ]; @@ -2663,25 +2690,6 @@ public function beforeLastProvider() ]; } - /** - * @dataProvider isEmailProvider() - * @param $expected - * @param $str - */ - public function testIsEmail($expected, $str) - { - $s = new Str($str); - $this->assertEquals($expected, $s->isEmail(), $str); - } - public function isEmailProvider() - { - return [ - [true, 'this.is.a.valid@email.com'], - [false, 'this@is/not@a.valid@email.com'], - [true, 'validemail22_@localhost'] - ]; - } - /** * @dataProvider isIpV4Provider() * @param $expected @@ -2727,10 +2735,10 @@ public function isIpV6Provider() * @param $sizeMax * @param $possibleChars */ - public function testRandom($expected, $size, $sizeMax = -1, $possibleChars = '') + public function testRandom($expected, $size, $sizeMax = -1, $possibleChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') { $s = new Str(''); - $this->assertEquals($expected, \mb_strlen($s->random($size, $sizeMax, $possibleChars))); + $this->assertEquals($expected, \mb_strlen((string)$s->random($size, $sizeMax, $possibleChars))); } public function randomProvider() { @@ -2748,7 +2756,7 @@ public function randomProvider() * @param int $sizeMax * @param string $possibleChars */ - public function testAppendUniqueIdentifier($expected, $str, $size = 4, $sizeMax = -1, $possibleChars = '') + public function testAppendUniqueIdentifier($expected, $str, $size = 4, $sizeMax = -1, $possibleChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') { $s = new Str($str); $this->assertEquals($expected, \mb_strlen((string)$s->appendUniqueIdentifier($size, $sizeMax, $possibleChars))); @@ -2850,6 +2858,25 @@ public function chopProvider() ]; } + /** + * @dataProvider isEmailProvider() + * @param $expected + * @param $str + */ + public function testIsEmail($expected, $str) + { + $s = new Str($str); + $this->assertEquals($expected, $s->isEmail(), $str); + } + public function isEmailProvider() + { + return [ + [true, 'this.is.a.valid@email.com'], + [false, 'this@is/not@a.valid@email.com'], + [true, 'validemail22_@localhost'] + ]; + } + /** * @dataProvider joinProvider() * @param $expected diff --git a/tests/StrTest.php b/tests/StrTest.php new file mode 100644 index 0000000..3d1fc0a --- /dev/null +++ b/tests/StrTest.php @@ -0,0 +1,402 @@ +assertEquals((string)$s, 'Hello world'); + $this->assertEquals((string)$s, $s); + $this->assertEquals($s->getString(), 'Hello world'); + } + + public function testPrefixSuffix() + { + $s = (new Str('世')) + ->ensureLeft('>>') + ->ensureRight('<<'); + + $this->assertEquals((string)$s, '>>世<<'); + $this->assertTrue($s->hasPrefix('>>')); + $this->assertTrue($s->hasSuffix('<<')); + } + + public function testCommon() + { + $s = new Str('b3467be4-1bd7-11e8-accf-0ed5f89f718b'); + $this->assertFalse($s->isUUIDv4()); + $this->assertFalse($s->contains('hello')); + $this->assertFalse($s->isAlpha()); + $this->assertFalse($s->isAlphanumeric()); + $this->assertFalse($s->isBase64()); + $this->assertFalse($s->isBlank()); + $this->assertFalse($s->isHexadecimal()); + $this->assertFalse($s->isJson()); + $this->assertFalse($s->isSerialized()); + $this->assertFalse($s->isUpperCase()); + $this->assertFalse($s->isLowerCase()); + $this->assertFalse($s->hasPrefix('sdhf')); + $this->assertFalse($s->hasSuffix('sdfjh')); + + $s = (new Str('世')) + ->ensureLeft('Hello ') + ->replaceWithLimit('l', 'L', 1); + + $this->assertEquals('HeLlo 世', $s); + $this->assertTrue($s->hasPrefix('HeL')); + $this->assertTrue($s->hasSuffix('世')); + $this->assertTrue($s->hasLowerCase()); + $this->assertTrue($s->hasUpperCase()); + $this->assertFalse($s->isAlpha()); + $this->assertFalse($s->isAlphanumeric()); + $this->assertFalse($s->isBase64()); + $this->assertFalse($s->isBlank()); + $this->assertFalse($s->isHexadecimal()); + $this->assertFalse($s->isJson()); + $this->assertFalse($s->isLowerCase()); + $this->assertFalse($s->isSerialized()); + $this->assertFalse($s->isUpperCase()); + $this->assertFalse($s->isEmail()); + + $s = new Str('HeL世'); + $this->assertEquals([' ','H','e','L','世'], + $s + ->prepend(' ') + ->chars() + ); + + $this->assertCount(5, $s->chars()); + $this->assertEquals(5, $s->length()); + $this->assertEquals(\count($s->chars()), $s->length()); + + $s = new Str('n世'); + $this->assertTrue($s->toBoolean()); + + $s = new Str('no'); + $this->assertFalse($s->toBoolean()); + + $s = new Str('email@email.com'); + $this->assertTrue($s->isEmail()); + + $s = new Str('1.0.1.0'); + $this->assertTrue($s->isIpV4()); + + $s = new Str('2001:cdba::3257:9652'); + $this->assertTrue($s->isIpV6()); + } + + public function testModifiers() + { + $s = new Str('Önnek İş'); + $this->assertEquals('Önnekİş', $s->stripWhitespace()); + $this->assertEquals('önnekiş', $s->toLowerCase()); + $this->assertTrue($s->hasPrefix('ön')); + $this->assertEquals('ö', $s->at(0)); + + $s = new Str('hello world'); + $this->assertEquals('Hello world', $s->upperCaseFirst()); + $this->assertEquals('hello world', $s->lowerCaseFirst()); + + $s = new Str('fòôbàř'); + $this->assertEquals('FÒÔBÀŘ', $s->toUpperCase()); + $this->assertEquals('fòôbàř', $s->toLowerCase()); + $this->assertEquals('Fòôbàř', $s->upperCaseFirst()); + $this->assertEquals('fòôbàř', $s->lowerCaseFirst()); + $this->assertEquals('fòhuôbàř', $s->insert('hu', 2)); + $this->assertEquals('huôbàř', $s->removeLeft('fò')); + $this->assertEquals('huôb', $s->removeRight('àř')); + $this->assertEquals('huôbhuôb', $s->repeat(2)); + $this->assertEquals('bôuhbôuh', $s->reverse()); + $this->assertEquals('Bôuhbôuh', $s->upperCaseFirst()); + $this->assertEquals('bôuhbôuh', $s->lowerCaseFirst()); + $this->assertEquals('BÔUHBÔUH', $s->swapCase()); + + $s = new Str('fòô bàř'); + $this->assertEquals('fòôBàř', $s->camelize()); + $this->assertEquals('fòô-bàř', $s->dasherize()); + $this->assertEquals('FòôBàř', $s->upperCamelize()); + $this->assertEquals('duh_FòôBàřduh_', $s->surround('duh_')); + $this->assertEquals('duh_fòôbàřduh', $s->snakeize()); + $this->assertEquals('duh_fusrodah_fòôbàřduh', $s->afterFirst('uh', '_fusrodah')); + $this->assertEquals('duh_duh_fusrodah_fòôbàřduh', $s->beforeFirst('uh', 'uh_d')); + $this->assertEquals('duh_duh_fusrodah_fòôbàřduh?', $s->afterLast('uh', '?')); + $this->assertEquals('duh_duh_fusrodah_fòôbàřduh??', $s->beforeLast('?', '?')); + + $s = new Str('author'); + $this->assertEquals('Author', $s->humanize()); + $this->assertEquals('aUTHOR', $s->swapCase()); + $this->assertEquals('U', $s->at(1)); + + $s = new Str('authôr'); + $this->assertEquals('Authôr', $s->humanize()); + $this->assertEquals('aUTHÔR', $s->swapCase()); + + $s = new Str('Hello 世 fòôbàř'); + $this->assertEquals('He', $s->first(2)); + $this->assertEquals('', $s->first(0)); + + $s = new Str('Hello 世 fòôbàř'); + $this->assertEquals('bàř', $s->last(3)); + $this->assertEquals('', $s->last(-1)); + + $s = new Str('fòô'); + $this->assertEquals(' fòô ', $s->padBoth(9)); + $this->assertEquals('bàř fòô ', $s->padLeft(12, 'bàř')); + $this->assertEquals('bàř fòô bàř', $s->padRight(15, 'bàř')); + $this->assertEquals('bàbàř fòô bàřbà', $s->padBoth(19, 'bàř')); + $this->assertEquals('bàbàř fòô bàřbà', $s->collapseWhitespace()); + $this->assertEquals('bàbàř*fòô*bàřbà', $s->delimit('*')); + $this->assertEquals('bàř*fòô*bàřbà', $s->substr(2)); + $this->assertEquals('ř*fòô*bà', $s->between('bà', 'řbà')); + $this->assertEquals('ř*fòô*bà', $s->truncate(20, 'i')); + + $s = new Str('oo bar'); + $this->assertEquals('foo bar', $s->insert('f', 0)); + $this->assertEquals('oo bar', $s->removeLeft('f')); + $this->assertEquals('oo b', $s->removeRight('ar')); + $this->assertEquals('oo boo b', $s->repeat(2)); + $this->assertEquals('b oob oo', $s->reverse()); + $this->assertEquals('bOobOo', $s->camelize()); + $this->assertEquals('b-oob-oo', $s->dasherize()); + $this->assertEquals('booo', $s->safeTruncate(4, 'ooo')); + $this->assertEquals('oo', $s->substr(2)); + $this->assertEquals('oo', $s->truncate(8, 'i')); + $this->assertEquals('Booo', $s->ensureLeft('Bo')); + $this->assertEquals('BoooBo', $s->ensureRight('Bo')); + $this->assertEquals('HeyBo', $s->replace('Booo', 'Hey')); + $this->assertEquals('heybo', $s->toLowerCase()); + $this->assertEquals('HEYBO', $s->toUpperCase()); + $this->assertEquals('huhHEYBOhuh', $s->padBoth(11, 'huh')); + $this->assertEquals('huhuhHEYBOhuh', $s->padLeft(13, 'hu')); + $this->assertEquals('huhuhHEYBOhuhuh', $s->padRight(15, 'uh')); + $this->assertEquals('huhuh_h_e_y_b_ohuhuh', $s->delimit('_')); + + $s = new Str('b-oob-oò'); + $this->assertEquals('booò', $s->safeTruncate(4, 'ooò')); + + $s = new Str('fòò and other stuff'); + $this->assertEquals('foo-and-other-stuff', $s->slugify()); + + $s = new Str('fòò and other stuff'); + $this->assertEquals('fòò_and_other_stuff', $s->underscored()); + $this->assertEquals('òòf_and_other_stuff', $s->move(0, 1, 3)); + + $s = new Str('foo foo foo'); + $this->assertEquals('foo foo foo', $s->collapseWhitespace()); + $this->assertEquals('h foo foo', $s->overwrite(0, 3, 'h')); + $this->assertEquals('"h" "foo" "foo"', $s->quote()); + + $s = new Str(' foo bar '); + $this->assertEquals('foo bar', $s->trim()); + $this->assertEquals('oo bar', $s->trimLeft('f')); + $this->assertEquals('oo ba', $s->trimRight('r')); + $this->assertEquals('oo bar baz', $s->append('r baz')); + $this->assertEquals('foo foo bar baz', $s->prepend('foo f')); + + $s = new Str('{foo} and {bar}'); + $this->assertEquals('bar', $s->between('{', '}', 1)); + $this->assertEquals('__bar__', $s->surround('__')); + + $s = new Str('foo and other stuff'); + $this->assertEquals('foo-and-other-stuff', $s->slugify()); + $this->assertEquals('foo_and_other_stuff', $s->underscored()); + + $s = new Str('fòôbàř'); + $this->assertEquals('òôbàř', $s->slice(1)); + $this->assertEquals('oobar', $s->toAscii()); + $this->assertEquals('obar', $s->slice(1)); + $this->assertEquals(4, $s->length()); + + $s = new Str('"fòôbàř"'); + $this->assertEquals('fòôbàř', $s->unquote()); + $this->assertEquals('fòôbàř#hey#ho', $s->join('#', ['hey', 'ho'])); + $this->assertEquals('fòôbàř', $s->shift('#')); + $this->assertEquals('ôbàř', $s->shiftReversed('ò')); + $this->assertEquals('àř', $s->pop('b')); + $this->assertEquals('à', $s->popReversed('ř')); + + $s = new Str('fòô bàř'); + $this->assertEquals('Fòô Bàř', $s->titleize()); + + $s = new Str('str with whitespace '); + $this->assertEquals('strwithwhitespace', $s->stripWhitespace()); + $this->assertEquals('Strwithwhitespace', $s->upperCamelize()); + $this->assertEquals('rwStithwhitespace', $s->move(0, 2, 4)); + $this->assertEquals('rw_stithwhitespace', $s->snakeize()); + $this->assertEquals('rw_stit_here!_hwhitespace', $s->afterFirst('it', '_here!_')); + $this->assertEquals('rw_stit_h_here!_ere!_hwhitespace', $s->beforeFirst('e', '_here!_')); + $this->assertEquals('rw_stit_h_here!_ere!_hwhit_ttt!_espace', $s->afterLast('t', '_ttt!_')); + $this->assertEquals('rw_stit_h_here!_ere!_hwhit_tt_morett!_t!_espace', $s->beforeLast('t', '_morett!_')); + $this->assertEquals('rw_stit_h_here', $s->shift('!')); + $this->assertEquals('_stit_h_here', $s->shiftReversed('w')); + $this->assertEquals('_stit_h_he', $s->popReversed('r')); + $this->assertEquals('_h_he', $s->pop('t')); + + $s = new Str('I see…'); + $this->assertEquals('I see...', $s->tidy()); + $this->assertEquals('I see...', $s->tidy()); + $this->assertEquals('I See...', $s->titleize()); + + $s = new Str("\t\tstr\twith tabs"); + $this->assertEquals('strwith tabs', $s->toSpaces(0)); + $this->assertEquals("strwith\ttabs", $s->toTabs(1)); + $this->assertEquals("Strwith\tTabs", $s->toTitleCase()); + $this->assertEquals("hello with\tTabs", $s->overwrite(0, 3, 'hello ')); + $this->assertEquals('"hello" "with" "Tabs"', $s->quote()); + $this->assertEquals('hello with Tabs', $s->unquote()); + $this->assertEquals('hello with Tabs@other@oie', $s->join('@', ['other', 'oie'])); + $this->assertEquals('oie', $s->last(3)); + + $s = new Str("\t\tstr\twith tàbs"); + $this->assertEquals('strwith tàbs', $s->toSpaces(0)); + $this->assertEquals("strwith\ttàbs", $s->toTabs(1)); + } + + public function testTrim() + { + $s = new Str('[fòôbàř]'); + $this->assertEquals('fòôbàř]', $s->trimLeft('[')); + $this->assertEquals('fòôbàř', $s->trimRight(']')); + $this->assertEquals('fòôbàř', $s + ->ensureLeft('!!') + ->ensureRight('!!') + ->trim('!') + ); + $this->assertEquals('fòôbà', $s + ->ensureLeft('!!!') + ->ensureRight('!!!') + ->trim('!ř') + ); + } + + public function testAppendAndPrepend() + { + $s = new Str('fòô'); + $this->assertEquals('fòôbàř', $s->append('bàř')); + $this->assertEquals('世 fòôbàř', $s->prepend('世 ')); + } + + public function testSearch() + { + $s = new Str('Hello 世 fòôbàř'); + $this->assertTrue($s->contains('世')); + $this->assertTrue($s->contains(' ')); + $this->assertTrue($s->contains('bàř')); + + $this->assertFalse($s->contains('rnd')); + $this->assertFalse($s->contains('bò世')); + $this->assertFalse($s->contains(' ')); + + $this->assertTrue($s->startsWith('Hel')); + $this->assertTrue($s->startsWithAny(['not', 'Hel'])); + + $this->assertTrue($s->endsWith('àř')); + $this->assertFalse($s->endsWithAny(['世', 'Hel'])); + + $this->assertEquals(6, $s->indexOf('世')); + $this->assertEquals(2, $s->indexOf('l')); + $this->assertEquals(3, $s->indexOfLast('l')); + + $s = new Str(' 世 HeLlo 世 fòôbàř'); + $this->assertEquals(2, $s->countSubstr('世')); + $this->assertEquals(2, $s->countSubstr('l', false)); + $this->assertTrue($s->containsAll(['世', 'fòôbàř'])); + $this->assertFalse($s->containsAny(['gh', 'H2'])); + $this->assertFalse($s->startsWith('anything')); + + $s = new Str('hello'); + $this->assertEquals(1, $s->indexOf('e')); + $this->assertEquals(3, $s->indexOfLast('l')); + $this->assertEquals(1, $s->countSubstr('lo')); + $this->assertTrue($s->containsAll(['e', 'lo'])); + $this->assertTrue($s->containsAny(['egh', 'lo'])); + $this->assertTrue($s->startsWith('hel')); + $this->assertTrue($s->startsWithAny(['not', 'hel'])); + $this->assertTrue($s->endsWith('o')); + $this->assertTrue($s->endsWithAny(['fdg', 'lo'])); + $this->assertTrue($s->hasLowerCase()); + $this->assertFalse($s->hasUpperCase()); + } + + public function testRandomFunctions() + { + $s = new Str('HeLlo 世 fòôbàř'); + $len = \mb_strlen((string)$s); + $this->assertEquals($len, \mb_strlen((string)$s->shuffle())); + $this->assertEquals($len, \mb_strlen((string)$s->random($len, -1, (string)$s))); + $this->assertEquals($len * 2, \mb_strlen((string)$s->appendUniqueIdentifier($len, -1, (string)$s))); + } + + public function testRegexFunctions() + { + $s = new Str('fòô '); + $this->assertEquals('bàř', $s->regexReplace('f[òô]+\s', 'bàř')); + $this->assertTrue($s->matchesPattern('b')); + + $s = new Str('foo '); + $this->assertEquals('bar', $s->regexReplace('f[o]+\s', 'bar')); + $this->assertTrue($s->matchesPattern('b')); + } + + public function testHtmlFunctions() + { + $s = new Str('&'); + $this->assertEquals('&', $s->htmlEncode()); + $this->assertEquals('&', $s->htmlDecode()); + + $s = new Str('&òô'); + $this->assertEquals('&òô', $s->htmlDecode()); + $this->assertEquals('&òô', $s->htmlEncode()); + } + + public function testArrayFunctions() + { + $s = new Str("fòô\r\nbàř"); + $this->assertEquals(['fòô', 'bàř'], $s->lines()); + + $s = new Str('foo foo foo'); + $this->assertEquals(['foo', 'foo', 'foo'], $s->split(' ')); + + $s = new Str('foo foo foo'); + $this->assertEquals(['foo', 'foo', 'foo'], $s->words()); + + $s = new Str('foo foo foo'); + $this->assertEquals(['fo', 'o ', ' ', 'fo', 'o ', 'fo', 'o'], $s->chop(2)); + + $s = new Str('hello'); + $this->assertEquals(['h', 'e', 'l', 'l', 'o'], $s->chars()); + + $s = new Str("foo\nboo"); + $this->assertEquals(['foo', 'boo'], $s->lines()); + + $s = new Str('fòo foo foo'); + $this->assertEquals(['fòo', 'foo', 'foo'], $s->split(' ')); + + $s = new Str('fòo foo foo'); + $this->assertEquals(['fòo', 'foo', 'foo'], $s->words()); + + $s = new Str('fòo foo foo'); + $this->assertEquals(['fò', 'o ', ' ', 'fo', 'o ', 'fo', 'o'], $s->chop(2)); + } + + public function testComparingFunctions() + { + $string = new Str('ooo foo'); + $otherString = 'fo foo'; + $this->assertEquals('o foo', $string->longestCommonSubstring($otherString)); + $this->assertEquals('o foo', $string->longestCommonSuffix($otherString)); + $this->assertEquals('', $string->longestCommonPrefix($otherString)); + + $string = new Str('ooo foô'); + $otherString = 'fo foô'; + $this->assertEquals('o foô', $string->longestCommonSubstring($otherString)); + $this->assertEquals('o foô', $string->longestCommonSuffix($otherString)); + $this->assertEquals('', $string->longestCommonPrefix($otherString)); + } +}