Skip to content
Open
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
105 changes: 73 additions & 32 deletions system/Helpers/Array/ArrayHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ private static function arraySearchDot(array $indexes, array|object $array)
*
* If wildcard `*` is used, all items for the key after it must have the key.
*
* @param array<array-key, mixed> $array
* @param array<array-key, mixed>|object $array
*/
public static function dotHas(string $index, array $array): bool
public static function dotHas(string $index, array|object $array): bool
{
self::ensureValidWildcardPattern($index);

Expand All @@ -164,10 +164,10 @@ public static function dotHas(string $index, array $array): bool
/**
* Recursively check key existence by dot path, including wildcard support.
*
* @param array<array-key, mixed> $array
* @param list<string> $indexes
* @param array<array-key, mixed>|object $array
* @param list<string> $indexes
*/
private static function hasByDotPath(array $array, array $indexes): bool
private static function hasByDotPath(array|object $array, array $indexes): bool
{
if ($indexes === []) {
return true;
Expand All @@ -176,28 +176,32 @@ private static function hasByDotPath(array $array, array $indexes): bool
$currentIndex = array_shift($indexes);

if ($currentIndex === '*') {
foreach ($array as $item) {
if (! is_array($item) || ! self::hasByDotPath($item, $indexes)) {
$iterable = is_object($array) ? self::toIterable($array) : $array;

foreach ($iterable as $item) {
if ((! is_array($item) && ! is_object($item)) || ! self::hasByDotPath($item, $indexes)) {
return false;
}
}

return true;
}

if (! array_key_exists($currentIndex, $array)) {
if (! self::valueExists($array, $currentIndex)) {
return false;
}

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

if (! is_array($array[$currentIndex])) {
$value = self::value($array, $currentIndex);

if (! is_array($value) && ! is_object($value)) {
return false;
}

return self::hasByDotPath($array[$currentIndex], $indexes);
return self::hasByDotPath($value, $indexes);
}

/**
Expand Down Expand Up @@ -247,12 +251,12 @@ public static function dotUnset(array &$array, string $index): bool
/**
* Gets only the specified keys using dot syntax.
*
* @param array<array-key, mixed> $array
* @param list<string>|string $indexes
* @param array<array-key, mixed>|object $array
* @param list<string>|string $indexes
*
* @return array<array-key, mixed>
*/
public static function dotOnly(array $array, array|string $indexes): array
public static function dotOnly(array|object $array, array|string $indexes): array
{
$indexes = is_string($indexes) ? [$indexes] : $indexes;
$result = [];
Expand All @@ -261,7 +265,7 @@ public static function dotOnly(array $array, array|string $indexes): array
self::ensureValidWildcardPattern($index, true);

if ($index === '*') {
$result = [...$result, ...$array];
$result = [...$result, ...(is_object($array) ? self::toIterable($array) : $array)];

continue;
}
Expand All @@ -280,15 +284,15 @@ public static function dotOnly(array $array, array|string $indexes): array
/**
* Gets all keys except the specified ones using dot syntax.
*
* @param array<array-key, mixed> $array
* @param list<string>|string $indexes
* @param array<array-key, mixed>|object $array
* @param list<string>|string $indexes
*
* @return array<array-key, mixed>
*/
public static function dotExcept(array $array, array|string $indexes): array
public static function dotExcept(array|object $array, array|string $indexes): array
{
$indexes = is_string($indexes) ? [$indexes] : $indexes;
$result = $array;
$result = self::toArrayView($array);

foreach ($indexes as $index) {
self::ensureValidWildcardPattern($index, true);
Expand Down Expand Up @@ -466,20 +470,20 @@ public static function sortValuesByNatural(array &$array, $sortByIndex = null):
private static function valueExists(array|object $data, string $key): bool
{
if (is_array($data)) {
return isset($data[$key]);
return array_key_exists($key, $data);
}

$array = self::entityToArray($data);

if ($array !== null) {
return isset($array[$key]);
return array_key_exists($key, $array);
}

if ($data instanceof ArrayAccess && $data->offsetExists($key)) {
return true;
}

if (isset(get_object_vars($data)[$key])) {
if (array_key_exists($key, get_object_vars($data))) {
return true;
}

Expand Down Expand Up @@ -550,6 +554,26 @@ private static function toIterable(object $data): array
return get_object_vars($data);
}

/**
* Normalize arrays or objects to an array view safe for dotExcept().
*
* @param array<array-key, mixed>|object $data
*
* @return array<array-key, mixed>
*/
private static function toArrayView(array|object $data): array
{
$array = is_object($data) ? self::toIterable($data) : $data;

foreach ($array as $key => $value) {
if (is_array($value) || is_object($value)) {
$array[$key] = self::toArrayView($value);
}
}

return $array;
}

/**
* Throws exception for invalid wildcard patterns.
*/
Expand Down Expand Up @@ -688,14 +712,15 @@ private static function clearByDotPath(array &$array, array $indexes): int
}

/**
* Projects matching paths from source array into result with preserved structure.
* Projects matching paths from source into result with preserved structure.
*
* @param list<string> $indexes
* @param list<string> $prefix
* @param array<array-key, mixed> $result
* @param array<array-key, mixed>|object $source
* @param list<string> $indexes
* @param list<string> $prefix
* @param array<array-key, mixed> $result
*/
private static function projectByDotPath(
mixed $source,
array|object $source,
array $indexes,
array &$result,
array $prefix = [],
Expand All @@ -709,21 +734,37 @@ private static function projectByDotPath(
$currentIndex = array_shift($indexes);

if ($currentIndex === '*') {
if (! is_array($source)) {
return;
}
$iterable = is_object($source) ? self::toIterable($source) : $source;

foreach ($iterable as $key => $value) {
if (! is_array($value) && ! is_object($value)) {
if ($indexes === []) {
self::setByDotPath($result, [...$prefix, (string) $key], $value);
}

continue;
}

foreach ($source as $key => $value) {
self::projectByDotPath($value, $indexes, $result, [...$prefix, (string) $key]);
}

return;
}

if (! is_array($source) || ! array_key_exists($currentIndex, $source)) {
if (! self::valueExists($source, $currentIndex)) {
return;
}

$value = self::value($source, $currentIndex);

if (! is_array($value) && ! is_object($value)) {
if ($indexes === []) {
self::setByDotPath($result, [...$prefix, $currentIndex], $value);
}

return;
}

self::projectByDotPath($source[$currentIndex], $indexes, $result, [...$prefix, $currentIndex]);
self::projectByDotPath($value, $indexes, $result, [...$prefix, $currentIndex]);
}
}
16 changes: 8 additions & 8 deletions system/Helpers/array_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ function dot_array_search(string $index, array|object $array)
/**
* Checks if an array key exists using dot syntax.
*
* @param array<array-key, mixed> $array
* @param array<array-key, mixed>|object $array
*/
function dot_array_has(string $index, array $array): bool
function dot_array_has(string $index, array|object $array): bool
{
return ArrayHelper::dotHas($index, $array);
}
Expand Down Expand Up @@ -70,12 +70,12 @@ function dot_array_unset(array &$array, string $index): bool
/**
* Gets only the specified keys using dot syntax.
*
* @param array<array-key, mixed> $array
* @param list<string>|string $indexes
* @param array<array-key, mixed>|object $array
* @param list<string>|string $indexes
*
* @return array<array-key, mixed>
*/
function dot_array_only(array $array, array|string $indexes): array
function dot_array_only(array|object $array, array|string $indexes): array
{
return ArrayHelper::dotOnly($array, $indexes);
}
Expand All @@ -85,12 +85,12 @@ function dot_array_only(array $array, array|string $indexes): array
/**
* Gets all keys except the specified ones using dot syntax.
*
* @param array<array-key, mixed> $array
* @param list<string>|string $indexes
* @param array<array-key, mixed>|object $array
* @param list<string>|string $indexes
*
* @return array<array-key, mixed>
*/
function dot_array_except(array $array, array|string $indexes): array
function dot_array_except(array|object $array, array|string $indexes): array
{
return ArrayHelper::dotExcept($array, $indexes);
}
Expand Down
Loading
Loading