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

[12.x] Introduce Arr::from() #55715

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 12, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions 46 src/Illuminate/Collections/Arr.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

use ArgumentCountError;
use ArrayAccess;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Support\Traits\Macroable;
use InvalidArgumentException;
use JsonSerializable;
use Random\Randomizer;
use Traversable;
use WeakMap;

class Arr
{
Expand All @@ -23,6 +28,21 @@ public static function accessible($value)
return is_array($value) || $value instanceof ArrayAccess;
}

/**
* Determine whether the given value is arrayable.
*
* @param mixed $value
* @return bool
*/
public static function arrayable($value)
daniser marked this conversation as resolved.
Show resolved Hide resolved
{
return is_array($value)
|| $value instanceof Arrayable
|| $value instanceof Traversable
|| $value instanceof Jsonable
|| $value instanceof JsonSerializable;
}

/**
* Add an element to an array using "dot" notation if it doesn't exist.
*
Expand Down Expand Up @@ -378,6 +398,32 @@ public static function forget(&$array, $keys)
}
}

/**
* Get the underlying array of items from the given argument.
*
* @template TKey of array-key = array-key
* @template TValue = mixed
*
* @param array<TKey, TValue>|Enumerable<TKey, TValue>|Arrayable<TKey, TValue>|WeakMap<object, TValue>|Traversable<TKey, TValue>|Jsonable|JsonSerializable|object $items
* @return ($items is WeakMap ? list<TValue> : array<TKey, TValue>)
*
* @throws \InvalidArgumentException
*/
public static function from($items)
{
return match (true) {
is_array($items) => $items,
$items instanceof Enumerable => $items->all(),
$items instanceof Arrayable => $items->toArray(),
$items instanceof WeakMap => iterator_to_array($items, false),
$items instanceof Traversable => iterator_to_array($items),
$items instanceof Jsonable => json_decode($items->toJson(), true),
$items instanceof JsonSerializable => (array) $items->jsonSerialize(),
is_object($items) => (array) $items,
default => throw new InvalidArgumentException('Items cannot be represented by a scalar value.'),
};
}

