Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 64c440f

Browse filesBrowse files
daniserSergey Danilchenko
and
Sergey Danilchenko
authored
[12.x] Introduce Arr::from() (#55715)
* [12.x] Introduce Arr::from() * Arr::from should not wrap scalar values * Arr::arrayable helper function * Arr::from can be used with raw objects except enums; handle UnitEnum instances as scalars * Add tests * Replace implicit `getArrayableItems` usage with `Arr::from` * Handle enums as regular objects in `Arr::from`; wrap enums in `getArrayableItems` * Remove useless `Arr::arrayable` checks * Revert to `instanceof Traversable` checks --------- Co-authored-by: Sergey Danilchenko <s.danilchenko@ttbooking.ru>
1 parent 98ae8ed commit 64c440f
Copy full SHA for 64c440f

File tree

Expand file treeCollapse file tree

8 files changed

+171
-80
lines changed
Filter options
Expand file treeCollapse file tree

8 files changed

+171
-80
lines changed

‎src/Illuminate/Collections/Arr.php

Copy file name to clipboardExpand all lines: src/Illuminate/Collections/Arr.php
+46Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44

55
use ArgumentCountError;
66
use ArrayAccess;
7+
use Illuminate\Contracts\Support\Arrayable;
8+
use Illuminate\Contracts\Support\Jsonable;
79
use Illuminate\Support\Traits\Macroable;
810
use InvalidArgumentException;
11+
use JsonSerializable;
912
use Random\Randomizer;
13+
use Traversable;
14+
use WeakMap;
1015

1116
class Arr
1217
{
@@ -23,6 +28,21 @@ public static function accessible($value)
2328
return is_array($value) || $value instanceof ArrayAccess;
2429
}
2530

31+
/**
32+
* Determine whether the given value is arrayable.
33+
*
34+
* @param mixed $value
35+
* @return bool
36+
*/
37+
public static function arrayable($value)
38+
{
39+
return is_array($value)
40+
|| $value instanceof Arrayable
41+
|| $value instanceof Traversable
42+
|| $value instanceof Jsonable
43+
|| $value instanceof JsonSerializable;
44+
}
45+
2646
/**
2747
* Add an element to an array using "dot" notation if it doesn't exist.
2848
*
@@ -378,6 +398,32 @@ public static function forget(&$array, $keys)
378398
}
379399
}
380400

401+
/**
402+
* Get the underlying array of items from the given argument.
403+
*
404+
* @template TKey of array-key = array-key
405+
* @template TValue = mixed
406+
*
407+
* @param array<TKey, TValue>|Enumerable<TKey, TValue>|Arrayable<TKey, TValue>|WeakMap<object, TValue>|Traversable<TKey, TValue>|Jsonable|JsonSerializable|object $items
408+
* @return ($items is WeakMap ? list<TValue> : array<TKey, TValue>)
409+
*
410+
* @throws \InvalidArgumentException
411+
*/
412+
public static function from($items)
413+
{
414+
return match (true) {
415+
is_array($items) => $items,
416+
$items instanceof Enumerable => $items->all(),
417+
$items instanceof Arrayable => $items->toArray(),
418+
$items instanceof WeakMap => iterator_to_array($items, false),
419+
$items instanceof Traversable => iterator_to_array($items),
420+
$items instanceof Jsonable => json_decode($items->toJson(), true),
421+
$items instanceof JsonSerializable => (array) $items->jsonSerialize(),
422+
is_object($items) => (array) $items,
423+
default => throw new InvalidArgumentException('Items cannot be represented by a scalar value.'),
424+
};
425+
}
426+
381427
/**
382428
* Get an item from an array using "dot" notation.
383429
*

‎src/Illuminate/Collections/Traits/EnumeratesValues.php

Copy file name to clipboardExpand all lines: src/Illuminate/Collections/Traits/EnumeratesValues.php
+3-14Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@
1212
use Illuminate\Support\Collection;
1313
use Illuminate\Support\Enumerable;
1414
use Illuminate\Support\HigherOrderCollectionProxy;
15-
use InvalidArgumentException;
1615
use JsonSerializable;
17-
use Traversable;
1816
use UnexpectedValueException;
1917
use UnitEnum;
20-
use WeakMap;
2118

2219
use function Illuminate\Support\enum_value;
2320

@@ -1059,17 +1056,9 @@ public function __get($key)
10591056
*/
10601057
protected function getArrayableItems($items)
10611058
{
1062-
return match (true) {
1063-
is_array($items) => $items,
1064-
$items instanceof WeakMap => throw new InvalidArgumentException('Collections can not be created using instances of WeakMap.'),
1065-
$items instanceof Enumerable => $items->all(),
1066-
$items instanceof Arrayable => $items->toArray(),
1067-
$items instanceof Traversable => iterator_to_array($items),
1068-
$items instanceof Jsonable => json_decode($items->toJson(), true),
1069-
$items instanceof JsonSerializable => (array) $items->jsonSerialize(),
1070-
$items instanceof UnitEnum => [$items],
1071-
default => (array) $items,
1072-
};
1059+
return is_null($items) || is_scalar($items) || $items instanceof UnitEnum
1060+
? Arr::wrap($items)
1061+
: Arr::from($items);
10731062
}
10741063

10751064
/**

‎src/Illuminate/Collections/helpers.php

Copy file name to clipboardExpand all lines: src/Illuminate/Collections/helpers.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ function data_get($target, $key, $default = null)
7777
$segment = match ($segment) {
7878
'\*' => '*',
7979
'\{first}' => '{first}',
80-
'{first}' => array_key_first(is_array($target) ? $target : (new Collection($target))->all()),
80+
'{first}' => array_key_first(Arr::from($target)),
8181
'\{last}' => '{last}',
82-
'{last}' => array_key_last(is_array($target) ? $target : (new Collection($target))->all()),
82+
'{last}' => array_key_last(Arr::from($target)),
8383
default => $segment,
8484
};
8585

‎src/Illuminate/Foundation/Testing/DatabaseTruncation.php

Copy file name to clipboardExpand all lines: src/Illuminate/Foundation/Testing/DatabaseTruncation.php
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Contracts\Console\Kernel;
66
use Illuminate\Database\ConnectionInterface;
77
use Illuminate\Foundation\Testing\Traits\CanConfigureMigrationCommands;
8+
use Illuminate\Support\Arr;
89
use Illuminate\Support\Collection;
910

1011
trait DatabaseTruncation
@@ -120,7 +121,7 @@ protected function getAllTablesForConnection(ConnectionInterface $connection, ?s
120121

121122
$schema = $connection->getSchemaBuilder();
122123

123-
return static::$allTables[$name] = (new Collection($schema->getTables($schema->getCurrentSchemaListing())))->all();
124+
return static::$allTables[$name] = Arr::from($schema->getTables($schema->getCurrentSchemaListing()));
124125
}
125126

126127
/**

‎src/Illuminate/Support/Str.php

Copy file name to clipboardExpand all lines: src/Illuminate/Support/Str.php
+5-5Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,7 +1180,7 @@ public static function repeat(string $string, int $times)
11801180
public static function replaceArray($search, $replace, $subject)
11811181
{
11821182
if ($replace instanceof Traversable) {
1183-
$replace = (new Collection($replace))->all();
1183+
$replace = Arr::from($replace);
11841184
}
11851185

11861186
$segments = explode($search, $subject);
@@ -1222,15 +1222,15 @@ private static function toStringOr($value, $fallback)
12221222
public static function replace($search, $replace, $subject, $caseSensitive = true)
12231223
{
12241224
if ($search instanceof Traversable) {
1225-
$search = (new Collection($search))->all();
1225+
$search = Arr::from($search);
12261226
}
12271227

12281228
if ($replace instanceof Traversable) {
1229-
$replace = (new Collection($replace))->all();
1229+
$replace = Arr::from($replace);
12301230
}
12311231

12321232
if ($subject instanceof Traversable) {
1233-
$subject = (new Collection($subject))->all();
1233+
$subject = Arr::from($subject);
12341234
}
12351235

12361236
return $caseSensitive
@@ -1363,7 +1363,7 @@ public static function replaceMatches($pattern, $replace, $subject, $limit = -1)
13631363
public static function remove($search, $subject, $caseSensitive = true)
13641364
{
13651365
if ($search instanceof Traversable) {
1366-
$search = (new Collection($search))->all();
1366+
$search = Arr::from($search);
13671367
}
13681368

13691369
return $caseSensitive

‎tests/Support/Common.php

Copy file name to clipboard
+62Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Support;
4+
5+
use ArrayIterator;
6+
use Illuminate\Contracts\Support\Arrayable;
7+
use Illuminate\Contracts\Support\Jsonable;
8+
use IteratorAggregate;
9+
use JsonSerializable;
10+
use Traversable;
11+
12+
class TestArrayableObject implements Arrayable
13+
{
14+
public function toArray()
15+
{
16+
return ['foo' => 'bar'];
17+
}
18+
}
19+
20+
class TestJsonableObject implements Jsonable
21+
{
22+
public function toJson($options = 0)
23+
{
24+
return '{"foo":"bar"}';
25+
}
26+
}
27+
28+
class TestJsonSerializeObject implements JsonSerializable
29+
{
30+
public function jsonSerialize(): array
31+
{
32+
return ['foo' => 'bar'];
33+
}
34+
}
35+
36+
class TestJsonSerializeWithScalarValueObject implements JsonSerializable
37+
{
38+
public function jsonSerialize(): string
39+
{
40+
return 'foo';
41+
}
42+
}
43+
44+
class TestTraversableAndJsonSerializableObject implements IteratorAggregate, JsonSerializable
45+
{
46+
public $items;
47+
48+
public function __construct($items = [])
49+
{
50+
$this->items = $items;
51+
}
52+
53+
public function getIterator(): Traversable
54+
{
55+
return new ArrayIterator($this->items);
56+
}
57+
58+
public function jsonSerialize(): array
59+
{
60+
return json_decode(json_encode($this->items), true);
61+
}
62+
}

‎tests/Support/SupportArrTest.php

Copy file name to clipboardExpand all lines: tests/Support/SupportArrTest.php
+49Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
use InvalidArgumentException;
1212
use PHPUnit\Framework\TestCase;
1313
use stdClass;
14+
use WeakMap;
15+
16+
include_once 'Common.php';
17+
include_once 'Enums.php';
1418

1519
class SupportArrTest extends TestCase
1620
{
@@ -32,6 +36,25 @@ public function testAccessible(): void
3236
$this->assertFalse(Arr::accessible(static fn () => null));
3337
}
3438

39+
public function testArrayable(): void
40+
{
41+
$this->assertTrue(Arr::arrayable([]));
42+
$this->assertTrue(Arr::arrayable(new TestArrayableObject));
43+
$this->assertTrue(Arr::arrayable(new TestJsonableObject));
44+
$this->assertTrue(Arr::arrayable(new TestJsonSerializeObject));
45+
$this->assertTrue(Arr::arrayable(new TestTraversableAndJsonSerializableObject));
46+
47+
$this->assertFalse(Arr::arrayable(null));
48+
$this->assertFalse(Arr::arrayable('abc'));
49+
$this->assertFalse(Arr::arrayable(new stdClass));
50+
$this->assertFalse(Arr::arrayable((object) ['a' => 1, 'b' => 2]));
51+
$this->assertFalse(Arr::arrayable(123));
52+
$this->assertFalse(Arr::arrayable(12.34));
53+
$this->assertFalse(Arr::arrayable(true));
54+
$this->assertFalse(Arr::arrayable(new \DateTime));
55+
$this->assertFalse(Arr::arrayable(static fn () => null));
56+
}
57+
3558
public function testAdd()
3659
{
3760
$array = Arr::add(['name' => 'Desk'], 'price', 100);
@@ -1485,6 +1508,32 @@ public function testForget()
14851508
$this->assertEquals([2 => [1 => 'products']], $array);
14861509
}
14871510

1511+
public function testFrom()
1512+
{
1513+
$this->assertSame(['foo' => 'bar'], Arr::from(['foo' => 'bar']));
1514+
$this->assertSame(['foo' => 'bar'], Arr::from((object) ['foo' => 'bar']));
1515+
$this->assertSame(['foo' => 'bar'], Arr::from(new TestArrayableObject));
1516+
$this->assertSame(['foo' => 'bar'], Arr::from(new TestJsonableObject));
1517+
$this->assertSame(['foo' => 'bar'], Arr::from(new TestJsonSerializeObject));
1518+
$this->assertSame(['foo'], Arr::from(new TestJsonSerializeWithScalarValueObject));
1519+
1520+
$this->assertSame(['name' => 'A'], Arr::from(TestEnum::A));
1521+
$this->assertSame(['name' => 'A', 'value' => 1], Arr::from(TestBackedEnum::A));
1522+
$this->assertSame(['name' => 'A', 'value' => 'A'], Arr::from(TestStringBackedEnum::A));
1523+
1524+
$subject = [new stdClass, new stdClass];
1525+
$items = new TestTraversableAndJsonSerializableObject($subject);
1526+
$this->assertSame($subject, Arr::from($items));
1527+
1528+
$items = new WeakMap;
1529+
$items[$temp = new class {}] = 'bar';
1530+
$this->assertSame(['bar'], Arr::from($items));
1531+
1532+
$this->expectException(InvalidArgumentException::class);
1533+
$this->expectExceptionMessage('Items cannot be represented by a scalar value.');
1534+
Arr::from(123);
1535+
}
1536+
14881537
public function testWrap()
14891538
{
14901539
$string = 'a';

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.