Skip to content

Commit 23c161e

Browse files
committed
Various improvements
- Improved some multibyte support - Improved performance of some methods - Reduced some code duplication
1 parent ccc14a4 commit 23c161e

File tree

8 files changed

+91
-31
lines changed

8 files changed

+91
-31
lines changed

src/Support/Str.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace PHLAK\Twine\Support;
4+
5+
class Str
6+
{
7+
/**
8+
* Split a string into an array of characters.
9+
*
10+
* @param string $string A String
11+
*
12+
* @return array
13+
*/
14+
public static function characters(string $string) : array
15+
{
16+
return preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
17+
}
18+
19+
/**
20+
* Split an string into an array of words.
21+
*
22+
* @param string $string A String
23+
*
24+
* @return array
25+
*/
26+
public static function words(string $string) : array
27+
{
28+
preg_match_all('/\p{Lu}?[\p{Ll}0-9]+/u', $string, $matches);
29+
30+
return $matches[0];
31+
}
32+
}

src/Traits/Caseable.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHLAK\Twine\Traits;
44

55
use PHLAK\Twine\Config;
6+
use PHLAK\Twine\Support;
67
use RuntimeException;
78

89
trait Caseable
@@ -95,8 +96,8 @@ public function lowercase(string $mode = Config\Lowercase::ALL) : self
9596
public function camelCase() : self
9697
{
9798
$words = array_map(function ($word) {
98-
return $word->uppercaseFirst();
99-
}, $this->words());
99+
return mb_strtoupper(mb_substr($word, 0, 1, $this->encoding), $this->encoding) . mb_substr($word, 1, null, $this->encoding);
100+
}, Support\Str::words($this->string));
100101

101102
$word = implode('', $words);
102103

@@ -113,8 +114,8 @@ public function camelCase() : self
113114
public function studlyCase() : self
114115
{
115116
$words = array_map(function ($word) {
116-
return $word->uppercaseFirst();
117-
}, $this->words());
117+
return mb_strtoupper(mb_substr($word, 0, 1, $this->encoding), $this->encoding) . mb_substr($word, 1, null, $this->encoding);
118+
}, Support\Str::words($this->string));
118119

119120
return new static(implode('', $words));
120121
}
@@ -137,8 +138,8 @@ public function pascalCase() : self
137138
public function snakeCase() : self
138139
{
139140
$words = array_map(function ($word) {
140-
return $word->lowercase();
141-
}, $this->words());
141+
return mb_strtolower($word, $this->encoding);
142+
}, Support\Str::words($this->string));
142143

143144
return new static(implode('_', $words));
144145
}
@@ -151,8 +152,8 @@ public function snakeCase() : self
151152
public function kebabCase() : self
152153
{
153154
$words = array_map(function ($word) {
154-
return $word->lowercase();
155-
}, $this->words());
155+
return mb_strtolower($word, $this->encoding);
156+
}, Support\Str::words($this->string));
156157

157158
return new static(implode('-', $words));
158159
}

src/Traits/Comparable.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public function endsWith(string $string) : bool
7373
return false;
7474
}
7575

76-
return substr($this->string, -strlen($string)) == $string;
76+
return mb_substr($this->string, -mb_strlen($string, $this->encoding), null, $this->encoding) === $string;
7777
}
7878

