Skip to content
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

Feat - add the has_shape method #1

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
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
Next Next commit
feat - add the has_shape method
lucatume committed Mar 15, 2024
commit cfa0ed9ee3b366ee8e919cba7726019eb3dd13a8
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ A library for array manipulations.
* [get_first_set](/docs/classes/StellarWP/Arrays/Arr.md#get_first_set)
* [get_in_any](/docs/classes/StellarWP/Arrays/Arr.md#get_in_any)
* [has](/docs/classes/StellarWP/Arrays/Arr.md#has)
* [has_shape]/docs/classes/StellarWP/Arrays/Arr.md#has_shape)
* [insert_after_key](/docs/classes/StellarWP/Arrays/Arr.md#insert_after_key)
* [insert_before_key](/docs/classes/StellarWP/Arrays/Arr.md#insert_before_key)
* [is_assoc](/docs/classes/StellarWP/Arrays/Arr.md#is_assoc)
24 changes: 21 additions & 3 deletions docs/classes/StellarWP/Arrays/Arr.md
Original file line number Diff line number Diff line change
@@ -274,7 +274,7 @@ The sanitized array

**See Also:**

* https://gist.github.com/esthezia/5804445 -
* https://gist.github.com/esthezia/5804445 -

***

@@ -604,7 +604,25 @@ public static has(\ArrayAccess|array $array, array|string|int|null $indexes): bo
| `$indexes` | **array|string|int|null** | The indexes to search; in order the function will look from the first to the last. |


### has_shape

Check if an array has a specific shape.

```php
public static has_shape(mixed $array, array $shape): bool
```

* This method is **static**.




**Parameters:**

| Parameter | Type | Description |
|-----------|-----------|-----------------------------------------------------------------------------------|
| `$array` | **mixed** | The array to check. |
| `$shape` | **array** | The shape to check for. A map from keys to the callable or Closure to check them. |

***

@@ -865,7 +883,7 @@ public merge_recursive(array& $array1, array& $array2): array

**See Also:**

* http://php.net/manual/en/function.array-merge-recursive.php#92195 -
* http://php.net/manual/en/function.array-merge-recursive.php#92195 -

***

@@ -1415,7 +1433,7 @@ Integer position of first needle occurrence.

**See Also:**

* \StellarWP\Arrays\strpos() -
* \StellarWP\Arrays\strpos() -

***

60 changes: 56 additions & 4 deletions src/Arrays/Arr.php
Original file line number Diff line number Diff line change
@@ -3,8 +3,10 @@
namespace StellarWP\Arrays;

use ArrayAccess;
use BadMethodCallException;
use Illuminate\Support\Enumerable;
use InvalidArgumentException;
use Throwable;

/**
* Array utilities
@@ -299,14 +301,14 @@
/**
* Determine if the given key exists in the provided array.
*
* @param \ArrayAccess|array $array
* @param string|int|float $key
* @param ArrayAccess|array $array
* @param string|int|float $key
*
* @return bool
*/
public static function exists( $array, $key ) {
if ( $array instanceof Enumerable ) {

Check failure on line 310 in src/Arrays/Arr.php

GitHub Actions / phpstan

Class Illuminate\Support\Enumerable not found.
return $array->has( $key );

Check failure on line 311 in src/Arrays/Arr.php

GitHub Actions / phpstan

Call to method has() on an unknown class Illuminate\Support\Enumerable.
}

if ( $array instanceof ArrayAccess ) {
@@ -357,7 +359,7 @@
public static function first( $array, callable $callback = null, $default = null ) {
if ( is_null( $callback ) ) {
if ( empty( $array ) ) {
return value( $default );

Check failure on line 362 in src/Arrays/Arr.php

GitHub Actions / phpstan

Function value not found.
}

foreach ( $array as $item ) {
@@ -371,7 +373,7 @@
}
}

return value( $default );

Check failure on line 376 in src/Arrays/Arr.php

GitHub Actions / phpstan

Function value not found.
}

/**
@@ -543,7 +545,7 @@
/**
* Check if an item or items exist in an array using "dot" notation.
*
* @param \ArrayAccess|array $array
* @param ArrayAccess|array $array
* @param array|string|int|null $indexes The indexes to search; in order the function will look from the first to the last.
*
* @return bool
@@ -691,7 +693,7 @@
*/
public static function last( $array, callable $callback = null, $default = null ) {
if ( is_null( $callback ) ) {
return empty( $array ) ? value( $default ) : end( $array );

Check failure on line 696 in src/Arrays/Arr.php

GitHub Actions / phpstan

Function value not found.
}

return static::first( array_reverse( $array, true ), $callback, $default );
@@ -942,7 +944,7 @@
*
* @return mixed
*
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
public static function random( $array, $number = null, $preserveKeys = false ) {
$requested = is_null( $number ) ? 1 : $number;
@@ -1386,4 +1388,54 @@

return is_array( $value ) ? $value : [ $value ];
}

/**
* Checks if an array has a specific shape.
*
* @since TBD
*
* @param array $array The array to check.
* @param array<string|int,callable> $shape The shape to check for. Each key, either a string or an integer,
* maps to a callable that will be used to validate the value at that key.
* The callable must have the signature `fn( mixed $value ) :bool`.
* @param bool $strict Whether the array should only contain the keys specified in the shape.
*
* @return bool Whether the array has the specified shape.
*/
public static function has_shape( $array, array $shape, bool $strict = false ): bool {
if ( ! is_array( $array ) ) {
return false;
}

if (
$strict
&& (
array_intersect_key( $array, $shape ) !== $array
||
array_diff_key( $array, $shape ) !== []
)
) {
return false;
}

if ( count( array_intersect_key( $shape, $array ) ) < count( $shape ) ) {
return false;
}

foreach ( $shape as $key => $check ) {
if ( ! is_callable( $check ) ) {
throw new \BadMethodCallException( 'The shape array must contain only callables as values.' );
}

try {
if ( ! $check( $array[ $key ] ) ) {
return false;
}
} catch ( \Throwable $th ) {
return false;
}
}

return true;
}
}
85 changes: 82 additions & 3 deletions tests/wpunit/ArraysTest.php
Original file line number Diff line number Diff line change
@@ -766,10 +766,89 @@ public function array_visit_recursive_data_provider() {
];
}

public function has_shape_data_provider(): array {
return [
'not an array' => [ 'foo', [], true, false ],
'empty array, empty shape' => [ [], [], true, true ],
'empty array, non-empty shape, strict' => [
[],
[ 'foo' => 'is_string' ],
true,
false
],
'empty array, non-empty shape, non-strict' => [
[],
[ 'foo' => 'is_string' ],
false,
false
],
'non-empty array, function shape, missing key, strict' => [
[ 'foo' => 23 ],
[ 'bar' => 'is_string' ],
true,
false
],
'non-empty array, function shape, missing key, non-strict' => [
[ 'foo' => 23 ],
[ 'bar' => 'is_string' ],
false,
false
],
'non-empty array, function shape, extra key, strict' => [
[ 'foo' => 23, 'bar' => 'baz' ],
[ 'foo' => 'is_int' ],
true,
false
],
'non-empty array, function shape, extra key, non-strict' => [
[ 'foo' => 23, 'bar' => 'baz' ],
[ 'foo' => 'is_int' ],
false,
true
],
'non-empty array, closure shape, all key fail failure, strict' => [
[ 'foo' => 23, 'bar' => 89 ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
true,
false
],
'non-empty array, closure shape, all key fail failure, non-strict' => [
[ 'foo' => 23, 'bar' => 89 ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
false,
false
],
'non-empty array, closure shape, all key pass, strict' => [
[ 'foo' => 'hello', 'bar' => 'world' ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
true,
true
],
'non-empty array, closure shape, all key pass, non-strict ' => [
[ 'foo' => 'hello', 'bar' => 'world' ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
false,
true
],
'non-empty array, closure shape, some key pass, strict' => [
[ 'foo' => 'hello', 'bar' => 89 ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
true,
false
],
'non-empty array, closure shape, some key pass, non-strict' => [
[ 'foo' => 'hello', 'bar' => 89 ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
false,
false
],
];
}

/**
* @dataProvider array_visit_recursive_data_provider
* @dataProvider has_shape_data_provider
*/
public function test_array_visit_recursive( $input, $visitor, $expected ) {
$this->assertEqualSets( $expected, Arr::array_visit_recursive( $input, $visitor ) );
public function test_has_shape( $input, $shape, $strict, $expected ): void {
$this->assertEquals( $expected, Arr::has_shape( $input, $shape, $strict ) );
}
}