/**
* Get an item from an array using "dot" notation.
*
Expand Down
17 changes: 3 additions & 14 deletions 17 src/Illuminate/Collections/Traits/EnumeratesValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Enumerable;
use Illuminate\Support\HigherOrderCollectionProxy;
use InvalidArgumentException;
use JsonSerializable;
use Traversable;
use UnexpectedValueException;
use UnitEnum;
use WeakMap;

use function Illuminate\Support\enum_value;

Expand Down Expand Up @@ -1059,17 +1056,9 @@ public function __get($key)
*/
protected function getArrayableItems($items)
{
return match (true) {
is_array($items) => $items,
$items instanceof WeakMap => throw new InvalidArgumentException('Collections can not be created using instances of WeakMap.'),
$items instanceof Enumerable => $items->all(),
$items instanceof Arrayable => $items->toArray(),
$items instanceof Traversable => iterator_to_array($items),
$items instanceof Jsonable => json_decode($items->toJson(), true),
$items instanceof JsonSerializable => (array) $items->jsonSerialize(),
$items instanceof UnitEnum => [$items],
default => (array) $items,
};
return is_null($items) || is_scalar($items) || $items instanceof UnitEnum
? Arr::wrap($items)
: Arr::from($items);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions 4 src/Illuminate/Collections/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ function data_get($target, $key, $default = null)
$segment = match ($segment) {
'\*' => '*',
'\{first}' => '{first}',
'{first}' => array_key_first(is_array($target) ? $target : (new Collection($target))->all()),
'{first}' => array_key_first(Arr::from($target)),
'\{last}' => '{last}',
'{last}' => array_key_last(is_array($target) ? $target : (new Collection($target))->all()),
'{last}' => array_key_last(Arr::from($target)),
default => $segment,
};

Expand Down
3 changes: 2 additions & 1 deletion 3 src/Illuminate/Foundation/Testing/DatabaseTruncation.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Foundation\Testing\Traits\CanConfigureMigrationCommands;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

trait DatabaseTruncation
Expand Down Expand Up @@ -120,7 +121,7 @@ protected function getAllTablesForConnection(ConnectionInterface $connection, ?s

$schema = $connection->getSchemaBuilder();

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

/**
Expand Down
21 changes: 10 additions & 11 deletions 21 src/Illuminate/Support/Str.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
use Ramsey\Uuid\UuidFactory;
use Symfony\Component\Uid\Ulid;
use Throwable;
use Traversable;
use voku\helper\ASCII;

class Str
Expand Down Expand Up @@ -1179,8 +1178,8 @@ public static function repeat(string $string, int $times)
*/
public static function replaceArray($search, $replace, $subject)
{
if ($replace instanceof Traversable) {
$replace = (new Collection($replace))->all();
if (Arr::arrayable($replace)) {
daniser marked this conversation as resolved.
Show resolved Hide resolved
$replace = Arr::from($replace);
}

$segments = explode($search, $subject);
Expand Down Expand Up @@ -1221,16 +1220,16 @@ private static function toStringOr($value, $fallback)
*/
public static function replace($search, $replace, $subject, $caseSensitive = true)
{
if ($search instanceof Traversable) {
$search = (new Collection($search))->all();
if (Arr::arrayable($search)) {
$search = Arr::from($search);
}

if ($replace instanceof Traversable) {
$replace = (new Collection($replace))->all();
if (Arr::arrayable($replace)) {
$replace = Arr::from($replace);
}

if ($subject instanceof Traversable) {
$subject = (new Collection($subject))->all();
if (Arr::arrayable($subject)) {
$subject = Arr::from($subject);
}

return $caseSensitive
Expand Down Expand Up @@ -1362,8 +1361,8 @@ public static function replaceMatches($pattern, $replace, $subject, $limit = -1)
*/
public static function remove($search, $subject, $caseSensitive = true)
{
if ($search instanceof Traversable) {
$search = (new Collection($search))->all();
if (Arr::arrayable($search)) {
$search = Arr::from($search);
}

return $caseSensitive
Expand Down
62 changes: 62 additions & 0 deletions 62 tests/Support/Common.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Illuminate\Tests\Support;

use ArrayIterator;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use IteratorAggregate;
use JsonSerializable;
use Traversable;

class TestArrayableObject implements Arrayable
{
public function toArray()
{
return ['foo' => 'bar'];
}
}

class TestJsonableObject implements Jsonable
{
public function toJson($options = 0)
{
return '{"foo":"bar"}';
}
}

class TestJsonSerializeObject implements JsonSerializable
{
public function jsonSerialize(): array
{
return ['foo' => 'bar'];
}
}

class TestJsonSerializeWithScalarValueObject implements JsonSerializable
{
public function jsonSerialize(): string
{
return 'foo';
}
}

class TestTraversableAndJsonSerializableObject implements IteratorAggregate, JsonSerializable
{
public $items;

public function __construct($items = [])
{
$this->items = $items;
}

public function getIterator(): Traversable
{
return new ArrayIterator($this->items);
}

public function jsonSerialize(): array
{
return json_decode(json_encode($this->items), true);
}
}
49 changes: 49 additions & 0 deletions 49 tests/Support/SupportArrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use stdClass;
use WeakMap;

include_once 'Common.php';
include_once 'Enums.php';

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

public function testArrayable(): void
{
$this->assertTrue(Arr::arrayable([]));
$this->assertTrue(Arr::arrayable(new TestArrayableObject));
$this->assertTrue(Arr::arrayable(new TestJsonableObject));
$this->assertTrue(Arr::arrayable(new TestJsonSerializeObject));
$this->assertTrue(Arr::arrayable(new TestTraversableAndJsonSerializableObject));

$this->assertFalse(Arr::arrayable(null));
$this->assertFalse(Arr::arrayable('abc'));
$this->assertFalse(Arr::arrayable(new stdClass));
$this->assertFalse(Arr::arrayable((object) ['a' => 1, 'b' => 2]));
$this->assertFalse(Arr::arrayable(123));
$this->assertFalse(Arr::arrayable(12.34));
$this->assertFalse(Arr::arrayable(true));
$this->assertFalse(Arr::arrayable(new \DateTime));
$this->assertFalse(Arr::arrayable(static fn () => null));
}

public function testAdd()
{
$array = Arr::add(['name' => 'Desk'], 'price', 100);
Expand Down Expand Up @@ -1485,6 +1508,32 @@ public function testForget()
$this->assertEquals([2 => [1 => 'products']], $array);
}

public function testFrom()
{
$this->assertSame(['foo' => 'bar'], Arr::from(['foo' => 'bar']));
$this->assertSame(['foo' => 'bar'], Arr::from((object) ['foo' => 'bar']));
$this->assertSame(['foo' => 'bar'], Arr::from(new TestArrayableObject));
$this->assertSame(['foo' => 'bar'], Arr::from(new TestJsonableObject));
$this->assertSame(['foo' => 'bar'], Arr::from(new TestJsonSerializeObject));
$this->assertSame(['foo'], Arr::from(new TestJsonSerializeWithScalarValueObject));

$this->assertSame(['name' => 'A'], Arr::from(TestEnum::A));
$this->assertSame(['name' => 'A', 'value' => 1], Arr::from(TestBackedEnum::A));
$this->assertSame(['name' => 'A', 'value' => 'A'], Arr::from(TestStringBackedEnum::A));

$subject = [new stdClass, new stdClass];
$items = new TestTraversableAndJsonSerializableObject($subject);
$this->assertSame($subject, Arr::from($items));

$items = new WeakMap;
$items[$temp = new class {}] = 'bar';
$this->assertSame(['bar'], Arr::from($items));

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Items cannot be represented by a scalar value.');
Arr::from(123);
}

public function testWrap()
{
$string = 'a';
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.