From 84b38085ed3ab2eb628b3d03dce0d24067caa45c Mon Sep 17 00:00:00 2001
From: Lars Moelleken
Date: Mon, 27 Jul 2026 00:09:06 +0200
Subject: [PATCH] Audit fourth PHPStan ignore chunk
---
CHANGELOG.md | 4 +
README.md | 20 +-
build/docs/base.md | 20 +-
phpstan.neon | 8 +
src/Arrayy.php | 288 +++++----
src/Collection/AbstractCollection.php | 4 +-
src/Create.php | 2 +-
src/Mapper/Json.php | 29 +-
.../DefaultDotNotationTypeInterface.php | 15 +
.../GetDynamicMethodReturnTypeExtension.php | 140 ++++
...DynamicStaticMethodReturnTypeExtension.php | 2 +-
src/Type/DetectFirstValueTypeCollection.php | 2 +-
src/TypeCheck/TypeCheckCallback.php | 2 +-
src/TypeCheck/TypeCheckPhpDoc.php | 26 +-
tests/Account.php | 2 +-
tests/ArrayyTest.php | 610 ++++++++++--------
tests/BasicArrayTest.php | 140 ++--
tests/Collection/BoolTypeTest.php | 2 +-
tests/Collection/CollectionTest.php | 14 +-
tests/Collection/StringTypeTest.php | 5 +-
tests/Collection/TypeTypeTest.php | 3 +-
tests/InfrastructureCoverageTest.php | 63 ++
tests/JsonMapperCoverageTest.php | 10 +-
tests/JsonMapperTest.php | 1 +
tests/MetaPhpStanIntegrationTest.php | 2 +-
tests/ModelA.php | 2 +-
tests/PHPStan/AccessShapeProfile.php | 28 +
tests/PHPStan/AccessShapeUser.php | 27 +
tests/PHPStan/AccessWaysTest.php | 52 ++
tests/PHPStan/AnalyseTest.php | 10 +-
.../PHPStan/CallableGenericInferenceTest.php | 64 ++
tests/PHPStan/CustomSeparatorAccessUser.php | 12 +
...etDynamicMethodReturnTypeExtensionTest.php | 123 ++++
tests/PHPStan/phpstan-fixtures.neon | 12 +
tests/TypeCheckCoreCoverageTest.php | 130 ++--
35 files changed, 1278 insertions(+), 596 deletions(-)
create mode 100644 src/PHPStan/DefaultDotNotationTypeInterface.php
create mode 100644 src/PHPStan/GetDynamicMethodReturnTypeExtension.php
create mode 100644 tests/PHPStan/AccessShapeProfile.php
create mode 100644 tests/PHPStan/AccessShapeUser.php
create mode 100644 tests/PHPStan/AccessWaysTest.php
create mode 100644 tests/PHPStan/CallableGenericInferenceTest.php
create mode 100644 tests/PHPStan/CustomSeparatorAccessUser.php
create mode 100644 tests/PHPStan/GetDynamicMethodReturnTypeExtensionTest.php
create mode 100644 tests/PHPStan/phpstan-fixtures.neon
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aa24e48..99d3fd9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
### Upcoming release
+- add PHPStan inference for literal `Arrayy::get()` dot-notation paths on typed subclasses while keeping custom path separators sound
+- improve callable generic inference and preserve transformed value types across `each()` and `map()`
+- fix nested dot-notation removal so removing a deep key preserves the root array and sibling values
+- preserve `Traversable` entries when mapping JSON data and harden array/object path traversal around scalar intermediates
- fix `average()` so non-numeric values no longer error on modern PHP versions
- make `changeKeyCase()` Unicode case conversion deterministic across PHP 8.0–8.5
- strengthen native property type checks, array-shape contracts, and regression coverage across Json mapper and collection helpers
diff --git a/README.md b/README.md
index 681125e..d132fd3 100644
--- a/README.md
+++ b/README.md
@@ -120,12 +120,26 @@ $arrayy->Lars->lastname; // 'Müller'
The library offers type checking for phpdoc array-shape annotations, legacy `@property` phpdoc-class-comments, and native declared properties. Prefer the array-shape form because it can reuse the `Arrayy` template for IDE autocompletion and static-analysis support. `meta()` is also understood by PHPStan, so `meta()`-derived keys such as `$userMeta->city` and `$cityMeta->name` keep precise literal-string information during static analysis. When you want PHPStan to check reads precisely, prefer array-like access with literal keys (for example `$user['lastName']`) or narrowed `meta()` keys on these array-shape-based models. Do not combine array-shape annotations and `@property` tags on the same model.
-If you use PHPStan and call `YourArrayySubclass::meta()`, you can register the custom return-type extension from `src/PHPStan/MetaDynamicStaticMethodReturnTypeExtension.php`. Use it when you want PHPStan to understand that `meta()` returns an object shape whose properties are the exact array keys collected from your array-shape annotations, `@property` tags, or native declared properties. That keeps expressions such as `$userMeta->id` typed as the literal string `'id'`, helps nested lookups like `$user[$userMeta->city][$cityMeta->name]`, and lets PHPStan report invalid meta-property access such as `$userMeta->ghost`.
+If you use PHPStan, register the return-type extensions from `src/PHPStan`. `GetDynamicMethodReturnTypeExtension` resolves literal dot-notation paths against a typed subclass's `TData` array shape, including fallback values. `MetaDynamicStaticMethodReturnTypeExtension` understands that `meta()` returns an object shape whose properties are the exact keys collected from array-shape annotations, `@property` tags, or native declared properties.
+
+All three access styles can therefore participate in static analysis:
+
+```php
+$name = $user->get('profile.name', 'Guest'); // dot path resolved from TData
+$name = $user['profile']['name']; // ArrayAccess / array-shape inference
+$name = $user->profile->name; // @property-read declarations
+```
+
+Dot-notation inference intentionally applies to literal dotted paths on typed `Arrayy` subclasses with a stable array-shape `TData` that implement `Arrayy\PHPStan\DefaultDotNotationTypeInterface`. Implementing this marker is a promise that the subclass keeps Arrayy's default `.` separator and does not switch it with `changeSeparator()`; without that promise, splitting the path statically would be unsound. Dynamic strings, wildcard paths, custom separators, and plain `Arrayy` instances retain the method's safe general return type. Object-property access should be declared with `@property` or `@property-read`; `meta()` keeps generated key access precise.
The repository's own `phpstan.neon` registers the extension like this; copy the same service definition into your project's PHPStan config because this repository file is not shipped in the Composer package:
```neon
services:
+ -
+ class: Arrayy\PHPStan\GetDynamicMethodReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
-
class: Arrayy\PHPStan\MetaDynamicStaticMethodReturnTypeExtension
tags:
@@ -137,7 +151,7 @@ services:
* @template T of array{id: int, firstName: int|string, lastName: string, city?: City|null}
* @extends \Arrayy\Arrayy,value-of,T>
*/
-class User extends \Arrayy\Arrayy
+class User extends \Arrayy\Arrayy implements \Arrayy\PHPStan\DefaultDotNotationTypeInterface
{
protected $checkPropertyTypes = true;
@@ -148,7 +162,7 @@ class User extends \Arrayy\Arrayy
* @template T of array{plz: string|null, name: string, infos: string[]}
* @extends \Arrayy\Arrayy,value-of,T>
*/
-class City extends \Arrayy\Arrayy
+class City extends \Arrayy\Arrayy implements \Arrayy\PHPStan\DefaultDotNotationTypeInterface
{
protected $checkPropertyTypes = true;
diff --git a/build/docs/base.md b/build/docs/base.md
index ec23151..2766f77 100644
--- a/build/docs/base.md
+++ b/build/docs/base.md
@@ -119,12 +119,26 @@ $arrayy->Lars->lastname; // 'Müller'
The library offers type checking for phpdoc array-shape annotations, legacy `@property` phpdoc-class-comments, and native declared properties. Prefer the array-shape form because it can reuse the `Arrayy` template for IDE autocompletion and static-analysis support. `meta()` is also understood by PHPStan, so `meta()`-derived keys such as `$userMeta->city` and `$cityMeta->name` keep precise literal-string information during static analysis. When you want PHPStan to check reads precisely, prefer array-like access with literal keys (for example `$user['lastName']`) or narrowed `meta()` keys on these array-shape-based models. Do not combine array-shape annotations and `@property` tags on the same model.
-If you use PHPStan and call `YourArrayySubclass::meta()`, you can register the custom return-type extension from `src/PHPStan/MetaDynamicStaticMethodReturnTypeExtension.php`. Use it when you want PHPStan to understand that `meta()` returns an object shape whose properties are the exact array keys collected from your array-shape annotations, `@property` tags, or native declared properties. That keeps expressions such as `$userMeta->id` typed as the literal string `'id'`, helps nested lookups like `$user[$userMeta->city][$cityMeta->name]`, and lets PHPStan report invalid meta-property access such as `$userMeta->ghost`.
+If you use PHPStan, register the return-type extensions from `src/PHPStan`. `GetDynamicMethodReturnTypeExtension` resolves literal dot-notation paths against a typed subclass's `TData` array shape, including fallback values. `MetaDynamicStaticMethodReturnTypeExtension` understands that `meta()` returns an object shape whose properties are the exact keys collected from array-shape annotations, `@property` tags, or native declared properties.
+
+All three access styles can therefore participate in static analysis:
+
+```php
+$name = $user->get('profile.name', 'Guest'); // dot path resolved from TData
+$name = $user['profile']['name']; // ArrayAccess / array-shape inference
+$name = $user->profile->name; // @property-read declarations
+```
+
+Dot-notation inference intentionally applies to literal dotted paths on typed `Arrayy` subclasses with a stable array-shape `TData` that implement `Arrayy\PHPStan\DefaultDotNotationTypeInterface`. Implementing this marker is a promise that the subclass keeps Arrayy's default `.` separator and does not switch it with `changeSeparator()`; without that promise, splitting the path statically would be unsound. Dynamic strings, wildcard paths, custom separators, and plain `Arrayy` instances retain the method's safe general return type. Object-property access should be declared with `@property` or `@property-read`; `meta()` keeps generated key access precise.
The repository's own `phpstan.neon` registers the extension like this; copy the same service definition into your project's PHPStan config because this repository file is not shipped in the Composer package:
```neon
services:
+ -
+ class: Arrayy\PHPStan\GetDynamicMethodReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
-
class: Arrayy\PHPStan\MetaDynamicStaticMethodReturnTypeExtension
tags:
@@ -136,7 +150,7 @@ services:
* @template T of array{id: int, firstName: int|string, lastName: string, city?: City|null}
* @extends \Arrayy\Arrayy,value-of,T>
*/
-class User extends \Arrayy\Arrayy
+class User extends \Arrayy\Arrayy implements \Arrayy\PHPStan\DefaultDotNotationTypeInterface
{
protected $checkPropertyTypes = true;
@@ -147,7 +161,7 @@ class User extends \Arrayy\Arrayy
* @template T of array{plz: string|null, name: string, infos: string[]}
* @extends \Arrayy\Arrayy,value-of,T>
*/
-class City extends \Arrayy\Arrayy
+class City extends \Arrayy\Arrayy implements \Arrayy\PHPStan\DefaultDotNotationTypeInterface
{
protected $checkPropertyTypes = true;
diff --git a/phpstan.neon b/phpstan.neon
index ff963b3..2b32038 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,11 +1,19 @@
parameters:
level: 8
reportUnmatchedIgnoredErrors: true
+ excludePaths:
+ analyse:
+ - %currentWorkingDirectory%/tests/PHPStan/ArrayShapeInvalidUsage.php
+ - %currentWorkingDirectory%/tests/PHPStan/MetaInvalidUsage.php
paths:
- %currentWorkingDirectory%/src/
- %currentWorkingDirectory%/tests/
services:
+ -
+ class: Arrayy\PHPStan\GetDynamicMethodReturnTypeExtension
+ tags:
+ - phpstan.broker.dynamicMethodReturnTypeExtension
-
class: Arrayy\PHPStan\MetaDynamicStaticMethodReturnTypeExtension
tags:
diff --git a/src/Arrayy.php b/src/Arrayy.php
index 55f51ad..6ffe1b5 100644
--- a/src/Arrayy.php
+++ b/src/Arrayy.php
@@ -247,7 +247,7 @@ public function &__get($key)
if (\is_array($return) === true) {
$return = static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (an empty late-static instance cannot satisfy every possible invariant TData shape)
$this->iteratorClass,
false
)->createByReference($return);
@@ -282,7 +282,7 @@ public function add($value, $key = null)
);
}
- $this->internalSet($key, $value);
+ $this->internalSet($key, $value); // @phpstan-ignore-line argument.type (add() can promote an existing scalar to an array during its documented recursive merge)
return $this;
}
@@ -761,7 +761,7 @@ public function offsetExists($offset): bool
$this->callAtPath(
$containerPath,
static function ($container) use ($lastOffset, &$offsetExists) {
- $offsetExists = \array_key_exists($lastOffset, $container);
+ $offsetExists = \is_array($container) && \array_key_exists($lastOffset, $container);
}
);
}
@@ -788,10 +788,10 @@ public function &offsetGet($offset)
$value = null;
if ($this->offsetExists($offset)) {
- $value = &$this->__get($offset);
+ $value = &$this->__get($offset); // @phpstan-ignore-line argument.templateType, argument.type (the dynamic value is intentionally forwarded through an invariant generic boundary)
}
- return $value;
+ return $value; // @phpstan-ignore return.type (offsetGet() intentionally returns the referenced value selected at runtime)
}
/**
@@ -1067,7 +1067,7 @@ public function appendArrayValues(array $values, $key = null)
\is_array($this->array[$key])
) {
foreach ($values as $value) {
- $this->array[$key][] = $value;
+ $this->array[$key][] = $value; // @phpstan-ignore-line assign.propertyType (runtime normalization intentionally rebuilds the generic backing array)
}
} else {
foreach ($values as $value) {
@@ -1103,7 +1103,7 @@ public function appendToEachKey($prefix): self
if ($item instanceof self) {
$result[$prefix . $key] = $item->appendToEachKey($prefix);
} elseif (\is_array($item)) {
- $result[$prefix . $key] = self::create($item, $this->iteratorClass, false)
+ $result[$prefix . $key] = self::create($item, $this->iteratorClass, false) // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
->appendToEachKey($prefix)
->toArray();
} else {
@@ -1112,7 +1112,7 @@ public function appendToEachKey($prefix): self
}
return self::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -1138,7 +1138,7 @@ public function appendToEachValue($prefix): self
if ($item instanceof self) {
$result[$key] = $item->appendToEachValue($prefix);
} elseif (\is_array($item)) {
- $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray();
+ $result[$key] = self::create($item, $this->iteratorClass, false)->appendToEachValue($prefix)->toArray(); // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
} elseif (\is_object($item) === true) {
$result[$key] = $item;
} else {
@@ -1146,7 +1146,7 @@ public function appendToEachValue($prefix): self
}
}
- return self::create($result, $this->iteratorClass, false);
+ return self::create($result, $this->iteratorClass, false); // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
}
/**
@@ -1215,7 +1215,7 @@ public function at(\Closure $closure): self
}
return static::create(
- $that->toArray(),
+ $that->toArray(), // @phpstan-ignore-line argument.type (the runtime conversion crosses from the receiver templates into a freshly constructed result shape)
$this->iteratorClass,
false
);
@@ -1307,7 +1307,7 @@ public function changeKeyCase(int $case = \CASE_LOWER): self
}
return static::create(
- $return,
+ $return, // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -1345,7 +1345,7 @@ public function changeSeparator($separator): self
* @return static|static[]
* (Immutable) A new array of chunks from the original array.
*
- * @phpstan-return static
+ * @phpstan-return self>,array>>>
* @psalm-mutation-free
*/
public function chunk($size, $preserveKeys = false): self
@@ -1732,13 +1732,13 @@ public function containsValues(array $needles): bool
* keys and their count as value.
*
*
- * @phpstan-return static
+ * @phpstan-return static>
* @psalm-mutation-free
*/
public function countValues(): self
{
- /** @phpstan-var static $return - help for phpstan */
- $return = self::create(\array_count_values($this->toArray()), $this->iteratorClass);
+ /** @phpstan-var static> $return */
+ $return = self::create(\array_count_values($this->toArray()), $this->iteratorClass); // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
return $return;
}
@@ -1862,13 +1862,15 @@ public static function createFromGeneratorFunction(callable $generatorFunction):
* @return static
* (Immutable) Returns an new instance of the Arrayy object.
*
- * @phpstan-param \Generator $generator
- * @phpstan-return static
+ * @template TGenerator
+ *
+ * @phpstan-param \Generator $generator
+ * @phpstan-return static>
* @psalm-mutation-free
*/
public static function createFromGeneratorImmutable(\Generator $generator): self
{
- return self::create(\iterator_to_array($generator, true));
+ return self::create(\iterator_to_array($generator, true)); // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
}
/**
@@ -1895,13 +1897,16 @@ public static function createFromJson(string $json): self
* @return static
* (Immutable) Returns an new instance of the Arrayy object.
*
- * @phpstan-param array $array
- * @phpstan-return static
+ * @template TArrayKey of array-key
+ * @template TArray
+ *
+ * @phpstan-param array $array
+ * @phpstan-return static>
* @psalm-mutation-free
*/
public static function createFromArray(array $array): self
{
- return static::create($array);
+ return static::create($array); // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
}
/**
@@ -1998,7 +2003,7 @@ static function (&$val) {
);
/** @var static $return - help for phpstan */
- $return = static::create($array);
+ $return = static::create($array); // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
return $return;
}
@@ -2014,13 +2019,16 @@ static function (&$val) {
* @return static
* (Immutable) Returns an new instance of the Arrayy object.
*
- * @phpstan-param \Traversable $traversable
- * @phpstan-return static
+ * @template TTraversableKey of array-key
+ * @template TTraversable
+ *
+ * @phpstan-param \Traversable $traversable
+ * @phpstan-return static>
* @psalm-mutation-free
*/
public static function createFromTraversableImmutable(\Traversable $traversable, bool $use_keys = true): self
{
- return self::create(\iterator_to_array($traversable, $use_keys));
+ return self::create(\iterator_to_array($traversable, $use_keys)); // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
}
/**
@@ -2039,7 +2047,7 @@ public static function createFromTraversableImmutable(\Traversable $traversable,
public static function createWithRange($low, $high, $step = 1): self
{
/** @phpstan-var static $return - help for phpstan */
- $return = static::create(\range($low, $high, $step));
+ $return = static::create(\range($low, $high, $step)); // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
return $return;
}
@@ -2365,7 +2373,7 @@ public function diffRecursive(array $array = [], $helperVariableForRecursion = n
}
return static::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -2390,7 +2398,7 @@ public function diffRecursive(array $array = [], $helperVariableForRecursion = n
public function diffReverse(array $array = []): self
{
return static::create(
- \array_diff($array, $this->toArray()),
+ \array_diff($array, $this->toArray()), // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -2412,7 +2420,7 @@ public function diffReverse(array $array = []): self
public function divide(): self
{
return static::create(
- [
+ [ // @phpstan-ignore-line argument.type (the runtime conversion crosses from the receiver templates into a freshly constructed result shape)
$this->keys(),
$this->values(),
],
@@ -2437,8 +2445,11 @@ public function divide(): self
* @return static
* (Immutable)
*
- * @phpstan-param \Closure(T,?TKey):T $closure
- * @phpstan-return static
+ * @template TEach
+ * The output value type.
+ *
+ * @phpstan-param \Closure(T,?TKey):TEach $closure
+ * @phpstan-return static>
* @psalm-mutation-free
*/
public function each(\Closure $closure): self
@@ -2450,8 +2461,8 @@ public function each(\Closure $closure): self
$array[$key] = $closure($value, $key);
}
- return static::create(
- $array,
+ return static::create( // @phpstan-ignore return.type (create() is intentionally re-parameterized with TEach)
+ $array, // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -2552,7 +2563,7 @@ public function fillWithDefaults(int $num, $default = null): self
}
return static::create(
- $tmpArray,
+ $tmpArray, // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -2594,7 +2605,10 @@ public function fillWithDefaults(int $num, $default = null): self
* @return static
* (Immutable)
*
- * @phpstan-param null|(\Closure(T,TKey=):bool)|(\Closure(T):bool)|(\Closure(TKey):bool) $closure
+ * @template TFilterFlag of int
+ *
+ * @phpstan-param (TFilterFlag is \ARRAY_FILTER_USE_KEY ? \Closure(TKey):bool : \Closure(T,TKey=):bool)|null $closure
+ * @phpstan-param TFilterFlag $flag
* @phpstan-return static
* @psalm-mutation-free
*/
@@ -2729,7 +2743,7 @@ static function ($item) use (
$comparisonOp
) {
$item = (array) $item;
- $itemArrayy = static::create($item);
+ $itemArrayy = static::create($item); // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$item[$property] = $itemArrayy->get($property, []);
return $ops[$comparisonOp]($item, $property, $value);
@@ -2738,7 +2752,7 @@ static function ($item) use (
);
return static::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -2901,7 +2915,7 @@ public function firstsImmutable(?int $number = null): self
}
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -2930,7 +2944,7 @@ public function firstsKeys(?int $number = null): self
}
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -3057,7 +3071,7 @@ public function get(
if ($key === null) {
return static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (an empty late-static instance cannot satisfy every possible invariant TData shape)
$this->iteratorClass,
false
)->createByReference($usedArray);
@@ -3072,7 +3086,7 @@ public function get(
if (\array_key_exists($key, $usedArray) === true) {
if (\is_array($usedArray[$key])) {
return static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (an empty late-static instance cannot satisfy every possible invariant TData shape)
$this->iteratorClass,
false
)->createByReference($usedArray[$key]);
@@ -3124,7 +3138,7 @@ public function get(
unset($segmentsTmp[0]);
$keyTmp = \implode('.', $segmentsTmp);
$returnTmp = static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (an empty late-static instance cannot satisfy every possible invariant TData shape)
$this->iteratorClass,
false
);
@@ -3170,7 +3184,7 @@ public function get(
if (\is_array($usedArrayTmp)) {
return static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (an empty late-static instance cannot satisfy every possible invariant TData shape)
$this->iteratorClass,
false
)->createByReference($usedArrayTmp);
@@ -3184,7 +3198,7 @@ public function get(
}
return static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (an empty late-static instance cannot satisfy every possible invariant TData shape)
$this->iteratorClass,
false
)->createByReference($usedArray);
@@ -3561,7 +3575,7 @@ public function getValues()
$this->generatorToArray(false);
return static::create(
- \array_values($this->array),
+ \array_values($this->array), // @phpstan-ignore-line argument.type (the runtime conversion crosses from the receiver templates into a freshly constructed result shape)
$this->iteratorClass,
false
);
@@ -3630,7 +3644,7 @@ public function group($grouper, bool $saveKeys = false): self
}
return static::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (this transformation constructs a fresh result shape rather than preserving the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -3745,7 +3759,7 @@ public function indexBy($key): self
}
return static::create(
- $results,
+ $results, // @phpstan-ignore-line argument.type (indexBy() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -3814,7 +3828,7 @@ public function intersection(array $search, bool $keepKeys = false): self
* @psalm-suppress MissingClosureParamType
*/
return static::create(
- \array_uintersect(
+ \array_uintersect( // @phpstan-ignore-line argument.type (intersection() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->toArray(),
$search,
static function ($a, $b) {
@@ -3827,7 +3841,7 @@ static function ($a, $b) {
}
return static::create(
- \array_values(\array_intersect($this->toArray(), $search)),
+ \array_values(\array_intersect($this->toArray(), $search)), // @phpstan-ignore-line argument.type (intersection() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -3848,7 +3862,7 @@ static function ($a, $b) {
public function intersectionMulti(...$array): self
{
return static::create(
- \array_values(\array_intersect($this->toArray(), ...$array)),
+ \array_values(\array_intersect($this->toArray(), ...$array)), // @phpstan-ignore-line argument.type (intersectionMulti() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -3904,7 +3918,7 @@ public function invoke($callable, $arguments = []): self
}
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (invoke() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4049,7 +4063,7 @@ public function isSequential(bool $recursive = false): bool
&&
(\is_array($value) || $value instanceof \Traversable)
&&
- self::create($value)->isSequential() === false
+ self::create($value)->isSequential() === false // @phpstan-ignore-line argument.type (the nested iterable is normalized into a temporary Arrayy with an independent shape)
) {
return false;
}
@@ -4157,7 +4171,7 @@ public function keys(
);
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (keys() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4324,7 +4338,7 @@ public function lastsImmutable(?int $number = null): self
{
if ($this->isEmpty()) {
return static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (lastsImmutable() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4340,7 +4354,7 @@ public function lastsImmutable(?int $number = null): self
}
$arrayy = static::create(
- $poppedValue,
+ $poppedValue, // @phpstan-ignore-line argument.type (lastsImmutable() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4412,7 +4426,7 @@ public function length(int $mode = \COUNT_NORMAL): int
* The output value type.
*
* @phpstan-param callable(T,TKey=,mixed=):T2 $callable
- * @phpstan-return static
+ * @phpstan-return static>
* @psalm-mutation-free
*/
public function map(
@@ -4581,7 +4595,7 @@ public function mergeAppendKeepIndex(array $array = [], bool $recursive = false)
}
return static::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (mergeAppendKeepIndex() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4623,7 +4637,7 @@ public function mergeAppendNewIndex(array $array = [], bool $recursive = false):
}
return static::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (mergeAppendNewIndex() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4664,7 +4678,7 @@ public function mergePrependKeepIndex(array $array = [], bool $recursive = false
}
return static::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (mergePrependKeepIndex() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4706,7 +4720,7 @@ public function mergePrependNewIndex(array $array = [], bool $recursive = false)
}
return static::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (mergePrependNewIndex() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4767,8 +4781,7 @@ public function min()
*/
public function mostUsedValue()
{
- /* @phpstan-ignore return.type */
- return $this->countValues()->arsortImmutable()->firstKey();
+ return $this->countValues()->arsortImmutable()->firstKey(); // @phpstan-ignore return.type (countValues() changes the intermediate value type, while firstKey() restores the original value)
}
/**
@@ -4832,7 +4845,7 @@ public function moveElement($from, $to): self
}
return static::create(
- $output,
+ $output, // @phpstan-ignore-line argument.type (moveElement() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4863,7 +4876,7 @@ public function moveElementToFirstPlace($key): self
}
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (moveElementToFirstPlace() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4894,7 +4907,7 @@ public function moveElementToLastPlace($key): self
}
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (moveElementToLastPlace() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -4997,7 +5010,7 @@ public function only(array $keys): self
public function pad(int $size, $value): self
{
return static::create(
- \array_pad($this->toArray(), $size, $value),
+ \array_pad($this->toArray(), $size, $value), // @phpstan-ignore-line argument.type (pad() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -5032,7 +5045,7 @@ public function partition(\Closure $closure): array
}
}
- return [self::create($matches), self::create($noMatches)];
+ return [self::create($matches), self::create($noMatches)]; // @phpstan-ignore-line argument.type (partition() constructs a result shape that cannot be substituted for the receiver's invariant TData)
}
/**
@@ -5148,7 +5161,7 @@ public function prependToEachKey($suffix): self
$result[$key] = $item->prependToEachKey($suffix);
} elseif (\is_array($item)) {
$result[$key] = self::create(
- $item,
+ $item, // @phpstan-ignore-line argument.type (prependToEachKey() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
)->prependToEachKey($suffix)
@@ -5159,7 +5172,7 @@ public function prependToEachKey($suffix): self
}
return self::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (prependToEachKey() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -5186,7 +5199,7 @@ public function prependToEachValue($suffix): self
$result[$key] = $item->prependToEachValue($suffix);
} elseif (\is_array($item)) {
$result[$key] = self::create(
- $item,
+ $item, // @phpstan-ignore-line argument.type (prependToEachValue() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
)->prependToEachValue($suffix)
@@ -5199,7 +5212,7 @@ public function prependToEachValue($suffix): self
}
return self::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (prependToEachValue() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -5294,7 +5307,7 @@ public function randomImmutable(?int $number = null): self
if ($this->count() === 0) {
return static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (randomImmutable() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -5304,7 +5317,7 @@ public function randomImmutable(?int $number = null): self
$arrayRandValue = [$this->array[\array_rand($this->array)]];
return static::create(
- $arrayRandValue,
+ $arrayRandValue, // @phpstan-ignore-line argument.type (randomImmutable() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -5314,7 +5327,7 @@ public function randomImmutable(?int $number = null): self
\shuffle($arrayTmp);
return static::create(
- $arrayTmp,
+ $arrayTmp, // @phpstan-ignore-line argument.type (randomImmutable() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
)->firstsImmutable($number);
@@ -5385,7 +5398,7 @@ public function randomKeys(int $number): self
$result = (array) \array_rand($this->array, $number);
return static::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (randomKeys() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -5411,7 +5424,7 @@ public function randomMutable(?int $number = null): self
if ($this->count() === 0) {
return static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (randomMutable() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -5562,7 +5575,7 @@ public function reduce_dimension(bool $unique = true): self
foreach ($this->getGenerator() as $val) {
if (\is_array($val)) {
- $result[] = static::create($val)->reduce_dimension($unique)->toArray();
+ $result[] = static::create($val)->reduce_dimension($unique)->toArray(); // @phpstan-ignore-line argument.type (reduce_dimension() constructs a result shape that cannot be substituted for the receiver's invariant TData)
} else {
$result[] = [$val];
}
@@ -5570,7 +5583,7 @@ public function reduce_dimension(bool $unique = true): self
$result = $result === [] ? [] : \array_merge(...$result);
- $resultArrayy = static::create($result);
+ $resultArrayy = static::create($result); // @phpstan-ignore-line argument.type (reduce_dimension() constructs a result shape that cannot be substituted for the receiver's invariant TData)
/**
* @psalm-suppress ImpureMethodCall - object is already re-created
@@ -5631,7 +5644,7 @@ public function reject(\Closure $closure): self
}
return static::create(
- $filtered,
+ $filtered, // @phpstan-ignore-line argument.type (reject() constructs a result shape that cannot be substituted for the receiver's invariant TData)
$this->iteratorClass,
false
);
@@ -5661,7 +5674,7 @@ public function remove($key)
}
return static::create(
- $this->toArray(),
+ $this->toArray(), // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -5670,7 +5683,7 @@ public function remove($key)
$this->internalRemove($key);
return static::create(
- $this->toArray(),
+ $this->toArray(), // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -5713,7 +5726,7 @@ public function removeFirst(): self
\array_shift($tmpArray);
return static::create(
- $tmpArray,
+ $tmpArray, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -5739,7 +5752,7 @@ public function removeLast(): self
\array_pop($tmpArray);
return static::create(
- $tmpArray,
+ $tmpArray, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -5779,7 +5792,7 @@ public function removeValue($value): self
}
return static::create(
- $this->array,
+ $this->array, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -5799,11 +5812,11 @@ public function removeValue($value): self
public function repeat($times): self
{
if ($times === 0) {
- return static::create([], $this->iteratorClass);
+ return static::create([], $this->iteratorClass); // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
}
return static::create(
- \array_fill(0, (int) $times, $this->toArray()),
+ \array_fill(0, (int) $times, $this->toArray()), // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -5872,14 +5885,11 @@ public function replace($oldKey, $newKey, $newValue): self
*/
public function replaceAllKeys(array $keys): self
{
- $data = \array_combine($keys, $this->toArray());
- /* @phpstan-ignore identical.alwaysFalse */
- if ($data === false) {
- $data = [];
- }
+ $values = $this->toArray();
+ $data = \count($keys) === \count($values) ? \array_combine($keys, $values) : [];
return static::create(
- $data,
+ $data, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -5916,14 +5926,11 @@ public function replaceAllKeys(array $keys): self
*/
public function replaceAllValues(array $array): self
{
- $data = \array_combine($this->toArray(), $array);
- /* @phpstan-ignore identical.alwaysFalse */
- if ($data === false) {
- $data = [];
- }
+ $keys = $this->toArray();
+ $data = \count($keys) === \count($array) ? \array_combine($keys, $array) : [];
return static::create(
- $data,
+ $data, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -5948,14 +5955,10 @@ public function replaceAllValues(array $array): self
public function replaceKeys(array $keys): self
{
$values = \array_values($this->toArray());
- $result = \array_combine($keys, $values);
- /* @phpstan-ignore identical.alwaysFalse */
- if ($result === false) {
- $result = [];
- }
+ $result = \count($keys) === \count($values) ? \array_combine($keys, $values) : [];
return static::create(
- $result,
+ $result, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -5990,7 +5993,7 @@ public function replaceOneValue($search, $replacement = ''): self
}
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -6019,8 +6022,7 @@ public function replaceValues($search, $replacement = ''): self
return \str_replace($search, $replacement, $value);
};
- /* @phpstan-ignore argument.type */
- return $this->each($callable);
+ return $this->each($callable); // @phpstan-ignore return.type (the replacement callback intentionally changes values while preserving the collection class)
}
/**
@@ -6043,7 +6045,7 @@ public function rest(int $from = 1): self
$tmpArray = $this->toArray();
return static::create(
- \array_splice($tmpArray, $from),
+ \array_splice($tmpArray, $from), // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -6194,7 +6196,7 @@ public function searchValue($index): self
if ($this->array === []) {
return static::create(
- [],
+ [], // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -6211,7 +6213,7 @@ public function searchValue($index): self
}
return static::create(
- $return,
+ $return, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -6343,7 +6345,7 @@ public function shuffle(bool $secure = false, ?array $array = null): self
}
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -6514,7 +6516,7 @@ public function sizeRecursive(): int
public function slice(int $offset, ?int $length = null, bool $preserveKeys = false)
{
return static::create(
- \array_slice(
+ \array_slice( // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->toArray(),
$offset,
$length,
@@ -6730,7 +6732,7 @@ public function sorter($sorter = null, $direction = \SORT_ASC, int $strategy = \
// Transform all values into their results.
if ($sorter) {
$arrayy = static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -6758,7 +6760,7 @@ static function ($value) use ($sorter) {
\array_multisort($results, $direction, $strategy, $array);
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -6788,7 +6790,7 @@ public function splice(int $offset, ?int $length = null, $replacement = []): sel
);
return static::create(
- $tmpArray,
+ $tmpArray, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -6924,7 +6926,7 @@ public function swap($swapA, $swapB): self
list($array[$swapA], $array[$swapB]) = [$array[$swapB], $array[$swapA]];
return static::create(
- $array,
+ $array, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -6970,8 +6972,7 @@ public function toArray(
}
}
- /* @phpstan-ignore return.type */
- return $array;
+ return $array; // @phpstan-ignore return.type (recursive Arrayy conversion produces the documented runtime array shape)
}
return \iterator_to_array($this->getGenerator(), $preserveKeys);
@@ -7061,7 +7062,7 @@ public function toPermutation(?array $items = null, array $helper = []): self
/** @var static $return - help for phpstan */
$return = static::create(
- $return,
+ $return, // @phpstan-ignore-line argument.type (the runtime API intentionally accepts or transforms a value PHPStan cannot reconcile with the invariant template)
$this->iteratorClass,
false
);
@@ -7451,7 +7452,7 @@ protected function array_keys_recursive(
*
* @return void
*
- * @phpstan-param array|null $currentOffset
+ * @phpstan-param array|null $currentOffset
* @psalm-mutation-free
*/
protected function callAtPath($path, $callable, &$currentOffset = null)
@@ -7463,10 +7464,6 @@ protected function callAtPath($path, $callable, &$currentOffset = null)
}
$explodedPath = \explode($this->pathSeparator, $path);
- /* @phpstan-ignore identical.alwaysFalse */
- if ($explodedPath === false) {
- return;
- }
$nextPath = \array_shift($explodedPath);
if (!isset($currentOffset[$nextPath])) {
@@ -7474,10 +7471,15 @@ protected function callAtPath($path, $callable, &$currentOffset = null)
}
if ($explodedPath !== []) {
+ if (!\is_array($currentOffset[$nextPath])) {
+ return;
+ }
+
+ $nestedOffset = &$currentOffset[$nextPath];
$this->callAtPath(
\implode($this->pathSeparator, $explodedPath),
$callable,
- $currentOffset[$nextPath]
+ $nestedOffset
);
} else {
$callable($currentOffset[$nextPath]);
@@ -7487,7 +7489,7 @@ protected function callAtPath($path, $callable, &$currentOffset = null)
/**
* Extracts the value of the given property or method from the object.
*
- * @param static $object
+ * @param array|object $object
* The object to extract the value from.
* @param string $keyOrPropertyOrMethod
* The property or method for which the
@@ -7498,11 +7500,15 @@ protected function callAtPath($path, $callable, &$currentOffset = null)
* @return mixed
*
The value extracted from the specified property or method.
*
- * @phpstan-param self $object
+ * @phpstan-param array|object $object
*/
- final protected function extractValue(self $object, string $keyOrPropertyOrMethod)
+ final protected function extractValue($object, string $keyOrPropertyOrMethod)
{
- if (isset($object[$keyOrPropertyOrMethod])) {
+ if (\is_array($object)) {
+ if (\array_key_exists($keyOrPropertyOrMethod, $object)) {
+ return $object[$keyOrPropertyOrMethod];
+ }
+ } elseif ($object instanceof self && isset($object[$keyOrPropertyOrMethod])) {
$return = $object->get($keyOrPropertyOrMethod);
if ($return instanceof self) {
@@ -7512,11 +7518,11 @@ final protected function extractValue(self $object, string $keyOrPropertyOrMetho
return $return;
}
- if (\property_exists($object, $keyOrPropertyOrMethod)) {
+ if (\is_object($object) && \property_exists($object, $keyOrPropertyOrMethod)) {
return $object->{$keyOrPropertyOrMethod};
}
- if (\method_exists($object, $keyOrPropertyOrMethod)) {
+ if (\is_object($object) && \method_exists($object, $keyOrPropertyOrMethod)) {
return $object->{$keyOrPropertyOrMethod}();
}
@@ -8027,6 +8033,10 @@ protected function internalRemove($key): bool
{
$this->generatorToArray();
+ if (\is_float($key)) {
+ $key = (int) $key;
+ }
+
if (
$this->pathSeparator
&&
@@ -8035,18 +8045,28 @@ protected function internalRemove($key): bool
\strpos($key, $this->pathSeparator) !== false
) {
$path = \explode($this->pathSeparator, (string) $key);
+ $array = &$this->array;
+
// crawl though the keys
while (\count($path, \COUNT_NORMAL) > 1) {
$key = \array_shift($path);
- if (!$this->has($key)) {
+ if (!\is_array($array) || !\array_key_exists($key, $array)) {
return false;
}
- $this->array = &$this->array[$key];
+ $array = &$array[$key];
}
$key = \array_shift($path);
+
+ if (!\is_array($array)) {
+ return false;
+ }
+
+ unset($array[$key]);
+
+ return true;
}
unset($this->array[$key]);
diff --git a/src/Collection/AbstractCollection.php b/src/Collection/AbstractCollection.php
index 58fbcd1..e62e6ae 100644
--- a/src/Collection/AbstractCollection.php
+++ b/src/Collection/AbstractCollection.php
@@ -317,11 +317,11 @@ public static function createFromJsonMapper(string $json)
if (\is_array($jsonObject)) {
foreach ($jsonObject as $jsonObjectSingle) {
$collectionData = $mapper->map($jsonObjectSingle, $type);
- $return->add($collectionData);
+ $return->add($collectionData); // @phpstan-ignore-line argument.type (map() instantiates the runtime class-string from getType(), whose relationship to T cannot be expressed)
}
} else {
$collectionData = $mapper->map($jsonObject, $type);
- $return->add($collectionData);
+ $return->add($collectionData); // @phpstan-ignore-line argument.type (map() instantiates the runtime class-string from getType(), whose relationship to T cannot be expressed)
}
} else {
foreach ($jsonObject as $key => $jsonValue) {
diff --git a/src/Create.php b/src/Create.php
index 9241591..5f5cc91 100644
--- a/src/Create.php
+++ b/src/Create.php
@@ -17,7 +17,7 @@
*/
function create($data): Arrayy
{
- return new Arrayy($data);
+ return new Arrayy($data); // @phpstan-ignore return.type (the convenience factory deliberately accepts mixed input)
}
}
diff --git a/src/Mapper/Json.php b/src/Mapper/Json.php
index a5724e0..59de56b 100644
--- a/src/Mapper/Json.php
+++ b/src/Mapper/Json.php
@@ -19,7 +19,7 @@ final class Json
* Override class names that JsonMapper uses to create objects.
* Useful when your setter methods accept abstract classes or interfaces.
*
- * @var array
+ * @var array
*/
public $classMap = [];
@@ -33,7 +33,7 @@ final class Json
* 2. Name of the unknown JSON property
* 3. JSON value of the property
*
- * @var callable
+ * @var callable|null
*/
public $undefinedPropertyHandler;
@@ -41,14 +41,14 @@ final class Json
* Runtime cache for inspected classes. This is particularly effective if
* mapArray() is called with a large number of objects
*
- * @var array property inspection result cache
+ * @var array> property inspection result cache
*/
private $arInspectedClasses = [];
/**
* Map data all data in $json into the given $object instance.
*
- * @param object|iterable $json
+ * @param object|iterable $json
* JSON object structure from json_decode()
* @param object|string $object
* Object to map $json data into
@@ -58,7 +58,7 @@ final class Json
*
* @see mapArray()
*
- * @template TObject
+ * @template TObject of object
* @phpstan-param TObject|class-string $object
* Object to map $json data into.
* @phpstan-return TObject
@@ -79,7 +79,10 @@ public function map($json, $object)
$strClassName = \get_class($object);
$rc = new \ReflectionClass($object);
$strNs = $rc->getNamespaceName();
- foreach ($json as $key => $jsonValue) {
+ $jsonValues = $json instanceof \Traversable
+ ? $json
+ : (\is_object($json) ? \get_object_vars($json) : $json);
+ foreach ($jsonValues as $key => $jsonValue) {
$key = $this->getSafeName($key);
// Store the property inspection results, so we don't have to do it
@@ -247,7 +250,7 @@ public function map($json, $object)
/**
* Map an array
*
- * @param array $json JSON array structure from json_decode()
+ * @param array $json JSON array structure from json_decode()
* @param mixed $array Array or ArrayObject that gets filled with
* data from $json
* @param string|null $class Class name for children objects.
@@ -302,7 +305,7 @@ public function mapArray($json, $array, $class = null, $parent_key = '')
&&
\count($typesTmp->getTypes()) === 1
) {
- $array[$key] = $this->map($jsonValue, $typesTmp->getTypes()[0]);
+ $array[$key] = $this->map($jsonValue, $typesTmp->getTypes()[0]); // @phpstan-ignore-line argument.templateType, argument.type (runtime PHPDoc type strings are validated by map())
$foundArrayy = true;
break;
@@ -404,7 +407,7 @@ private function getFullNamespace($type, $strNs)
* @param \ReflectionClass $rc Reflection class to check
* @param string $name Property name
*
- * @return array First value: if the property exists
+ * @return array{bool,string|\ReflectionMethod|\ReflectionProperty|null,string|null} First value: if the property exists
* Second value: the accessor to use (
* Array-Key-String or ReflectionMethod or ReflectionProperty, or null)
* Third value: type of the property
@@ -485,7 +488,7 @@ private function inspectProperty(\ReflectionClass $rc, $name): array
*
* @param string $docblock Full method docblock
*
- * @return array
+ * @return array>
*/
private static function parseAnnotations($docblock): array
{
@@ -692,13 +695,13 @@ private function isArrayOfType($strType): bool
/**
* Checks if the given type is nullable
*
- * @param string $type type name from the phpdoc param
+ * @param string|null $type type name from the phpdoc param
*
* @return bool True if it is nullable
*/
private function isNullable($type): bool
{
- return \stripos('|' . $type . '|', '|null|') !== false;
+ return $type !== null && \stripos('|' . $type . '|', '|null|') !== false;
}
/**
@@ -739,7 +742,7 @@ private function removeNullable($type)
*
* @internal
*
- * @template TClass
+ * @template TClass of object
* @phpstan-param TClass|class-string $class
* @phpstan-return TClass
*/
diff --git a/src/PHPStan/DefaultDotNotationTypeInterface.php b/src/PHPStan/DefaultDotNotationTypeInterface.php
new file mode 100644
index 0000000..53adfc5
--- /dev/null
+++ b/src/PHPStan/DefaultDotNotationTypeInterface.php
@@ -0,0 +1,15 @@
+getName() === 'get';
+ }
+
+ public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type
+ {
+ if (!isset($methodCall->args[0]) || !$methodCall->args[0] instanceof Arg) {
+ return null;
+ }
+
+ $pathType = $scope->getType($methodCall->args[0]->value);
+ $paths = $pathType->getConstantStrings();
+ if (
+ \count($paths) !== 1
+ || !\str_contains($paths[0]->getValue(), '.')
+ || \str_contains($paths[0]->getValue(), '*')
+ ) {
+ return null;
+ }
+
+ $dataType = $this->getArrayyDataType($scope->getType($methodCall->var));
+ if ($dataType === null || !$dataType->isConstantArray()->yes()) {
+ return null;
+ }
+
+ $valueType = $dataType;
+ foreach (\explode('.', $paths[0]->getValue()) as $segment) {
+ $valueType = TypeCombinator::removeNull($valueType);
+ $nestedDataType = $this->getArrayyDataType($valueType);
+ if ($nestedDataType !== null) {
+ $valueType = $nestedDataType;
+ }
+
+ $offsetType = new ConstantStringType($segment);
+ if (!$valueType->isOffsetAccessible()->yes() || $valueType->hasOffsetValueType($offsetType)->no()) {
+ return $this->getFallbackType($methodCall, $scope);
+ }
+
+ $valueType = $valueType->getOffsetValueType($offsetType);
+ }
+
+ if (!$valueType->isArray()->yes() && $valueType->getArrays() !== []) {
+ // Arrayy wraps array results in an Arrayy instance. If the shape is a
+ // union of arrays and scalars, the native method type is safer than
+ // pretending the runtime wrapper is a scalar.
+ return null;
+ }
+
+ if ($valueType->isArray()->yes()) {
+ $valueType = new GenericObjectType(
+ Arrayy::class,
+ [$valueType->getIterableKeyType(), $valueType->getIterableValueType(), $valueType]
+ );
+ }
+
+ return TypeCombinator::union($valueType, $this->getFallbackType($methodCall, $scope));
+ }
+
+ private function getArrayyDataType(Type $type): ?Type
+ {
+ foreach ($type->getObjectClassReflections() as $classReflection) {
+ if ($classReflection->getName() === Arrayy::class) {
+ // Plain Arrayy instances freely change shape at runtime. Typed
+ // subclasses provide the stable TData contract needed here.
+ continue;
+ }
+
+ if ($classReflection->getAncestorWithClassName(DefaultDotNotationTypeInterface::class) === null) {
+ // The separator is mutable runtime state. Only an explicit
+ // default-separator contract makes splitting on "." sound.
+ continue;
+ }
+
+ $arrayyReflection = $this->getArrayyReflection($classReflection);
+ if ($arrayyReflection === null) {
+ continue;
+ }
+
+ $dataType = $arrayyReflection->getActiveTemplateTypeMap()->getType('TData');
+ if ($dataType !== null) {
+ return $dataType;
+ }
+ }
+
+ return null;
+ }
+
+ private function getArrayyReflection(ClassReflection $classReflection): ?ClassReflection
+ {
+ if ($classReflection->getName() === Arrayy::class) {
+ return $classReflection;
+ }
+
+ return $classReflection->getAncestorWithClassName(Arrayy::class);
+ }
+
+ private function getFallbackType(MethodCall $methodCall, Scope $scope): Type
+ {
+ if (!isset($methodCall->args[1]) || !$methodCall->args[1] instanceof Arg) {
+ return new NullType();
+ }
+
+ $fallbackType = $scope->getType($methodCall->args[1]->value);
+
+ return $fallbackType instanceof NeverType ? new NullType() : $fallbackType;
+ }
+}
diff --git a/src/PHPStan/MetaDynamicStaticMethodReturnTypeExtension.php b/src/PHPStan/MetaDynamicStaticMethodReturnTypeExtension.php
index feb0c21..8ccfe92 100644
--- a/src/PHPStan/MetaDynamicStaticMethodReturnTypeExtension.php
+++ b/src/PHPStan/MetaDynamicStaticMethodReturnTypeExtension.php
@@ -38,7 +38,7 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
}
$className = $scope->resolveName($methodCall->class);
- if (!\is_a($className, Arrayy::class, true)) {
+ if (!\is_a($className, Arrayy::class, true)) { // @phpstan-ignore-line phpstanApi.runtimeReflection (the extension requires runtime reflection because PHPStan does not expose this check as a stable API)
return null;
}
diff --git a/src/Type/DetectFirstValueTypeCollection.php b/src/Type/DetectFirstValueTypeCollection.php
index b0693a1..89605f9 100644
--- a/src/Type/DetectFirstValueTypeCollection.php
+++ b/src/Type/DetectFirstValueTypeCollection.php
@@ -26,7 +26,7 @@ final class DetectFirstValueTypeCollection extends Collection implements TypeInt
* @param string $iteratorClass
* @param bool $checkPropertiesInConstructor
*
- * @phpstan-param array|Arrayy> $data
+ * @phpstan-param mixed $data
* @phpstan-param class-string<\Arrayy\ArrayyIterator> $iteratorClass
*/
public function __construct(
diff --git a/src/TypeCheck/TypeCheckCallback.php b/src/TypeCheck/TypeCheckCallback.php
index 8600770..6cc8b37 100644
--- a/src/TypeCheck/TypeCheckCallback.php
+++ b/src/TypeCheck/TypeCheckCallback.php
@@ -51,7 +51,7 @@ public function checkType(&$value): bool
}
/**
- * @return array
+ * @return array
*/
public function getTypes(): array
{
diff --git a/src/TypeCheck/TypeCheckPhpDoc.php b/src/TypeCheck/TypeCheckPhpDoc.php
index 43fbbe7..d58bc6e 100644
--- a/src/TypeCheck/TypeCheckPhpDoc.php
+++ b/src/TypeCheck/TypeCheckPhpDoc.php
@@ -14,11 +14,6 @@
*/
final class TypeCheckPhpDoc extends AbstractTypeCheck implements TypeCheckInterface
{
- /**
- * @var bool
- */
- private $hasTypeDeclaration = false;
-
/**
* @var string
*/
@@ -68,8 +63,6 @@ public static function fromDocTypeObject(string $property, $type)
$tmpReflection = new self($property);
if ($type) {
- $tmpReflection->hasTypeDeclaration = true;
-
$docTypes = self::parseDocTypeObject($type);
if (\is_array($docTypes) === true) {
foreach ($docTypes as $docType) {
@@ -94,8 +87,6 @@ public static function fromReflectionProperty(\ReflectionProperty $reflectionPro
$docTypes = self::getTypesFromReflectionPropertyDocBlock($reflectionProperty);
if ($docTypes !== null) {
- $tmpReflection->hasTypeDeclaration = true;
-
if (\is_array($docTypes) === true) {
foreach ($docTypes as $docType) {
$tmpReflection->types[] = $docType;
@@ -109,8 +100,6 @@ public static function fromReflectionProperty(\ReflectionProperty $reflectionPro
return $tmpReflection;
} else {
- $tmpReflection->hasTypeDeclaration = true;
-
$docTypes = self::parseReflectionTypeObject($type);
if (\is_array($docTypes) === true) {
foreach ($docTypes as $docType) {
@@ -243,15 +232,12 @@ private static function getScalarPseudoTypeClasses(): array
{
$classes = [];
- foreach (
- [
- '\phpDocumentor\Reflection\PseudoTypes\Scalar',
- '\phpDocumentor\Reflection\Types\Scalar',
- ] as $className
- ) {
- if (\class_exists($className)) {
- $classes[] = $className;
- }
+ if (\class_exists(\phpDocumentor\Reflection\PseudoTypes\Scalar::class)) {
+ $classes[] = \phpDocumentor\Reflection\PseudoTypes\Scalar::class;
+ }
+
+ if (\class_exists(\phpDocumentor\Reflection\Types\Scalar::class)) {
+ $classes[] = \phpDocumentor\Reflection\Types\Scalar::class;
}
return $classes;
diff --git a/tests/Account.php b/tests/Account.php
index 88ae8c8..33d2917 100644
--- a/tests/Account.php
+++ b/tests/Account.php
@@ -7,7 +7,7 @@
*/
class Account
{
- public function __construct($accountName)
+ public function __construct(string $accountName)
{
$this->accountName = $accountName;
}
diff --git a/tests/ArrayyTest.php b/tests/ArrayyTest.php
index 6eea6f7..c371682 100644
--- a/tests/ArrayyTest.php
+++ b/tests/ArrayyTest.php
@@ -19,7 +19,7 @@ final class ArrayyTest extends \PHPUnit\Framework\TestCase
const TYPE_NUMERIC = 'numeric';
/**
- * @return array
+ * @return array
*/
public function appendProvider(): array
{
@@ -45,7 +45,7 @@ public function appendProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function appendToEachKeyProvider(): array
{
@@ -114,7 +114,7 @@ public function appendToEachKeyProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function appendToEachValueProvider(): array
{
@@ -194,7 +194,7 @@ public static function assertArrayy($actual): void
}
/**
- * @return array
+ * @return array
*/
public function averageProvider(): array
{
@@ -213,37 +213,23 @@ public function averageProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function cleanProvider(): array
{
- // breaking-change from PHP8
- // -> Implement the negative_array_index RFC: https://github.com/php/php-src/commit/6732028273b109cb342387ab5580c367f629d0ac
- if (\PHP_VERSION_ID >= 80000) {
- return [
- [[], []],
- [[null, false], []],
- [[0 => true], [0 => true]],
- [[0 => -9, 0], [0 => -9]],
- [[-8 => -9, 1, 2 => false], [-8 => -9, -7 => 1]],
- [[0 => 1.18, 1 => false], [0 => 1.18]],
- [['foo' => false, 'foo', 'lall'], ['foo', 'lall']],
- ];
- }
-
return [
[[], []],
[[null, false], []],
[[0 => true], [0 => true]],
[[0 => -9, 0], [0 => -9]],
- [[-8 => -9, 1, 2 => false], [-8 => -9, 0 => 1]],
+ [[-8 => -9, 1, 2 => false], [-8 => -9, -7 => 1]],
[[0 => 1.18, 1 => false], [0 => 1.18]],
[['foo' => false, 'foo', 'lall'], ['foo', 'lall']],
];
}
/**
- * @return array
+ * @return array
*/
public function containsCaseInsensitiveProvider(): array
{
@@ -265,7 +251,7 @@ public function containsCaseInsensitiveProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function containsCaseInsensitiveProviderRecursive(): array
{
@@ -287,7 +273,7 @@ public function containsCaseInsensitiveProviderRecursive(): array
}
/**
- * @return array
+ * @return array
*/
public function containsOnlyProvider(): array
{
@@ -306,7 +292,7 @@ public function containsOnlyProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function containsProvider(): array
{
@@ -324,7 +310,7 @@ public function containsProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function containsProviderRecursive(): array
{
@@ -342,7 +328,7 @@ public function containsProviderRecursive(): array
}
/**
- * @return array
+ * @return array
*/
public function countProvider(): array
{
@@ -361,7 +347,7 @@ public function countProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function countProviderRecursive(): array
{
@@ -380,7 +366,7 @@ public function countProviderRecursive(): array
}
/**
- * @return array
+ * @return array
*/
public function diffProvider(): array
{
@@ -454,7 +440,7 @@ public function diffProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function diffKeyProvider(): array
{
@@ -527,7 +513,7 @@ public function diffKeyProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function diffKeyAndValueProvider(): array
{
@@ -604,7 +590,7 @@ public function diffKeyAndValueProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function diffReverseProvider(): array
{
@@ -679,7 +665,7 @@ public function diffReverseProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function fillWithDefaultsProvider(): array
{
@@ -695,7 +681,7 @@ public function fillWithDefaultsProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function findProvider(): array
{
@@ -711,7 +697,7 @@ public function findProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function firstProvider(): array
{
@@ -731,7 +717,7 @@ public function firstProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function firstsProvider(): array
{
@@ -751,7 +737,7 @@ public function firstsProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function getProvider(): array
{
@@ -769,7 +755,7 @@ public function getProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function hasProvider(): array
{
@@ -789,7 +775,7 @@ public function hasProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function implodeKeysProvider(): array
{
@@ -813,7 +799,7 @@ public function implodeKeysProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function implodeProvider(): array
{
@@ -837,7 +823,7 @@ public function implodeProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function initialProvider(): array
{
@@ -858,7 +844,7 @@ public function initialProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function isAssocProvider(): array
{
@@ -877,7 +863,7 @@ public function isAssocProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function isMultiArrayProvider(): array
{
@@ -899,7 +885,7 @@ public function isMultiArrayProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function lastProvider(): array
{
@@ -921,7 +907,7 @@ public function lastProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function matchesAnyProvider(): array
{
@@ -943,7 +929,7 @@ public function matchesAnyProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function matchesProvider(): array
{
@@ -966,7 +952,7 @@ public function matchesProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function maxProvider(): array
{
@@ -985,7 +971,7 @@ public function maxProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function mergeAppendKeepIndexProvider(): array
{
@@ -1077,7 +1063,7 @@ public function mergeAppendKeepIndexProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function mergeAppendNewIndexProvider(): array
{
@@ -1175,7 +1161,7 @@ public function mergeAppendNewIndexProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function mergePrependKeepIndexProvider(): array
{
@@ -1267,7 +1253,7 @@ public function mergePrependKeepIndexProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function mergePrependNewIndexProvider(): array
{
@@ -1365,7 +1351,7 @@ public function mergePrependNewIndexProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function minProvider(): array
{
@@ -1384,7 +1370,7 @@ public function minProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function prependProvider(): array
{
@@ -1409,7 +1395,7 @@ public function prependProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function prependToEachKeyProvider(): array
{
@@ -1478,7 +1464,7 @@ public function prependToEachKeyProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function prependToEachValueProvider(): array
{
@@ -1547,7 +1533,7 @@ public function prependToEachValueProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function randomProvider(): array
{
@@ -1565,7 +1551,7 @@ public function randomProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function randomWeightedProvider(): array
{
@@ -1582,7 +1568,7 @@ public function randomWeightedProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function removeFirstProvider(): array
{
@@ -1598,7 +1584,7 @@ public function removeFirstProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function removeLastProvider(): array
{
@@ -1614,7 +1600,7 @@ public function removeLastProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function removeProvider(): array
{
@@ -1633,7 +1619,7 @@ public function removeProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function removeV2Provider(): array
{
@@ -1649,7 +1635,7 @@ public function removeV2Provider(): array
}
/**
- * @return array
+ * @return array
*/
public function removeValueProvider(): array
{
@@ -1666,7 +1652,7 @@ public function removeValueProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function restProvider(): array
{
@@ -1686,7 +1672,7 @@ public function restProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function reverseProvider(): array
{
@@ -1710,7 +1696,7 @@ public function reverseProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function searchIndexProvider(): array
{
@@ -1726,7 +1712,7 @@ public function searchIndexProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function searchValueProvider(): array
{
@@ -1744,7 +1730,7 @@ public function searchValueProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function setAndGetProvider(): array
{
@@ -1763,7 +1749,7 @@ public function setAndGetProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function setProvider(): array
{
@@ -1782,7 +1768,7 @@ public function setProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function simpleArrayProvider(): array
{
@@ -1823,7 +1809,7 @@ public function simpleArrayProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function reduceDimensionProvider(): array
{
@@ -1846,7 +1832,7 @@ public function reduceDimensionProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function sortKeysProvider(): array
{
@@ -1864,7 +1850,7 @@ public function sortKeysProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function stringWithSeparatorProvider(): array
{
@@ -1922,8 +1908,8 @@ public function testPrependImmutableYield(): void
/**
* @dataProvider appendProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
* @param mixed $value
*/
public function testAppend($array, $result, $value): void
@@ -1936,8 +1922,8 @@ public function testAppend($array, $result, $value): void
/**
* @dataProvider appendToEachKeyProvider
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testAppendToEachKey($array, $result): void
{
@@ -1949,8 +1935,8 @@ public function testAppendToEachKey($array, $result): void
/**
* @dataProvider appendToEachValueProvider
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testAppendToEachValue($array, $result): void
{
@@ -1962,7 +1948,7 @@ public function testAppendToEachValue($array, $result): void
/**
* @dataProvider averageProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $value
* @param float|int $expected
*/
@@ -2156,7 +2142,7 @@ public function testChangeKeyCase(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testChunk(array $array): void
{
@@ -2177,8 +2163,8 @@ public function testChunk(array $array): void
/**
* @dataProvider cleanProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testClean($array, $result): void
{
@@ -2190,7 +2176,7 @@ public function testClean($array, $result): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testClear(array $array): void
{
@@ -2298,7 +2284,7 @@ public function testConstructWithArray(): void
/**
* @dataProvider containsOnlyProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $value
* @param bool $expected
*/
@@ -2312,7 +2298,7 @@ public function testContainsOnly($array, $value, $expected): void
/**
* @dataProvider containsProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $value
* @param bool $expected
*/
@@ -2328,7 +2314,7 @@ public function testContains($array, $value, $expected): void
/**
* @dataProvider containsCaseInsensitiveProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $value
* @param bool $expected
*/
@@ -2343,7 +2329,7 @@ public function testContainsCaseInsensitive($array, $value, $expected): void
/**
* @dataProvider containsCaseInsensitiveProviderRecursive()
*
- * @param array $array
+ * @param array $array
* @param mixed $value
* @param bool $expected
*/
@@ -2416,7 +2402,7 @@ public function testContainsKeysRecursive(): void
/**
* @dataProvider containsProviderRecursive()
*
- * @param array $array
+ * @param array $array
* @param mixed $value
* @param bool $expected
*/
@@ -2440,7 +2426,7 @@ public function testContainsValues(): void
/**
* @dataProvider countProvider()
*
- * @param array $array
+ * @param array $array
* @param int $expected
*/
public function testCount($array, $expected): void
@@ -2457,7 +2443,7 @@ public function testCount($array, $expected): void
/**
* @dataProvider countProviderRecursive()
*
- * @param array $array
+ * @param array $array
* @param int $expected
*/
public function testCountRecursive($array, $expected): void
@@ -2576,7 +2562,6 @@ public function testCreateFromString($string, $separator): void
} else {
$array = [$string];
}
- \assert(\is_array($array));
$arrayy = new A($array);
@@ -2650,7 +2635,7 @@ public function testCreateWithRange(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testCustomSort(array $array): void
{
@@ -2673,7 +2658,7 @@ public function testCustomSort(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testCustomSortImmutable(array $array): void
{
@@ -2696,7 +2681,7 @@ public function testCustomSortImmutable(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testCustomSortKeys(array $array): void
{
@@ -2808,8 +2793,8 @@ public function testCustomSortValuesByDateTimeObject(): void
/**
* sort by date - helper-function
*
- * @param array $a
- * @param array $b
+ * @param array $a
+ * @param array $b
*
* @return int
*/
@@ -2829,10 +2814,10 @@ public function testCustomSortValuesByDateTimeObject(): void
/**
* reduce by date - helper-function
*
- * @param array $resultArray
- * @param array $value
+ * @param array $resultArray
+ * @param array $value
*
- * @return array
+ * @return array
*/
$closureReduce = static function ($resultArray, $value) use ($currentDate) {
/* @var $valueDate \DateTime */
@@ -2857,8 +2842,12 @@ public function testCustomSortValuesByDateTimeObject(): void
/* @var $resultMatch Arrayy|Arrayy[] */
$resultMatch = $birthDatesAraayy->reduce($closureReduce);
- $thisYear = $resultMatch['thisYear']->customSortValues($closureSort);
- $nextYear = $resultMatch['nextYear']->customSortValues($closureSort);
+ $thisYear = $resultMatch['thisYear'];
+ $nextYear = $resultMatch['nextYear'];
+ static::assertInstanceOf(A::class, $thisYear);
+ static::assertInstanceOf(A::class, $nextYear);
+ $thisYear = $thisYear->customSortValues($closureSort);
+ $nextYear = $nextYear->customSortValues($closureSort);
$resultMatch = $nextYear->reverse()->mergePrependNewIndex($thisYear->reverse()->getArray());
@@ -2894,9 +2883,9 @@ public function testCustomSortValuesByDateTimeObject(): void
/**
* @dataProvider diffProvider()
*
- * @param array $array
- * @param array $arrayNew
- * @param array $result
+ * @param array $array
+ * @param array $arrayNew
+ * @param array $result
*/
public function testDiff($array, $arrayNew, $result): void
{
@@ -2908,9 +2897,9 @@ public function testDiff($array, $arrayNew, $result): void
/**
* @dataProvider diffKeyProvider()
*
- * @param array $array
- * @param array $arrayNew
- * @param array $result
+ * @param array $array
+ * @param array $arrayNew
+ * @param array $result
*/
public function testDiffKey($array, $arrayNew, $result): void
{
@@ -2922,9 +2911,9 @@ public function testDiffKey($array, $arrayNew, $result): void
/**
* @dataProvider diffKeyAndValueProvider()
*
- * @param array $array
- * @param array $arrayNew
- * @param array $result
+ * @param array $array
+ * @param array $arrayNew
+ * @param array $result
*/
public function testDiffKeyAndValue($array, $arrayNew, $result): void
{
@@ -3000,7 +2989,7 @@ public function testDiffRecursive(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testDiffWith(array $array): void
{
@@ -3063,10 +3052,10 @@ public function testExchangeArray(): void
/**
* @dataProvider fillWithDefaultsProvider()
*
- * @param array $array
+ * @param array $array
* @param int $num
* @param mixed $default
- * @param array $expected
+ * @param array $expected
*/
public function testFillWithDefaults($array, $num, $default, $expected): void
{
@@ -3139,7 +3128,7 @@ static function ($value) {
$under = A::create([0 => 1, 1 => 2, 2 => 3, 3 => 4, 7 => 7])->filter(
/* @phpstan-ignore argument.type */
static function ($key, $value): bool {
- return ($value % 2 !== 0) && ($key & 2 !== 0);
+ return ($value % 2 !== 0) && (($key & 1) !== 0);
},
\ARRAY_FILTER_USE_BOTH
);
@@ -3171,26 +3160,22 @@ public function testFilterBy(): void
$b = $arrayy->filterBy('name', 'baz');
static::assertCount(1, $b);
- /** @noinspection OffsetOperationsInspection */
- static::assertSame(2365, $b[0]['value']);
+ static::assertSame(2365, $b->get('0.value'));
$b = $arrayy->filterBy('name', ['baz']);
static::assertCount(1, $b);
- /** @noinspection OffsetOperationsInspection */
- static::assertSame(2365, $b[0]['value']);
+ static::assertSame(2365, $b->get('0.value'));
$c = $arrayy->filterBy('value', 2468);
static::assertCount(1, $c);
- /** @noinspection OffsetOperationsInspection */
- static::assertSame('primary', $c[0]['group']);
+ static::assertSame('primary', $c->get('0.group'));
$d = $arrayy->filterBy('group', 'primary');
static::assertCount(3, $d);
$e = $arrayy->filterBy('value', 2000, 'lt');
static::assertCount(1, $e);
- /** @noinspection OffsetOperationsInspection */
- static::assertSame(1468, $e[0]['value']);
+ static::assertSame(1468, $e->get('0.value'));
$e = $arrayy->filterBy('value', [2468, 2365], 'contains');
static::assertCount(2, $e);
@@ -3207,7 +3192,7 @@ public function testFilterBy(): void
/**
* @dataProvider findProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $search
* @param false|mixed $result
*/
@@ -3224,7 +3209,7 @@ public function testFind($array, $search, $result): void
}
/**
- * @return array
+ * @return array
*/
public function findKeyProvider(): array
{
@@ -3246,7 +3231,7 @@ public function findKeyProvider(): array
/**
* @dataProvider findKeyProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $search
* @param false|mixed $result
*/
@@ -3305,8 +3290,8 @@ public function testFindKeyForMinAndMaxValues(): void
/**
* @dataProvider firstProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testFirst($array, $result): void
{
@@ -3318,8 +3303,8 @@ public function testFirst($array, $result): void
/**
* @dataProvider firstsProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
* @param null $take
*/
public function testFirsts($array, $result, $take = null): void
@@ -3368,7 +3353,7 @@ public function testGet(): void
* @dataProvider getProvider()
*
* @param mixed $expected
- * @param array $array
+ * @param array $array
* @param mixed $key
*/
public function testGetV2($expected, $array, $key): void
@@ -3400,7 +3385,7 @@ public function testGetViaDotNotation(): void
* @dataProvider hasProvider()
*
* @param mixed $expected
- * @param array $array
+ * @param array $array
* @param mixed $key
*/
public function testHas($expected, $array, $key): void
@@ -3412,7 +3397,7 @@ public function testHas($expected, $array, $key): void
/**
* @dataProvider implodeProvider()
*
- * @param array $array
+ * @param array $array
* @param string $result
* @param string $with
*/
@@ -3426,7 +3411,7 @@ public function testImplode($array, $result, $with = ','): void
/**
* @dataProvider implodeKeysProvider()
*
- * @param array $array
+ * @param array $array
* @param string $result
* @param string $with
*/
@@ -3464,8 +3449,8 @@ public function testIndexByReturnSome(): void
/**
* @dataProvider initialProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
* @param int $to
*/
public function testInitial($array, $result, $to = 1): void
@@ -3622,7 +3607,7 @@ public function testIsArrayMultidim(): void
/**
* @dataProvider isAssocProvider()
*
- * @param array $array
+ * @param array $array
* @param bool $result
*/
public function testIsAssoc($array, $result): void
@@ -3879,8 +3864,8 @@ public function testKeys(): void
/**
* @dataProvider lastProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
* @param null $take
*/
public function testLast($array, $result, $take = null): void
@@ -3947,7 +3932,7 @@ public function testMagicSetViaDotNotation(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testMap(array $array): void
{
@@ -3983,7 +3968,7 @@ public function testMapSimpleExample(): void
/**
* @dataProvider matchesProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $search
* @param bool $result
*/
@@ -4007,8 +3992,8 @@ public function testMatches($array, $search, $result): void
/**
* @dataProvider matchesAnyProvider()
*
- * @param array $array
- * @param array $search
+ * @param array $array
+ * @param array $search
* @param bool $result
*/
public function testMatchesAny($array, $search, $result): void
@@ -4069,7 +4054,7 @@ public function testMatchesSimple(): void
/**
* @dataProvider maxProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $expected
*/
public function testMax($array, $expected): void
@@ -4127,9 +4112,9 @@ public function testMergeMethods(): void
/**
* @dataProvider mergeAppendKeepIndexProvider()
*
- * @param array $array
- * @param array $arrayNew
- * @param array $result
+ * @param array $array
+ * @param array $arrayNew
+ * @param array $result
*/
public function testMergeAppendKeepIndex($array, $arrayNew, $result): void
{
@@ -4141,9 +4126,9 @@ public function testMergeAppendKeepIndex($array, $arrayNew, $result): void
/**
* @dataProvider mergeAppendNewIndexProvider()
*
- * @param array $array
- * @param array $arrayNew
- * @param array $result
+ * @param array $array
+ * @param array $arrayNew
+ * @param array $result
*/
public function testMergeAppendNewIndex($array, $arrayNew, $result): void
{
@@ -4155,9 +4140,9 @@ public function testMergeAppendNewIndex($array, $arrayNew, $result): void
/**
* @dataProvider mergePrependKeepIndexProvider()
*
- * @param array $array
- * @param array $arrayNew
- * @param array $result
+ * @param array $array
+ * @param array $arrayNew
+ * @param array $result
*/
public function testMergePrependKeepIndex($array, $arrayNew, $result): void
{
@@ -4169,9 +4154,9 @@ public function testMergePrependKeepIndex($array, $arrayNew, $result): void
/**
* @dataProvider mergePrependNewIndexProvider()
*
- * @param array $array
- * @param array $arrayNew
- * @param array $result
+ * @param array $array
+ * @param array $arrayNew
+ * @param array $result
*/
public function testMergePrependNewIndex($array, $arrayNew, $result): void
{
@@ -4183,7 +4168,7 @@ public function testMergePrependNewIndex($array, $arrayNew, $result): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testMergePrependNewIndexV2(array $array): void
{
@@ -4203,7 +4188,7 @@ public function testMergePrependNewIndexV2(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testMergeToRecursively(array $array): void
{
@@ -4223,7 +4208,7 @@ public function testMergeToRecursively(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testMergeWith(array $array): void
{
@@ -4243,7 +4228,7 @@ public function testMergeWith(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testMergeWithRecursively(array $array): void
{
@@ -4263,7 +4248,7 @@ public function testMergeWithRecursively(array $array): void
/**
* @dataProvider minProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $expected
*/
public function testMin($array, $expected): void
@@ -4378,7 +4363,7 @@ public function testNested(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testOffsetNullSet(array $array): void
{
@@ -4395,7 +4380,7 @@ public function testOffsetNullSet(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testOffsetSet(array $array): void
{
@@ -4412,7 +4397,7 @@ public function testOffsetSet(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testOffsetUnset(array $array): void
{
@@ -4431,7 +4416,7 @@ public function testOffsetUnset(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testDeleteKey(array $array): void
{
@@ -4477,10 +4462,20 @@ public function testOffsetUnsetViaDotNotation(): void
static::assertSame([0 => 'a', 'b' => []], $array);
static::assertSame($array, $arrayy->toArray());
- static::assertFalse(isset($array[$offset]));
static::assertFalse($arrayy->offsetExists($offset));
}
+ public function testDotNotationStopsAtScalarIntermediateValue(): void
+ {
+ $arrayy = new A(['user' => 'not-an-array']);
+
+ static::assertFalse($arrayy->offsetExists('user.name'));
+
+ $arrayy->offsetUnset('user.name');
+
+ static::assertSame(['user' => null], $arrayy->toArray());
+ }
+
public function testOrderByKey(): void
{
$array = [
@@ -4579,7 +4574,7 @@ public function testOrderByValueNewIndex(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testPad(array $array): void
{
@@ -4593,7 +4588,7 @@ public function testPad(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testPop(array $array): void
{
@@ -4609,8 +4604,8 @@ public function testPop(array $array): void
/**
* @dataProvider prependProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
* @param mixed $value
*/
public function testPrepend($array, $result, $value): void
@@ -4656,8 +4651,8 @@ public function testPrependKey(): void
/**
* @dataProvider prependToEachKeyProvider
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testPrependToEachKey($array, $result): void
{
@@ -4676,8 +4671,8 @@ public function testPrependToEachKey($array, $result): void
/**
* @dataProvider prependToEachValueProvider
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testPrependToEachValue($array, $result): void
{
@@ -4689,7 +4684,7 @@ public function testPrependToEachValue($array, $result): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testPush(array $array): void
{
@@ -4707,7 +4702,7 @@ public function testPush(array $array): void
/**
* @dataProvider randomProvider()
*
- * @param array $array
+ * @param array $array
* @param int|null $take
*/
public function testRandom($array, $take = null): void
@@ -4760,7 +4755,7 @@ public function testRandomValues(): void
/**
* @dataProvider randomWeightedProvider()
*
- * @param array $array
+ * @param array $array
* @param int|null $take
*/
public function testRandomWeighted($array, $take = null): void
@@ -4794,10 +4789,10 @@ public function testReduceViaFunction(): void
$testArray = ['foo', 2 => 'bar', 4 => 'lall'];
/**
- * @param array $resultArray
+ * @param array $resultArray
* @param mixed $value
*
- * @return array
+ * @return array
*/
$myReducer = static function ($resultArray, $value): array {
if ($value === 'foo') {
@@ -4816,8 +4811,8 @@ public function testReduceViaFunction(): void
/**
* @dataProvider reduceDimensionProvider
*
- * @param array $array
- * @param array $expected
+ * @param array $array
+ * @param array $expected
* @param bool $unique
*/
public function testReduceDimension(array $array, array $expected, bool $unique = false): void
@@ -4831,7 +4826,7 @@ public function testReduceDimension(array $array, array $expected, bool $unique
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testTestgetValues(array $array): void
{
@@ -4845,7 +4840,7 @@ public function testTestgetValues(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testGetValuesYield(array $array): void
{
@@ -4864,7 +4859,7 @@ public function testGetValuesYield(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testGetGetBackwardsGenerator(array $array): void
{
@@ -4883,7 +4878,7 @@ public function testGetGetBackwardsGenerator(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testReindex(array $array): void
{
@@ -4983,9 +4978,9 @@ static function ($value, $key) {
/**
* @dataProvider removeProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $key
- * @param array $result
+ * @param array $result
*/
public function testRemove($array, $key, $result): void
{
@@ -4994,11 +4989,50 @@ public function testRemove($array, $key, $result): void
static::assertSame($result, $resultTmp);
}
+ public function testRemoveWithDotNotationPreservesRootAndSiblingValues(): void
+ {
+ $arrayy = new A([
+ 'user' => [
+ 'profile' => [
+ 'name' => 'Lars',
+ 'avatar' => 'avatar.png',
+ ],
+ 'active' => true,
+ ],
+ 'keep' => 'root value',
+ ]);
+
+ $result = $arrayy->remove('user.profile.name');
+ $expected = [
+ 'user' => [
+ 'profile' => ['avatar' => 'avatar.png'],
+ 'active' => true,
+ ],
+ 'keep' => 'root value',
+ ];
+
+ static::assertSame($expected, $arrayy->toArray());
+ static::assertSame($expected, $result->toArray());
+ }
+
+ public function testRemoveWithDotNotationStopsAtScalarIntermediateValue(): void
+ {
+ $arrayy = new A([
+ 'user' => 'not-an-array',
+ 'keep' => true,
+ ]);
+
+ $result = $arrayy->remove('user.name');
+
+ static::assertSame(['user' => 'not-an-array', 'keep' => true], $arrayy->toArray());
+ static::assertSame($arrayy->toArray(), $result->toArray());
+ }
+
/**
* @dataProvider removeFirstProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testRemoveFirst($array, $result): void
{
@@ -5010,8 +5044,8 @@ public function testRemoveFirst($array, $result): void
/**
* @dataProvider removeLastProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testRemoveLast($array, $result): void
{
@@ -5023,8 +5057,8 @@ public function testRemoveLast($array, $result): void
/**
* @dataProvider removeV2Provider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
* @param mixed $key
*/
public function testRemoveV2($array, $result, $key): void
@@ -5037,8 +5071,8 @@ public function testRemoveV2($array, $result, $key): void
/**
* @dataProvider removeValueProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
* @param mixed $value
*/
public function testRemoveValue($array, $result, $value): void
@@ -5189,7 +5223,7 @@ public function testReplaceAllValuesV2(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testReplaceIn(array $array): void
{
@@ -5209,7 +5243,7 @@ public function testReplaceIn(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testReplaceInRecursively(array $array): void
{
@@ -5239,6 +5273,15 @@ public function testReplaceKeys(): void
static::assertSame('foo', $arrayy['replaced']);
}
+ public function testReplacementMethodsReturnEmptyForMismatchedSizes(): void
+ {
+ $arrayy = A::create(['one', 'two']);
+
+ static::assertSame([], $arrayy->replaceAllKeys(['only-one'])->toArray());
+ static::assertSame([], $arrayy->replaceAllValues([1])->toArray());
+ static::assertSame([], $arrayy->replaceKeys(['only-one'])->toArray());
+ }
+
public function testReplaceOneValue(): void
{
$testArray = ['bar', 'foo' => 'foo', 'foobar' => 'foobar'];
@@ -5275,7 +5318,7 @@ public function testReplaceValues(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testReplaceWith(array $array): void
{
@@ -5295,7 +5338,7 @@ public function testReplaceWith(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testReplaceWithRecursively(array $array): void
{
@@ -5315,8 +5358,8 @@ public function testReplaceWithRecursively(array $array): void
/**
* @dataProvider restProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
* @param int $from
*/
public function testRest($array, $result, $from = 1): void
@@ -5329,8 +5372,8 @@ public function testRest($array, $result, $from = 1): void
/**
* @dataProvider reverseProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testReverse($array, $result): void
{
@@ -5343,7 +5386,7 @@ public function testReverse($array, $result): void
* @dataProvider searchIndexProvider()
*
* @param false|int|string $expected
- * @param array $array
+ * @param array $array
* @param mixed $value
*/
public function testSearchIndex($expected, $array, $value): void
@@ -5356,8 +5399,8 @@ public function testSearchIndex($expected, $array, $value): void
/**
* @dataProvider searchValueProvider()
*
- * @param array $expected
- * @param array $array
+ * @param array $expected
+ * @param array $array
* @param mixed $value
*/
public function testSearchValue($expected, $array, $value): void
@@ -5385,13 +5428,8 @@ public function testSerialize(): void
static::assertSame($object->arrayy, $arrayy);
// serialize + tests
- if (\PHP_VERSION_ID < 70400) {
- static::assertStringContainsString('O:8:"stdClass":1:{s:6:"arrayy";C:13:"Arrayy\Arrayy":', \serialize($object));
- static::assertNotSame($object, \unserialize(\serialize($object)));
- } else {
- static::assertStringContainsString('O:8:"stdClass":1:{s:6:"arrayy";O:13:"Arrayy\\Arrayy":', \serialize($object));
- static::assertNotSame($object, \unserialize(\serialize($object)));
- }
+ static::assertStringContainsString('O:8:"stdClass":1:{s:6:"arrayy";O:13:"Arrayy\\Arrayy":', \serialize($object));
+ static::assertNotSame($object, \unserialize(\serialize($object)));
$arrayy = new A([1 => 1, 2 => 2, 3 => 3]);
$serialized = $arrayy->serialize();
@@ -5412,17 +5450,10 @@ public function testSerialize(): void
);
// serialize + tests
- if (\PHP_VERSION_ID < 70400) {
- static::assertInstanceOf(CityData::class, $model);
- static::assertStringContainsString('C:21:"Arrayy\tests\CityData":', \serialize($model));
- static::assertNotSame($model, \unserialize(\serialize($model)));
- static::assertInstanceOf(CityData::class, $model);
- } else {
- static::assertInstanceOf(CityData::class, $model);
- static::assertStringContainsString('O:21:"Arrayy\tests\CityData":', \serialize($model));
- static::assertNotSame($model, \unserialize(\serialize($model)));
- static::assertInstanceOf(CityData::class, $model);
- }
+ static::assertInstanceOf(CityData::class, $model);
+ static::assertStringContainsString('O:21:"Arrayy\tests\CityData":', \serialize($model));
+ static::assertNotSame($model, \unserialize(\serialize($model)));
+ static::assertInstanceOf(CityData::class, $model);
}
public function testSerializeSimple(): void
@@ -5435,7 +5466,7 @@ public function testSerializeSimple(): void
/**
* @dataProvider setProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $key
* @param mixed $value
*/
@@ -5449,7 +5480,7 @@ public function testSet($array, $key, $value): void
/**
* @dataProvider setAndGetProvider()
*
- * @param array $array
+ * @param array $array
* @param mixed $key
* @param mixed $value
*/
@@ -5519,12 +5550,14 @@ public function testSetViaDotNotation(): void
{
$arrayy = new A(['Lars' => ['lastname' => 'Moelleken']]);
- static::assertSame(['lastname' => 'Moelleken'], $arrayy['Lars']->getArray());
+ $lars = $arrayy['Lars'];
+ static::assertInstanceOf(A::class, $lars);
+ static::assertSame(['lastname' => 'Moelleken'], $lars->getArray());
$result = $arrayy->get('Lars.lastname');
static::assertSame('Moelleken', $result);
- static::assertSame(['lastname' => 'Moelleken'], $arrayy['Lars']->getArray());
+ static::assertSame(['lastname' => 'Moelleken'], $lars->getArray());
/* @phpstan-ignore property.notFound */
static::assertSame(['lastname' => 'Moelleken'], $arrayy->Lars->getArray());
@@ -5532,7 +5565,7 @@ public function testSetViaDotNotation(): void
/* @phpstan-ignore property.notFound */
static::assertSame('Moelleken', $arrayy->Lars->lastname);
- static::assertSame('Moelleken', $arrayy['Lars']['lastname']);
+ static::assertSame('Moelleken', $lars['lastname']);
$tmp = $arrayy['Lars'];
static::assertSame('Moelleken', $tmp['lastname']);
@@ -5584,7 +5617,7 @@ public function testSetViaDotNotation(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testShift(array $array): void
{
@@ -5715,7 +5748,7 @@ public function testSimpleRandomWeighted(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testSlice(array $array): void
{
@@ -5774,7 +5807,7 @@ static function ($value) {
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testSortAscWithPreserveKeys(array $array): void
{
@@ -5807,7 +5840,7 @@ public function testSortAscWithPreserveKeys(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testSortAscWithoutPreserveKeys(array $array): void
{
@@ -5831,7 +5864,7 @@ public function testSortAscWithoutPreserveKeys(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testSortDescWithPreserveKeys(array $array): void
{
@@ -5855,7 +5888,7 @@ public function testSortDescWithPreserveKeys(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testSortImmutableDescWithPreserveKeys(array $array): void
{
@@ -5879,7 +5912,7 @@ public function testSortImmutableDescWithPreserveKeys(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testSortDescWithoutPreserveKeys(array $array): void
{
@@ -5912,8 +5945,8 @@ public function testSortDescWithoutPreserveKeys(array $array): void
/**
* @dataProvider sortKeysProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
* @param string $direction
*/
public function testSortKeys($array, $result, $direction = 'ASC'): void
@@ -5926,7 +5959,7 @@ public function testSortKeys($array, $result, $direction = 'ASC'): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testSortKeysAsc(array $array): void
{
@@ -5959,7 +5992,7 @@ public function testSortKeysAsc(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testNatcasesort(array $array): void
{
@@ -5975,7 +6008,7 @@ public function testNatcasesort(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testNatsortImmutable(array $array): void
{
@@ -5991,7 +6024,7 @@ public function testNatsortImmutable(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testNatsort(array $array): void
{
@@ -6007,7 +6040,7 @@ public function testNatsort(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testNatcasesortImmutable(array $array): void
{
@@ -6023,7 +6056,7 @@ public function testNatcasesortImmutable(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testUasort(array $array): void
{
@@ -6046,7 +6079,7 @@ public function testUasort(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testUasortImmutable(array $array): void
{
@@ -6069,7 +6102,7 @@ public function testUasortImmutable(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testSortKeysDesc(array $array): void
{
@@ -6172,7 +6205,7 @@ public function testSplit(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testStaticCreate(array $array): void
{
@@ -6185,7 +6218,7 @@ public function testStaticCreate(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testStaticCreateFromGeneratorImmutableFromArray(array $array): void
{
@@ -6199,7 +6232,7 @@ public function testStaticCreateFromGeneratorImmutableFromArray(array $array): v
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
* @param int $count
*/
public function testStaticCreateFromGeneratorFunctionFromArray(array $array, int $count): void
@@ -6219,7 +6252,7 @@ static function () use ($arrayy) {
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testStaticCreateFromJson(array $array): void
{
@@ -6234,7 +6267,7 @@ public function testStaticCreateFromJson(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testStaticCreateFromObject(array $array): void
{
@@ -6334,7 +6367,6 @@ public function testStaticCreateFromString($string, $separator): void
} else {
$array = [$string];
}
- \assert(\is_array($array));
$arrayy = A::create($array);
$resultArrayy = A::createFromString($string, $separator);
@@ -6369,9 +6401,9 @@ public function testSwap(): void
/**
* @dataProvider diffReverseProvider()
*
- * @param array $array
- * @param array $arrayNew
- * @param array $result
+ * @param array $array
+ * @param array $arrayNew
+ * @param array $result
*/
public function testTestdiffReverse($array, $arrayNew, $result): void
{
@@ -6383,7 +6415,7 @@ public function testTestdiffReverse($array, $arrayNew, $result): void
/**
* @dataProvider isMultiArrayProvider()
*
- * @param array $array
+ * @param array $array
* @param bool $result
*/
public function testTestisMultiArray($array, $result): void
@@ -6397,7 +6429,7 @@ public function testTestisMultiArray($array, $result): void
* @dataProvider toStringProvider()
*
* @param string $expected
- * @param array $array
+ * @param array $array
*/
public function testToString($expected, $array): void
{
@@ -6407,8 +6439,8 @@ public function testToString($expected, $array): void
/**
* @dataProvider uniqueProvider()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testUnique($array, $result): void
{
@@ -6422,8 +6454,8 @@ public function testUnique($array, $result): void
/**
* @dataProvider uniqueProviderKeepIndex()
*
- * @param array $array
- * @param array $result
+ * @param array $array
+ * @param array $result
*/
public function testUniqueKeepIndex($array, $result): void
{
@@ -6456,7 +6488,7 @@ public function testUnsetSimple(): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testUnshift(array $array): void
{
@@ -6480,10 +6512,30 @@ public function testValues(): void
static::assertSame($matcher, $values->getArray());
}
+ public function testWhereSupportsArraysAndObjects(): void
+ {
+ $object = new \stdClass();
+ $object->status = 'active';
+
+ $result = A::create([
+ ['status' => 'active', 'name' => 'array'],
+ ['status' => 'inactive', 'name' => 'other'],
+ $object,
+ ])->where('status', 'active');
+
+ static::assertSame(
+ [
+ ['status' => 'active', 'name' => 'array'],
+ 2 => $object,
+ ],
+ $result->toArray()
+ );
+ }
+
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testWalk(array $array): void
{
@@ -6502,7 +6554,7 @@ public function testWalk(array $array): void
/**
* @dataProvider simpleArrayProvider
*
- * @param array $array
+ * @param array $array
*/
public function testWalkRecursively(array $array): void
{
@@ -6547,7 +6599,7 @@ public function testWalkSimpleRecursively(): void
}
/**
- * @return array
+ * @return array
*/
public function toStringProvider(): array
{
@@ -6562,7 +6614,7 @@ public function toStringProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function uniqueProvider(): array
{
@@ -6619,7 +6671,7 @@ public function uniqueProvider(): array
}
/**
- * @return array
+ * @return array
*/
public function uniqueProviderKeepIndex(): array
{
@@ -6676,10 +6728,17 @@ public function uniqueProviderKeepIndex(): array
}
/**
- * @param A> $arrayzy
- * @param A>|A>|A> $resultArrayzy
- * @param array $array
- * @param array $resultArray
+ * @template TOriginalKey of array-key
+ * @template TOriginal
+ * @template TOriginalData of array
+ * @template TResultKey of array-key
+ * @template TResult
+ * @template TResultData of array
+ *
+ * @param A $arrayzy
+ * @param A $resultArrayzy
+ * @param array $array
+ * @param array