7979
/**
@@ -107,7 +107,7 @@ public function in(string $string, string $mode = Config\In::CASE_SENSITIVE) : b
107107
{
108108
Config\In::validateOption($mode);
109109

110-
return $mode($string, $this->string, 0) !== false;
110+
return $mode($string, $this->string, 0, $this->encoding) !== false;
111111
}
112112

113113
/**

src/Traits/Convenience.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHLAK\Twine\Traits;
44

55
use PHLAK\Twine\Config;
6+
use PHLAK\Twine\Support;
67

78
trait Convenience
89
{
@@ -75,7 +76,7 @@ public function characters($mode = Config\Characters::ALL) : array
7576
{
7677
Config\Characters::validateOption($mode);
7778

78-
$characters = preg_split('//u', $this->string, -1, PREG_SPLIT_NO_EMPTY);
79+
$characters = Support\Str::characters($this->string);
7980

8081
if ($mode === Config\Characters::UNIQUE) {
8182
$characters = array_values(array_unique($characters));
@@ -97,7 +98,7 @@ public function characters($mode = Config\Characters::ALL) : array
9798
public function nth(int $step, int $offset = 0) : self
9899
{
99100
$length = $step - 1;
100-
$substring = $this->substring($offset);
101+
$substring = mb_substr($this->string, $offset, null, $this->encoding);
101102

102103
preg_match_all("/(?:^|(?:.|\p{L}|\w){{$length}})(.|\p{L}|\w)/u", $substring, $matches);
103104

src/Traits/Encodable.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHLAK\Twine\Traits;
44

55
use PHLAK\Twine\Config;
6+
use PHLAK\Twine\Support;
67
use RuntimeException;
78

89
trait Encodable
@@ -65,12 +66,8 @@ public function hex(string $mode = Config\Hex::ENCODE) : self
6566

6667
switch ($mode) {
6768
case Config\Hex::ENCODE:
68-
$split = preg_split('//u', $this->string, -1, PREG_SPLIT_NO_EMPTY);
69-
70-
$string = array_reduce($split, function ($str, $char) {
71-
$str .= '\x' . dechex(mb_ord($char, $this->encoding));
72-
73-
return $str;
69+
$string = array_reduce(Support\Str::characters($this->string), function ($str, $char) {
70+
return $str . '\x' . dechex(mb_ord($char, $this->encoding));
7471
}, '');
7572
break;
7673

src/Traits/Segmentable.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ public function chunk(int $length) : array
110110
public function split(int $chunks) : array
111111
{
112112
$length = ceil($this->length() / $chunks);
113-
114113
preg_match_all("/(?:.|\p{L}|\w){1,{$length}}/u", $this->string, $chunks);
115114

116115
return array_map(function ($chunk) {

src/Traits/Transformable.php

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHLAK\Twine\Traits;
44

55
use PHLAK\Twine\Config;
6+
use PHLAK\Twine\Support;
67

78
trait Transformable
89
{
@@ -28,14 +29,9 @@ public function insert(string $string, int $position) : self
2829
*/
2930
public function reverse() : self
3031
{
31-
$length = mb_strlen($this->string, $this->encoding);
32-
$reversed = '';
33-
34-
while ($length-- > 0) {
35-
$reversed .= mb_substr($this->string, $length, 1, $this->encoding) ?? '';
36-
}
37-
38-
return new static($reversed);
32+
return new static(
33+
implode(array_reverse(Support\Str::characters($this->string)))
34+
);
3935
}
4036

4137
/**
@@ -59,10 +55,7 @@ public function replace($search, $replace, int &$count = null) : self
5955
*/
6056
public function shuffle() : self
6157
{
62-
$characters = [];
63-
for ($character = 0; $character <= mb_strlen($this->string, $this->encoding); $character++) {
64-
$characters[] = mb_substr($this->string, $character, 1, $this->encoding);
65-
}
58+
$characters = Support\Str::characters($this->string);
6659

6760
shuffle($characters);
6861

tests/Support/StrTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace PHLAK\Twine\Tests\Support;
4+
5+
use PHLAK\Twine;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class StrTest extends TestCase
9+
{
10+
public function test_it_can_split_a_string_into_an_array_of_characters()
11+
{
12+
$characters = Twine\Support\Str::characters('john pinkerton');
13+
14+
$this->assertEquals(['j', 'o', 'h', 'n', ' ', 'p', 'i', 'n', 'k', 'e', 'r', 't', 'o', 'n'], $characters);
15+
}
16+
17+
public function test_it_can_split_a_multibyte_string_into_an_array_of_characters()
18+
{
19+
$characters = Twine\Support\Str::characters('宮本 茂');
20+
21+
$this->assertEquals(['', '', ' ', ''], $characters);
22+
}
23+
24+
public function test_it_can_split_a_string_into_an_array_of_words()
25+
{
26+
$words = Twine\Support\Str::words('john pinkerton-jingleHeimer_ShmidtJohnson');
27+
28+
$this->assertEquals(['john', 'pinkerton', 'jingle', 'Heimer', 'Shmidt', 'Johnson'], $words);
29+
}
30+
31+
public function test_a_multibyte_string_can_be_split_into_an_array_of_words()
32+
{
33+
$words = Twine\Support\Str::words('Джон Пинкертон-звяканьеХаймер_ШмидтДжонсон');
34+
35+
$this->assertEquals(['Джон', 'Пинкертон', 'звяканье', 'Хаймер', 'Шмидт', 'Джонсон'], $words);
36+
}
37+
}

0 commit comments

Comments
 (0)