Skip to content

Commit

Permalink
Allow Hash::remove() to remove from ArrayAccess objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Corey Taylor committed Apr 27, 2024
1 parent cf60b26 commit 7129958
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .phive/phars.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="psalm" version="5.20.0" installed="5.20.0" location="./tools/psalm" copy="false"/>
<phar name="psalm" version="5.23.1" installed="5.23.1" location="./tools/psalm" copy="false"/>
</phive>
15 changes: 11 additions & 4 deletions phpstan-baseline.neon
Expand Up @@ -14,15 +14,17 @@ parameters:
message: "#^Property Cake\\\\Console\\\\ConsoleInput\\:\\:\\$_input \\(resource\\) in isset\\(\\) is not nullable\\.$#"
count: 1
path: src/Console/ConsoleInput.php
-
message: "#^Property Cake\\\\Console\\\\ConsoleOutput\\:\\:\\$_output \\(resource\\) in isset\\(\\) is not nullable\\.$#"
count: 2
path: src/Console/ConsoleOutput.php

-
message: "#^Unsafe usage of new static\\(\\)\\.$#"
count: 2
path: src/Console/ConsoleOptionParser.php

-
message: "#^Property Cake\\\\Console\\\\ConsoleOutput\\:\\:\\$_output \\(resource\\) in isset\\(\\) is not nullable\\.$#"
count: 2
path: src/Console/ConsoleOutput.php

-
message: "#^Unsafe usage of new static\\(\\)\\.$#"
count: 8
Expand Down Expand Up @@ -118,6 +120,11 @@ parameters:
count: 1
path: src/TestSuite/TestCase.php

-
message: "#^Unable to resolve the template type T in call to method static method Cake\\\\Utility\\\\Hash\\:\\:insert\\(\\)$#"
count: 1
path: src/Utility/Hash.php

