Skip to content

Commit 3665096

Browse files
authored
[9.x] Fix Stringable typehints with Enumerable (#44030)
* Fix Stringable types * Make Stringable work everytime with iterable * Add some tests for iterator acceptance
1 parent 8549151 commit 3665096

File tree

4 files changed

+78
-76
lines changed

4 files changed

+78
-76
lines changed

src/Illuminate/Support/Str.php

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Ramsey\Uuid\Generator\CombGenerator;
1515
use Ramsey\Uuid\Uuid;
1616
use Ramsey\Uuid\UuidFactory;
17+
use Traversable;
1718
use voku\helper\ASCII;
1819

1920
class Str
@@ -218,22 +219,25 @@ public static function camel($value)
218219
* Determine if a given string contains a given substring.
219220
*
220221
* @param string $haystack
221-
* @param string|string[]|Enumerable<array-key, string> $needles
222+
* @param string|iterable<string> $needles
222223
* @param bool $ignoreCase
223224
* @return bool
224225
*/
225226
public static function contains($haystack, $needles, $ignoreCase = false)
226227
{
227-
if ($needles instanceof Enumerable) {
228-
$needles = $needles->toArray();
229-
}
230-
231228
if ($ignoreCase) {
232229
$haystack = mb_strtolower($haystack);
233-
$needles = array_map('mb_strtolower', (array) $needles);
234230
}
235231

236-
foreach ((array) $needles as $needle) {
232+
if (! is_iterable($needles)) {
233+
$needles = (array) $needles;
234+
}
235+
236+
foreach ($needles as $needle) {
237+
if ($ignoreCase) {
238+
$needle = mb_strtolower($needle);
239+
}
240+
237241
if ($needle !== '' && str_contains($haystack, $needle)) {
238242
return true;
239243
}
@@ -246,23 +250,14 @@ public static function contains($haystack, $needles, $ignoreCase = false)
246250
* Determine if a given string contains all array values.
247251
*
248252
* @param string $haystack
249-
* @param string[]|Enumerable<array-key, string> $needles
253+
* @param iterable<string> $needles
250254
* @param bool $ignoreCase
251255
* @return bool
252256
*/
253257
public static function containsAll($haystack, $needles, $ignoreCase = false)
254258
{
255-
if ($needles instanceof Enumerable) {
256-
$needles = $needles->toArray();
257-
}
258-
259-
if ($ignoreCase) {
260-
$haystack = mb_strtolower($haystack);
261-
$needles = array_map('mb_strtolower', $needles);
262-
}
263-
264259
foreach ($needles as $needle) {
265-
if (! static::contains($haystack, $needle)) {
260+
if (! static::contains($haystack, $needle, $ignoreCase)) {
266261
return false;
267262
}
268263
}
@@ -274,12 +269,16 @@ public static function containsAll($haystack, $needles, $ignoreCase = false)
274269
* Determine if a given string ends with a given substring.
275270
*
276271
* @param string $haystack
277-
* @param string|string[]|Enumerable<array-key, string> $needles
272+
* @param string|iterable<string> $needles
278273
* @return bool
279274
*/
280275
public static function endsWith($haystack, $needles)
281276
{
282-
foreach ((array) $needles as $needle) {
277+
if (! is_iterable($needles)) {
278+
$needles = (array) $needles;
279+
}
280+
281+
foreach ($needles as $needle) {
283282
if ((string) $needle !== '' && str_ends_with($haystack, $needle)) {
284283
return true;
285284
}
@@ -341,21 +340,19 @@ public static function finish($value, $cap)
341340
/**
342341
* Determine if a given string matches a given pattern.
343342
*
344-
* @param string|array $pattern
343+
* @param string|iterable<string> $pattern
345344
* @param string $value
346345
* @return bool
347346
*/
348347
public static function is($pattern, $value)
349348
{
350-
$patterns = Arr::wrap($pattern);
351-
352349
$value = (string) $value;
353350

354-
if (empty($patterns)) {
355-
return false;
351+
if (! is_iterable($pattern)) {
352+
$pattern = [$pattern];
356353
}
357354

358-
foreach ($patterns as $pattern) {
355+
foreach ($pattern as $pattern) {
359356
$pattern = (string) $pattern;
360357

361358
// If the given value is an exact match we can of course return true right
@@ -789,14 +786,14 @@ public static function repeat(string $string, int $times)
789786
* Replace a given value in the string sequentially with an array.
790787
*
791788
* @param string $search
792-
* @param string[]|Enumerable<array-key, string> $replace
789+
* @param iterable<string> $replace
793790
* @param string $subject
794791
* @return string
795792
*/
796793
public static function replaceArray($search, $replace, $subject)
797794
{
798-
if ($replace instanceof Enumerable) {
799-
$replace = $replace->toArray();
795+
if ($replace instanceof Traversable) {
796+
$replace = collect($replace)->all();
800797
}
801798

802799
$segments = explode($search, $subject);
@@ -813,23 +810,23 @@ public static function replaceArray($search, $replace, $subject)
813810
/**
814811
* Replace the given value in the given string.
815812
*
816-
* @param string|string[]|Enumerable<array-key, string> $search
817-
* @param string|string[]|Enumerable<array-key, string> $replace
818-
* @param string|string[]|Enumerable<array-key, string> $subject
813+
* @param string|iterable<string> $search
814+
* @param string|iterable<string> $replace
815+
* @param string|iterable<string> $subject
819816
* @return string
820817
*/
821818
public static function replace($search, $replace, $subject)
822819
{
823-
if ($search instanceof Enumerable) {
824-
$search = $search->toArray();
820+
if ($search instanceof Traversable) {
821+
$search = collect($search)->all();
825822
}
826823

827-
if ($replace instanceof Enumerable) {
828-
$replace = $replace->toArray();
824+
if ($replace instanceof Traversable) {
825+
$replace = collect($replace)->all();
829826
}
830827

831-
if ($subject instanceof Enumerable) {
832-
$subject = $subject->toArray();
828+
if ($subject instanceof Traversable) {
829+
$subject = collect($subject)->all();
833830
}
834831

835832
return str_replace($search, $replace, $subject);
@@ -886,15 +883,15 @@ public static function replaceLast($search, $replace, $subject)
886883
/**
887884
* Remove any occurrence of the given string in the subject.
888885
*
889-
* @param string|string[]|Enumerable<array-key, string> $search
886+
* @param string|iterable<string> $search
890887
* @param string $subject
891888
* @param bool $caseSensitive
892889
* @return string
893890
*/
894891
public static function remove($search, $subject, $caseSensitive = true)
895892
{
896-
if ($search instanceof Enumerable) {
897-
$search = $search->toArray();
893+
if ($search instanceof Traversable) {
894+
$search = collect($search)->all();
898895
}
899896

900897
$subject = $caseSensitive
@@ -1049,12 +1046,16 @@ public static function squish($value)
10491046
* Determine if a given string starts with a given substring.
10501047
*
10511048
* @param string $haystack
1052-
* @param string|string[]|Enumerable<array-key, string> $needles
1049+
* @param string|iterable<string> $needles
10531050
* @return bool
10541051
*/
10551052
public static function startsWith($haystack, $needles)
10561053
{
1057-
foreach ((array) $needles as $needle) {
1054+
if (! is_iterable($needles)) {
1055+
$needles = [$needles];
1056+
}
1057+
1058+
foreach ($needles as $needle) {
10581059
if ((string) $needle !== '' && str_starts_with($haystack, $needle)) {
10591060
return true;
10601061
}
@@ -1118,11 +1119,11 @@ public static function substrCount($haystack, $needle, $offset = 0, $length = nu
11181119
/**
11191120
* Replace text within a portion of a string.
11201121
*
1121-
* @param string|array $string
1122-
* @param string|array $replace
1123-
* @param array|int $offset
1124-
* @param array|int|null $length
1125-
* @return string|array
1122+
* @param string|string[] $string
1123+
* @param string|string[] $replace
1124+
* @param int|int[] $offset
1125+
* @param int|int[]|null $length
1126+
* @return string|string[]
11261127
*/
11271128
public static function substrReplace($string, $replace, $offset = 0, $length = null)
11281129
{
@@ -1171,7 +1172,7 @@ public static function ucfirst($string)
11711172
* Split a string into pieces by uppercase characters.
11721173
*
11731174
* @param string $string
1174-
* @return array
1175+
* @return string[]
11751176
*/
11761177
public static function ucsplit($string)
11771178
{

src/Illuminate/Support/Stringable.php

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public function camel()
166166
/**
167167
* Determine if a given string contains a given substring.
168168
*
169-
* @param string|string[] $needles
169+
* @param string|iterable<string> $needles
170170
* @return bool
171171
*/
172172
public function contains($needles)
@@ -177,10 +177,10 @@ public function contains($needles)
177177
/**
178178
* Determine if a given string contains all array values.
179179
*
180-
* @param array $needles
180+
* @param iterable<string> $needles
181181
* @return bool
182182
*/
183-
public function containsAll(array $needles)
183+
public function containsAll($needles)
184184
{
185185
return Str::containsAll($this->value, $needles);
186186
}
@@ -199,7 +199,7 @@ public function dirname($levels = 1)
199199
/**
200200
* Determine if a given string ends with a given substring.
201201
*
202-
* @param string|string[] $needles
202+
* @param string|iterable<string> $needles
203203
* @return bool
204204
*/
205205
public function endsWith($needles)
@@ -279,7 +279,7 @@ public function finish($cap)
279279
/**
280280
* Determine if a given string matches a given pattern.
281281
*
282-
* @param string|array $pattern
282+
* @param string|iterable<string> $pattern
283283
* @return bool
284284
*/
285285
public function is($pattern)
@@ -576,31 +576,23 @@ public function repeat(int $times)
576576
/**
577577
* Replace the given value in the given string.
578578
*
579-
* @param string|string[]|Enumerable<array-key, string> $search
580-
* @param string|string[]|Enumerable<array-key, string> $replace
579+
* @param string|iterable<string> $search
580+
* @param string|iterable<string> $replace
581581
* @return static
582582
*/
583583
public function replace($search, $replace)
584584
{
585-
if ($search instanceof Enumerable) {
586-
$search = $search->toArray();
587-
}
588-
589-
if ($replace instanceof Enumerable) {
590-
$replace = $replace->toArray();
591-
}
592-
593-
return new static(str_replace($search, $replace, $this->value));
585+
return new static(Str::replace($search, $replace, $this->value));
594586
}
595587

596588
/**
597589
* Replace a given value in the string sequentially with an array.
598590
*
599591
* @param string $search
600-
* @param array $replace
592+
* @param iterable<string> $replace
601593
* @return static
602594
*/
603-
public function replaceArray($search, array $replace)
595+
public function replaceArray($search, $replace)
604596
{
605597
return new static(Str::replaceArray($search, $replace, $this->value));
606598
}
@@ -755,7 +747,7 @@ public function snake($delimiter = '_')
755747
/**
756748
* Determine if a given string starts with a given substring.
757749
*
758-
* @param string|string[] $needles
750+
* @param string|iterable<string> $needles
759751
* @return bool
760752
*/
761753
public function startsWith($needles)
@@ -801,9 +793,9 @@ public function substrCount($needle, $offset = null, $length = null)
801793
/**
802794
* Replace text within a portion of a string.
803795
*
804-
* @param string|array $replace
805-
* @param array|int $offset
806-
* @param array|int|null $length
796+
* @param string|string[] $replace
797+
* @param int|int[] $offset
798+
* @param int|int[]|null $length
807799
* @return static
808800
*/
809801
public function substrReplace($replace, $offset = 0, $length = null)
@@ -888,7 +880,7 @@ public function ucsplit()
888880
/**
889881
* Execute the given callback if the string contains a given substring.
890882
*
891-
* @param string|string[] $needles
883+
* @param string|iterable<string> $needles
892884
* @param callable $callback
893885
* @param callable|null $default
894886
* @return static
@@ -901,7 +893,7 @@ public function whenContains($needles, $callback, $default = null)
901893
/**
902894
* Execute the given callback if the string contains all array values.
903895
*
904-
* @param array $needles
896+
* @param iterable<string> $needles
905897
* @param callable $callback
906898
* @param callable|null $default
907899
* @return static
@@ -938,7 +930,7 @@ public function whenNotEmpty($callback, $default = null)
938930
/**
939931
* Execute the given callback if the string ends with a given substring.
940932
*
941-
* @param string|string[] $needles
933+
* @param string|iterable<string> $needles
942934
* @param callable $callback
943935
* @param callable|null $default
944936
* @return static
@@ -977,7 +969,7 @@ public function whenNotExactly($value, $callback, $default = null)
977969
/**
978970
* Execute the given callback if the string matches a given pattern.
979971
*
980-
* @param string|array $pattern
972+
* @param string|iterable<string> $pattern
981973
* @param callable $callback
982974
* @param callable|null $default
983975
* @return static
@@ -1014,7 +1006,7 @@ public function whenIsUuid($callback, $default = null)
10141006
/**
10151007
* Execute the given callback if the string starts with a given substring.
10161008
*
1017-
* @param string|string[] $needles
1009+
* @param string|iterable<string> $needles
10181010
* @param callable $callback
10191011
* @param callable|null $default
10201012
* @return static

tests/Support/SupportStrTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public function testStartsWith()
9292
$this->assertTrue(Str::startsWith('jason', 'jason'));
9393
$this->assertTrue(Str::startsWith('jason', ['jas']));
9494
$this->assertTrue(Str::startsWith('jason', ['day', 'jas']));
95+
$this->assertTrue(Str::startsWith('jason', collect(['day', 'jas'])));
9596
$this->assertFalse(Str::startsWith('jason', 'day'));
9697
$this->assertFalse(Str::startsWith('jason', ['day']));
9798
$this->assertFalse(Str::startsWith('jason', null));
@@ -125,6 +126,7 @@ public function testEndsWith()
125126
$this->assertTrue(Str::endsWith('jason', 'jason'));
126127
$this->assertTrue(Str::endsWith('jason', ['on']));
127128
$this->assertTrue(Str::endsWith('jason', ['no', 'on']));
129+
$this->assertTrue(Str::endsWith('jason', collect(['no', 'on'])));
128130
$this->assertFalse(Str::endsWith('jason', 'no'));
129131
$this->assertFalse(Str::endsWith('jason', ['no']));
130132
$this->assertFalse(Str::endsWith('jason', ''));
@@ -876,6 +878,7 @@ public function strContainsProvider()
876878
['Taylor', ['ylo'], true, true],
877879
['Taylor', ['ylo'], true, false],
878880
['Taylor', ['xxx', 'ylo'], true, true],
881+
['Taylor', collect(['xxx', 'ylo']), true, true],
879882
['Taylor', ['xxx', 'ylo'], true, false],
880883
['Taylor', 'xxx', false],
881884
['Taylor', ['xxx'], false],

0 commit comments

Comments
 (0)