diff --git a/src/macros/output/Hyperf/Collection/Arr.php b/src/macros/output/Hyperf/Collection/Arr.php index 68b19631c..a3d4871b4 100644 --- a/src/macros/output/Hyperf/Collection/Arr.php +++ b/src/macros/output/Hyperf/Collection/Arr.php @@ -16,6 +16,16 @@ class Arr { + /** + * Determine whether the given value is arrayable. + * + * @param mixed $value + * @return bool + */ + public static function arrayable($value) + { + } + /** * Get an array item from an array using "dot" notation. * @return array @@ -43,6 +53,21 @@ public static function float(ArrayAccess|array $array, string|int|null $key, ?fl { } + /** + * Get the underlying array of items from the given argument. + * + * @template TKey of array-key = array-key + * @template TValue = mixed + * + * @param array|Enumerable|Arrayable|WeakMap|Traversable|Jsonable|JsonSerializable|object $items + * @return ($items is WeakMap ? list : array) + * + * @throws InvalidArgumentException + */ + public static function from($items) + { + } + /** * Get an integer item from an array using "dot" notation. * @return int diff --git a/src/macros/src/ArrMixin.php b/src/macros/src/ArrMixin.php index af17b4c25..5da4135ef 100644 --- a/src/macros/src/ArrMixin.php +++ b/src/macros/src/ArrMixin.php @@ -13,14 +13,29 @@ use ArrayAccess; use Hyperf\Collection\Arr; +use Hyperf\Collection\Enumerable; +use Hyperf\Contract\Arrayable; +use Hyperf\Contract\Jsonable; use InvalidArgumentException; +use JsonSerializable; +use Traversable; +use WeakMap; /** * @mixin Arr */ class ArrMixin { - public static function array() + public function arrayable() + { + return fn ($value) => is_array($value) + || $value instanceof Arrayable + || $value instanceof Traversable + || $value instanceof Jsonable + || $value instanceof JsonSerializable; + } + + public function array() { return function (ArrayAccess|array $array, string|int|null $key, ?array $default = null) { $value = Arr::get($array, $key, $default); @@ -38,7 +53,7 @@ public static function array() /** * Get a boolean item from an array using "dot" notation. */ - public static function boolean() + public function boolean() { return function (ArrayAccess|array $array, string|int|null $key, ?bool $default = null) { $value = Arr::get($array, $key, $default); @@ -56,7 +71,7 @@ public static function boolean() /** * Get a float item from an array using "dot" notation. */ - public static function float() + public function float() { return function (ArrayAccess|array $array, string|int|null $key, ?float $default = null) { $value = Arr::get($array, $key, $default); @@ -71,10 +86,25 @@ public static function float() }; } + public function from() + { + return fn ($items) => 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->__toString(), 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 integer item from an array using "dot" notation. */ - public static function integer() + public function integer() { return function (ArrayAccess|array $array, string|int|null $key, ?int $default = null) { $value = Arr::get($array, $key, $default); @@ -92,7 +122,7 @@ public static function integer() /** * Get a string item from an array using "dot" notation. */ - public static function string() + public function string() { return function (ArrayAccess|array $array, string|int|null $key, ?string $default = null) { $value = Arr::get($array, $key, $default); diff --git a/tests/Macros/ArrTest.php b/tests/Macros/ArrTest.php index 040e89227..01b31b9f3 100644 --- a/tests/Macros/ArrTest.php +++ b/tests/Macros/ArrTest.php @@ -10,6 +10,26 @@ */ use Hyperf\Collection\Arr; +require_once __DIR__ . '/Stubs/Common.php'; + +test('test arrayable', function () { + $this->assertTrue(Arr::arrayable([])); + $this->assertTrue(Arr::arrayable(new FriendsOfHyperf\Tests\Macros\Stubs\TestArrayableObject())); + $this->assertTrue(Arr::arrayable(new FriendsOfHyperf\Tests\Macros\Stubs\TestJsonableObject())); + $this->assertTrue(Arr::arrayable(new FriendsOfHyperf\Tests\Macros\Stubs\TestJsonSerializeObject())); + $this->assertTrue(Arr::arrayable(new FriendsOfHyperf\Tests\Macros\Stubs\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)); +}); + test('test getsAString', function () { $test_array = ['string' => 'foo bar', 'integer' => 1234]; @@ -73,6 +93,31 @@ Arr::float($test_array, 'string'); }); +test('test from', function () { + $this->assertSame(['foo' => 'bar'], Arr::from(['foo' => 'bar'])); + $this->assertSame(['foo' => 'bar'], Arr::from((object) ['foo' => 'bar'])); + $this->assertSame(['foo' => 'bar'], Arr::from(new FriendsOfHyperf\Tests\Macros\Stubs\TestArrayableObject())); + $this->assertSame(['foo' => 'bar'], Arr::from(new FriendsOfHyperf\Tests\Macros\Stubs\TestJsonableObject())); + $this->assertSame(['foo' => 'bar'], Arr::from(new FriendsOfHyperf\Tests\Macros\Stubs\TestJsonSerializeObject())); + $this->assertSame(['foo'], Arr::from(new FriendsOfHyperf\Tests\Macros\Stubs\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 FriendsOfHyperf\Tests\Macros\Stubs\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); +}); + test('test getsABoolean', function () { $test_array = ['string' => 'foo bar', 'boolean' => true]; diff --git a/tests/Macros/Stubs/Common.php b/tests/Macros/Stubs/Common.php new file mode 100644 index 000000000..3a7cdb8dd --- /dev/null +++ b/tests/Macros/Stubs/Common.php @@ -0,0 +1,76 @@ + 'bar']; + } +} + +class TestJsonableObject implements Jsonable +{ + public function __toString(): string + { + return '{"foo":"bar"}'; + } + + public function toJson($options = 0): string + { + 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); + } +}