-
message: "#^Unsafe usage of new static\\(\\)\\.$#"
count: 1
Expand Down
115 changes: 60 additions & 55 deletions psalm-baseline.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.20.0@3f284e96c9d9be6fe6b15c79416e1d1903dcfef4">
<files psalm-version="5.23.1@8471a896ccea3526b26d082f4461eeea467f10a4">
<file src="src/Cache/Engine/FileEngine.php">
<TooManyTemplateParams>
<code>$iterator</code>
<code><![CDATA[$iterator]]></code>
</TooManyTemplateParams>
</file>
<file src="src/Cache/Engine/RedisEngine.php">
Expand All @@ -15,93 +15,93 @@
<InvalidReturnStatement>
<code><![CDATA[$this->_Redis->set($key, $value)]]></code>
<code><![CDATA[$this->_Redis->setEx($key, $duration, $value)]]></code>
<code>$value</code>
<code>$value</code>
<code><![CDATA[$value]]></code>
<code><![CDATA[$value]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code>bool</code>
<code>int|false</code>
<code>int|false</code>
<code><![CDATA[bool]]></code>
<code><![CDATA[int|false]]></code>
<code><![CDATA[int|false]]></code>
</InvalidReturnType>
</file>
<file src="src/Core/PluginConfig.php">
<RedundantCondition>
<code>is_array($pluginLoadConfig)</code>
<code><![CDATA[is_array($pluginLoadConfig)]]></code>
</RedundantCondition>
</file>
<file src="src/Event/EventDispatcherTrait.php">
<MoreSpecificImplementedParamType>
<code>$subject</code>
<code><![CDATA[$subject]]></code>
</MoreSpecificImplementedParamType>
</file>
<file src="src/Event/EventManager.php">
<InvalidArgument>
<code>_callListener</code>
<code>addEventToList</code>
<code>addEventToList</code>
<code><![CDATA[_callListener]]></code>
<code><![CDATA[addEventToList]]></code>
<code><![CDATA[addEventToList]]></code>
</InvalidArgument>
<InvalidReturnStatement>
<code>$event</code>
<code>$event</code>
<code><![CDATA[$event]]></code>
<code><![CDATA[$event]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code>EventInterface</code>
<code><![CDATA[EventInterface]]></code>
</InvalidReturnType>
</file>
<file src="src/I18n/Date.php">
<ImpureFunctionCall>
<code>call_user_func(static::$_jsonEncodeFormat, $this)</code>
<code>static::$_jsonEncodeFormat</code>
<code><![CDATA[call_user_func(static::$_jsonEncodeFormat, $this)]]></code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
</ImpureFunctionCall>
<ImpureMethodCall>
<code>_formatObject</code>
<code>dateAgoInWords</code>
<code>diffFormatter</code>
<code>getDefaultLocale</code>
<code><![CDATA[_formatObject]]></code>
<code><![CDATA[dateAgoInWords]]></code>
<code><![CDATA[diffFormatter]]></code>
<code><![CDATA[getDefaultLocale]]></code>
</ImpureMethodCall>
<ImpureStaticProperty>
<code>static::$_jsonEncodeFormat</code>
<code>static::$_jsonEncodeFormat</code>
<code>static::$_jsonEncodeFormat</code>
<code>static::$_toStringFormat</code>
<code>static::$niceFormat</code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
<code><![CDATA[static::$_toStringFormat]]></code>
<code><![CDATA[static::$niceFormat]]></code>
</ImpureStaticProperty>
</file>
<file src="src/I18n/DateTime.php">
<ImpureFunctionCall>
<code>call_user_func(static::$_jsonEncodeFormat, $this)</code>
<code>static::$_jsonEncodeFormat</code>
<code><![CDATA[call_user_func(static::$_jsonEncodeFormat, $this)]]></code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
</ImpureFunctionCall>
<ImpureMethodCall>
<code>_formatObject</code>
<code>diffFormatter</code>
<code>getDefaultLocale</code>
<code>timeAgoInWords</code>
<code><![CDATA[_formatObject]]></code>
<code><![CDATA[diffFormatter]]></code>
<code><![CDATA[getDefaultLocale]]></code>
<code><![CDATA[timeAgoInWords]]></code>
</ImpureMethodCall>
<ImpureStaticProperty>
<code>static::$_jsonEncodeFormat</code>
<code>static::$_jsonEncodeFormat</code>
<code>static::$_jsonEncodeFormat</code>
<code>static::$_toStringFormat</code>
<code>static::$niceFormat</code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
<code><![CDATA[static::$_toStringFormat]]></code>
<code><![CDATA[static::$niceFormat]]></code>
</ImpureStaticProperty>
</file>
<file src="src/I18n/Time.php">
<ImpureFunctionCall>
<code>call_user_func(static::$_jsonEncodeFormat, $this)</code>
<code>static::$_jsonEncodeFormat</code>
<code><![CDATA[call_user_func(static::$_jsonEncodeFormat, $this)]]></code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
</ImpureFunctionCall>
<ImpureMethodCall>
<code>_formatObject</code>
<code>getDefaultLocale</code>
<code>toNative</code>
<code><![CDATA[_formatObject]]></code>
<code><![CDATA[getDefaultLocale]]></code>
<code><![CDATA[toNative]]></code>
</ImpureMethodCall>
<ImpureStaticProperty>
<code>static::$_jsonEncodeFormat</code>
<code>static::$_jsonEncodeFormat</code>
<code>static::$_jsonEncodeFormat</code>
<code>static::$_toStringFormat</code>
<code>static::$niceFormat</code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
<code><![CDATA[static::$_jsonEncodeFormat]]></code>
<code><![CDATA[static::$_toStringFormat]]></code>
<code><![CDATA[static::$niceFormat]]></code>
</ImpureStaticProperty>
</file>
<file src="src/TestSuite/Constraint/EventFired.php">
Expand Down Expand Up @@ -148,27 +148,32 @@
</file>
<file src="src/TestSuite/Constraint/Session/FlashParamEquals.php">
<InternalClass>
<code>new AssertionFailedError($message)</code>
<code><![CDATA[new AssertionFailedError($message)]]></code>
</InternalClass>
<InternalMethod>
<code>new AssertionFailedError($message)</code>
<code><![CDATA[new AssertionFailedError($message)]]></code>
</InternalMethod>
</file>
<file src="src/TestSuite/TestCase.php">
<UndefinedVariable>
<code>$previousHandler</code>
<code><![CDATA[$previousHandler]]></code>
</UndefinedVariable>
</file>
<file src="src/Utility/Filesystem.php">
<TooManyTemplateParams>
<code>$iterator</code>
<code>$iterator</code>
<code><![CDATA[$iterator]]></code>
<code><![CDATA[$iterator]]></code>
</TooManyTemplateParams>
</file>
<file src="src/Utility/Hash.php">
<RedundantCondition>
<code>is_array($_list)</code>
</RedundantCondition>
<InvalidReturnStatement>
<code><![CDATA[$data]]></code>
<code><![CDATA[$data]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code><![CDATA[(T is array ? array : \ArrayAccess)]]></code>
<code><![CDATA[(T is array ? array : \ArrayAccess)]]></code>
</InvalidReturnType>
</file>
<file src="src/Validation/Validation.php">
<RedundantCondition>
Expand Down
1 change: 0 additions & 1 deletion src/Http/ServerRequest.php
Expand Up @@ -1562,7 +1562,6 @@ public function withoutAttribute(string $name): static
* @param string $name The attribute name.
* @param mixed $default The default value if the attribute has not been set.
* @return mixed
* @psalm-suppress MethodSignatureMismatch
*/
public function getAttribute(string $name, mixed $default = null): mixed
{
Expand Down
43 changes: 30 additions & 13 deletions src/Utility/Hash.php
Expand Up @@ -16,6 +16,7 @@
namespace Cake\Utility;

use ArrayAccess;
use Cake\Core\Exception\CakeException;
use InvalidArgumentException;
use const SORT_ASC;
use const SORT_DESC;
Expand Down Expand Up @@ -288,13 +289,15 @@ protected static function _matches(ArrayAccess|array $data, string $selector): b
* Insert $values into an array with the given $path. You can use
* `{n}` and `{s}` elements to insert $data multiple times.
*
* @param array $data The data to insert into.
* @template T of \ArrayAccess|array
* @param T $data The data to insert into.
* @param string $path The path to insert at.
* @param mixed $values The values to insert.
* @return array The data with $values inserted.
* @return \ArrayAccess|array The data with $values inserted.
* @psalm-return (T is array ? array : \ArrayAccess)
* @link https://book.cakephp.org/5/en/core-libraries/hash.html#Cake\Utility\Hash::insert
*/
public static function insert(array $data, string $path, mixed $values = null): array
public static function insert(ArrayAccess|array $data, string $path, mixed $values = null): ArrayAccess|array
{
$noTokens = !str_contains($path, '[');
if ($noTokens && !str_contains($path, '.')) {
Expand All @@ -313,6 +316,10 @@ public static function insert(array $data, string $path, mixed $values = null):
return static::_simpleOp('insert', $data, $tokens, $values);
}

if (!is_iterable($data)) {
throw new CakeException('Cannot use path tokens of type `{}` or `[]` for non-iterable objects.');
}

/** @var string $token */
$token = array_shift($tokens);
$nextPath = implode('.', $tokens);
Expand All @@ -337,13 +344,17 @@ public static function insert(array $data, string $path, mixed $values = null):
* Perform a simple insert/remove operation.
*
* @param string $op The operation to do.
* @param array $data The data to operate on.
* @param \ArrayAccess|array $data The data to operate on.
* @param list<string> $path The path to work on.
* @param mixed $values The values to insert when doing inserts.
* @return array data.
* @return \ArrayAccess|array
*/
protected static function _simpleOp(string $op, array $data, array $path, mixed $values = null): array
{
protected static function _simpleOp(
string $op,
ArrayAccess|array $data,
array $path,
mixed $values = null
): ArrayAccess|array {
$_list = &$data;

$count = count($path);
Expand All @@ -357,12 +368,12 @@ protected static function _simpleOp(string $op, array $data, array $path, mixed
}
$_list[$key] ??= [];
$_list = &$_list[$key];
if (!is_array($_list)) {
if (!is_array($_list) && !$_list instanceof ArrayAccess) {
$_list = [];
}
} elseif ($op === 'remove') {
if ($i === $last) {
if (is_array($_list)) {
if (is_array($_list) || $_list instanceof ArrayAccess) {
unset($_list[$key]);
}

Expand All @@ -383,12 +394,14 @@ protected static function _simpleOp(string $op, array $data, array $path, mixed
* You can use `{n}` and `{s}` to remove multiple elements
* from $data.
*
* @param array $data The data to operate on
* @template T of \ArrayAccess|array
* @param T $data The data to operate on
* @param string $path A path expression to use to remove.
* @return array The modified array.
* @return \ArrayAccess|array The modified array.
* @psalm-return (T is array ? array : \ArrayAccess)
* @link https://book.cakephp.org/5/en/core-libraries/hash.html#Cake\Utility\Hash::remove
*/
public static function remove(array $data, string $path): array
public static function remove(ArrayAccess|array $data, string $path): ArrayAccess|array
{
$noTokens = !str_contains($path, '[');
$noExpansion = !str_contains($path, '{');
Expand All @@ -405,6 +418,10 @@ public static function remove(array $data, string $path): array
return static::_simpleOp('remove', $data, $tokens);
}

if (!is_iterable($data)) {
throw new CakeException('Cannot use path tokens of type `{}` or `[]` for non-iterable objects.');
}

/** @var string $token */
$token = array_shift($tokens);
$nextPath = implode('.', $tokens);
Expand All @@ -413,7 +430,7 @@ public static function remove(array $data, string $path): array

foreach ($data as $k => $v) {
$match = static::_matchToken($k, $token);
if ($match && is_array($v)) {
if ($match && (is_array($v) || $v instanceof ArrayAccess)) {
if ($conditions) {
if (static::_matches($v, $conditions)) {
if ($nextPath !== '') {
Expand Down

0 comments on commit 7129958

Please sign in to comment.