Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .github/workflows/tests-php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
${SLIC_BIN} debug on
${SLIC_BIN} info
${SLIC_BIN} config
${SLIC_BIN} php-version set 7.4
- name: Set up StellarWP Arrays
run: |
${SLIC_BIN} use arrays
Expand Down
162 changes: 100 additions & 62 deletions src/Arrays/Arr.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ public static function accessible( $value ): bool {
* @return array
*/
public static function add( $array, $key, $value ) {
$key = explode( '.', $key );
$key = static::wrap( $key );
if ( is_null( static::get( $array, $key ) ) ) {
static::set( $array, $key, $value );
$array = static::set( $array, $key, $value );
}

return $array;
Expand Down Expand Up @@ -208,7 +210,7 @@ public static function dot( $array, $prepend = '' ) {
$results = [];

foreach ( $array as $key => $value ) {
if ( is_array( $value ) && ! empty( $value ) ) {
if ( is_array( $value ) ) {
$results = array_merge( $results, static::dot( $value, $prepend . $key . '.' ) );
} else {
$results[ $prepend . $key ] = $value;
Expand Down Expand Up @@ -290,9 +292,10 @@ public static function filter_to_flat_scalar_associative_array( $array ) {
* @return array
*/
public static function except( $array, $keys ) {
static::forget( $array, $keys );
$new_array = $array;
static::forget( $new_array, $keys );

return $array;
return $new_array;
}


Expand All @@ -305,19 +308,7 @@ public static function except( $array, $keys ) {
* @return bool
*/
public static function exists( $array, $key ) {
if ( $array instanceof Enumerable ) {
return $array->has( $key );
}

if ( $array instanceof ArrayAccess ) {
return $array->offsetExists( $key );
}

if ( is_float( $key ) ) {
$key = (string) $key;
}

return array_key_exists( $key, $array );
return static::has( $array, $key );
}

/**
Expand Down Expand Up @@ -390,16 +381,30 @@ public static function first( $array, callable $callback = null, $default = null
public static function flatten( $array, int $depth = PHP_INT_MAX ): array {
$result = [];

foreach ( $array as $item ) {
if ( $depth < 1 ) {
return $array;
}

foreach ( $array as $key => $item ) {
if ( ! is_array( $item ) ) {
$result[] = $item;
// Preserve string keys, use numeric keys for numeric
if ( is_string( $key ) ) {
$result[ $key ] = $item;
} else {
$result[] = $item;
}
} else {
$values = $depth === 1
? array_values( $item )
: static::flatten( $item, $depth - 1 );

foreach ( $values as $value ) {
$result[] = $value;
foreach ( $values as $value_key => $value ) {
// Preserve string keys from nested arrays
if ( is_string( $value_key ) ) {
$result[ $value_key ] = $value;
} else {
$result[] = $value;
}
}
}
}
Expand All @@ -416,38 +421,29 @@ public static function flatten( $array, int $depth = PHP_INT_MAX ): array {
* @return void
*/
public static function forget( &$array, $keys ) {
$original = &$array;

$keys = static::wrap( $keys );

if ( count( $keys ) === 0 ) {
return;
}
$keys = (array) $keys;

foreach ( $keys as $key ) {
// if the exact key exists in the top-level, remove it
if ( static::exists( $array, $key ) ) {
unset( $array[ $key ] );
// Convert dot notation to array segments
$parts = explode( '.', $key );

if ( count( $parts ) === 1 ) {
unset( $array[ $key ] );
continue;
}

$parts = explode( '.', $key );

// clean up before each pass
$array = &$original;

while ( count( $parts ) > 1 ) {
$part = array_shift( $parts );
// For nested keys, traverse the array
$current = &$array;
$lastKey = array_pop( $parts );

if ( isset( $array[ $part ] ) && static::accessible( $array[ $part ] ) ) {
$array = &$array[ $part ];
} else {
foreach ( $parts as $part ) {
if ( ! isset( $current[ $part ] ) || ! is_array( $current[ $part ] ) ) {
continue 2;
}
$current = &$current[ $part ];
}

unset( $array[ array_shift( $parts ) ] );
unset( $current[ $lastKey ] );
}
}

Expand All @@ -457,25 +453,28 @@ public static function forget( &$array, $keys ) {
* Example: get( $a, [ 0, 1, 2 ] ) returns the value of $a[0][1][2] or the default.
*
* @param array|object|mixed $variable Array or object to search within.
* @param array|string|int|null $indexes Specify each nested index in order.
* Example: array( 'lvl1', 'lvl2' );
* @param array|string|int|null $indexes Specify each nested index in order. Can also be in dot notation.
* Example: array( 'lvl1', 'lvl2' ) or 'lvl1.lvl2'.
* @param mixed $default Default value if the search finds nothing.
*
* @return mixed The value of the specified index or the default if not found.
* @throws \InvalidArgumentException If the provided variable is not an array and does not implement ArrayAccess.
*/
public static function get( $variable, $indexes, $default = null ) {
if ( is_object( $variable ) ) {
$variable = static::wrap( $variable );
}

if ( ! static::accessible( $variable ) ) {
return $default;
throw new \InvalidArgumentException( 'The provided variable is not an array and does not implement ArrayAccess.' );
}

if ( is_null( $indexes ) ) {
return $variable;
}

if ( is_string( $indexes ) && isset( $variable[ $indexes ] ) ) {
return $variable[ $indexes ];
} elseif ( is_string( $indexes ) ) {
$indexes = explode( '.', $indexes );
}

$indexes = static::wrap( $indexes );

foreach ( $indexes as $index ) {
Expand Down Expand Up @@ -553,12 +552,40 @@ public static function has( $array, $indexes ) {
return false;
}

if ( isset( $array[ $indexes ] ) ) {
return true;
}

if ( is_string( $indexes ) ) {
$indexes = explode( '.', $indexes );
}

// Convert strings and such to array.
$indexes = static::wrap( $indexes );

foreach ( $indexes as $index ) {
if ( ! static::exists( $array, $index ) ) {
// Setup a pointer that we can point to the key specified.
$key_pointer = &$array;

// Iterate through every key, setting the pointer one level deeper each time.
foreach ( $indexes as $i ) {

// Ensure current array depth can have children set.
if ( ! is_array( $key_pointer ) ) {
// $key_pointer is set but is not an array. Converting it to an array
// would likely lead to unexpected problems for whatever first set it.
$error = sprintf(
'Attempted to set $array[%1$s] but %2$s is already set and is not an array.',
implode( '][', $indexes ),
$i
);

throw new \RuntimeException( $error );
} elseif ( ! isset( $key_pointer[ $i ] ) ) {
return false;
}

// Dive one level deeper into the nested array.
$key_pointer = &$key_pointer[ $i ];
}

return true;
Expand Down Expand Up @@ -722,7 +749,7 @@ public static function list_to_array( $value, $sep = ',' ): array {
}

if ( ! is_array( $value ) ) {
$value = preg_split( '/\\s*' . preg_quote( $sep ) . '\\s*/', $value );
$value = preg_split( '/\\s*' . preg_quote( $sep, '/' ) . '\\s*/', $value );
}

$filtered = [];
Expand Down Expand Up @@ -794,10 +821,12 @@ public static function merge_recursive( array &$array1, array &$array2 ): array
$merged = $array1;

foreach ( $array2 as $key => &$value ) {
if ( is_array( $value ) && isset( $merged [ $key ] ) && is_array( $merged [ $key ] ) ) {
$merged [ $key ] = static::merge_recursive( $merged [ $key ], $value );
if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
$merged[ $key ] = static::merge_recursive( $merged[ $key ], $value );
} else if ( is_int( $key ) ) {
$merged[] = $value;
} else {
$merged [ $key ] = $value;
$merged[ $key ] = $value;
}
}

Expand Down Expand Up @@ -991,17 +1020,19 @@ public static function random( $array, $number = null, $preserveKeys = false ) {
* @return bool The sorting result.
*/
public static function recursive_ksort( array &$array ): bool {
// First recursively sort all sub-arrays
foreach ( $array as &$value ) {
if ( is_array( $value ) ) {
static::recursive_ksort( $value );
}
}

return ksort( $array );
// Then sort the current array, ensuring numeric keys are sorted numerically
return ksort( $array, SORT_NATURAL );
}

/**
* Recursively remove associative, non numeric, keys from an array.
* Recursively remove numeric keys from an array.
*
* @since 1.0.0
*
Expand Down Expand Up @@ -1069,8 +1100,7 @@ public static function set( $array, $key, $value ): array {
$i
);

_doing_it_wrong( __FUNCTION__, esc_html( $error ), '4.3' );
break;
throw new \InvalidArgumentException( $error );
Comment on lines -1072 to +1103
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

} elseif ( ! isset( $key_pointer[ $i ] ) ) {
$key_pointer[ $i ] = [];
}
Expand Down Expand Up @@ -1153,7 +1183,7 @@ public static function shuffle( $array, $seed = null ) {
*/
public static function sort_by_priority( $array ): array {
if ( ! is_array( $array ) ) {
return $array;
throw new \InvalidArgumentException( 'Input must be an array' );
}

if ( static::is_assoc( $array ) ) {
Expand Down Expand Up @@ -1249,7 +1279,7 @@ public static function sort_recursive_desc( $array, $options = SORT_REGULAR ) {
* @return array<string,mixed> The input array with each numeric key stringified.
*/
public static function stringify_keys( array $input, $prefix = null ): array {
$prefix = null === $prefix ? uniqid( 'sk_', true ) : $prefix;
$prefix = null === $prefix ? uniqid( 'sk_' ) : $prefix;
$visitor = static function( $key, $value ) use ( $prefix ) {
$string_key = is_numeric( $key ) ? $prefix . $key : $key;

Expand All @@ -1273,6 +1303,10 @@ public static function stringify_keys( array $input, $prefix = null ): array {
public static function strpos( string $haystack, $needles, int $offset = 0 ) {
$needles = static::wrap( $needles );

$needles = array_filter( $needles, static function( $needle ) {
return is_string( $needle ) && $needle !== '';
} );

foreach ( $needles as $i ) {
$search = strpos( $haystack, $i, $offset );

Expand All @@ -1295,8 +1329,12 @@ public static function strpos( string $haystack, $needles, int $offset = 0 ) {
* @return string The list separated by the specified separator or the original list if the list is empty.
*/
public static function to_list( $list, string $sep = ',' ): string {
if ( $list === null ) {
return '';
}

if ( empty( $list ) ) {
return $list;
return is_array( $list ) ? '' : $list;
}

if ( is_array( $list ) ) {
Expand Down
15 changes: 15 additions & 0 deletions tests/wpunit/AccessibleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
namespace StellarWP\Arrays;

use StellarWP\Arrays\Tests\ArraysTestCase;

final class AccessibleTest extends ArraysTestCase {

public function test_accessible() {
$this->assertTrue(Arr::accessible([]));
$this->assertTrue(Arr::accessible([ 'foo' => 'bar' ]));
$this->assertTrue(Arr::accessible(new \ArrayObject([ 'foo' => 'bar' ])));
$this->assertFalse(Arr::accessible('foo'));
$this->assertFalse(Arr::accessible(23));
}
}
Loading
Loading