This commit is contained in:
2025-05-12 14:25:25 +02:00
parent ab2db755ef
commit 9e378ca2b7
2719 changed files with 46505 additions and 60181 deletions

View File

@@ -42,7 +42,7 @@ abstract class AbstractArray implements ArrayInterface
*/
public function __construct(array $data = [])
{
// Invoke offsetSet() for each value added; in this way, sub-classes
// Invoke offsetSet() for each value added; in this way, subclasses
// may provide additional logic about values added to the array object.
foreach ($data as $key => $value) {
$this[$key] = $value;

View File

@@ -112,7 +112,6 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
$temp = [];
foreach ($this->data as $item) {
/** @psalm-suppress MixedAssignment */
$temp[] = $this->extractValue($item, $propertyOrMethod);
}
@@ -165,15 +164,8 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
usort(
$collection->data,
/**
* @param T $a
* @param T $b
*/
function (mixed $a, mixed $b) use ($propertyOrMethod, $order): int {
/** @var mixed $aValue */
$aValue = $this->extractValue($a, $propertyOrMethod);
/** @var mixed $bValue */
$bValue = $this->extractValue($b, $propertyOrMethod);
return ($aValue <=> $bValue) * ($order === Sort::Descending ? -1 : 1);
@@ -207,15 +199,7 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
public function where(?string $propertyOrMethod, mixed $value): CollectionInterface
{
return $this->filter(
/**
* @param T $item
*/
function (mixed $item) use ($propertyOrMethod, $value): bool {
/** @var mixed $accessorValue */
$accessorValue = $this->extractValue($item, $propertyOrMethod);
return $accessorValue === $value;
},
fn (mixed $item): bool => $this->extractValue($item, $propertyOrMethod) === $value,
);
}
@@ -229,7 +213,6 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
*/
public function map(callable $callback): CollectionInterface
{
/** @var Collection<TCallbackReturn> */
return new Collection('mixed', array_map($callback, $this->data));
}
@@ -244,7 +227,6 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
*/
public function reduce(callable $callback, mixed $initial): mixed
{
/** @var TCarry */
return array_reduce($this->data, $callback, $initial);
}
@@ -264,11 +246,8 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
$diffAtoB = array_udiff($this->data, $other->toArray(), $this->getComparator());
$diffBtoA = array_udiff($other->toArray(), $this->data, $this->getComparator());
/** @var array<array-key, T> $diff */
$diff = array_merge($diffAtoB, $diffBtoA);
$collection = clone $this;
$collection->data = $diff;
$collection->data = array_merge($diffAtoB, $diffBtoA);
return $collection;
}
@@ -286,11 +265,8 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
{
$this->compareCollectionTypes($other);
/** @var array<array-key, T> $intersect */
$intersect = array_uintersect($this->data, $other->toArray(), $this->getComparator());
$collection = clone $this;
$collection->data = $intersect;
$collection->data = array_uintersect($this->data, $other->toArray(), $this->getComparator());
return $collection;
}
@@ -359,23 +335,19 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
private function getComparator(): Closure
{
return /**
* @param T $a
* @param T $b
*/
function (mixed $a, mixed $b): int {
// If the two values are object, we convert them to unique scalars.
// If the collection contains mixed values (unlikely) where some are objects
// and some are not, we leave them as they are.
// The comparator should still work and the result of $a < $b should
// be consistent but unpredictable since not documented.
if (is_object($a) && is_object($b)) {
$a = spl_object_id($a);
$b = spl_object_id($b);
}
return function (mixed $a, mixed $b): int {
// If the two values are object, we convert them to unique scalars.
// If the collection contains mixed values (unlikely) where some are objects
// and some are not, we leave them as they are.
// The comparator should still work and the result of $a < $b should
// be consistent but unpredictable since not documented.
if (is_object($a) && is_object($b)) {
$a = spl_object_id($a);
$b = spl_object_id($b);
}
return $a === $b ? 0 : ($a < $b ? 1 : -1);
};
return $a === $b ? 0 : ($a < $b ? 1 : -1);
};
}
/**

View File

@@ -30,7 +30,14 @@ abstract class AbstractSet extends AbstractCollection
return false;
}
return parent::add($element);
// Call offsetSet() on the parent instead of add(), since calling
// parent::add() will invoke $this->offsetSet(), which will call
// $this->contains() a second time. This can cause performance issues
// with extremely large collections. For more information, see
// https://github.com/ramsey/collection/issues/68.
parent::offsetSet(null, $element);
return true;
}
public function offsetSet(mixed $offset, mixed $value): void

View File

@@ -24,7 +24,7 @@ namespace Ramsey\Collection;
*
* Example usage:
*
* ``` php
* ```
* $collection = new \Ramsey\Collection\Collection('My\\Foo');
* $collection->add(new \My\Foo());
* $collection->add(new \My\Foo());
@@ -37,7 +37,7 @@ namespace Ramsey\Collection;
* It is preferable to subclass `AbstractCollection` to create your own typed
* collections. For example:
*
* ``` php
* ```
* namespace My\Foo;
*
* class FooCollection extends \Ramsey\Collection\AbstractCollection
@@ -51,7 +51,7 @@ namespace Ramsey\Collection;
*
* And then use it similarly to the earlier example:
*
* ``` php
* ```
* $fooCollection = new \My\Foo\FooCollection();
* $fooCollection->add(new \My\Foo());
* $fooCollection->add(new \My\Foo());
@@ -64,7 +64,7 @@ namespace Ramsey\Collection;
* The benefit with this approach is that you may do type-checking on the
* collection object:
*
* ``` php
* ```
* if ($collection instanceof \My\Foo\FooCollection) {
* // the collection is a collection of My\Foo objects
* }

View File

@@ -89,7 +89,7 @@ interface CollectionInterface extends ArrayInterface
* @param string $propertyOrMethod The name of the property, method, or
* array key to evaluate and return.
*
* @return array<int, mixed>
* @return list<mixed>
*
* @throws InvalidPropertyOrMethod if the $propertyOrMethod does not exist
* on the elements in this collection.

View File

@@ -55,7 +55,6 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
* @param T $value The value to set at the given offset.
*
* @inheritDoc
* @psalm-suppress MoreSpecificImplementedParamType,DocblockTypeContradiction
*/
public function offsetSet(mixed $offset, mixed $value): void
{
@@ -84,6 +83,7 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
*/
public function keys(): array
{
/** @var list<K> */
return array_keys($this->data);
}
@@ -190,6 +190,7 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
*/
public function __serialize(): array
{
/** @var array<K, T> */
return parent::__serialize();
}
@@ -198,6 +199,7 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
*/
public function toArray(): array
{
/** @var array<K, T> */
return parent::toArray();
}
}

View File

@@ -37,7 +37,6 @@ abstract class AbstractTypedMap extends AbstractMap implements TypedMapInterface
* @param T $value
*
* @inheritDoc
* @psalm-suppress MoreSpecificImplementedParamType
*/
public function offsetSet(mixed $offset, mixed $value): void
{

View File

@@ -29,7 +29,7 @@ namespace Ramsey\Collection\Map;
*
* Example usage:
*
* ```php
* ```
* $map = new TypedMap('string', Foo::class);
* $map['x'] = new Foo();
* foreach ($map as $key => $value) {
@@ -51,7 +51,7 @@ namespace Ramsey\Collection\Map;
* It is preferable to subclass `AbstractTypedMap` to create your own typed map
* implementation:
*
* ```php
* ```
* class FooTypedMap extends AbstractTypedMap
* {
* public function getKeyType()
@@ -68,7 +68,7 @@ namespace Ramsey\Collection\Map;
*
* but you also may use the `TypedMap` class:
*
* ```php
* ```
* class FooTypedMap extends TypedMap
* {
* public function __constructor(array $data = [])

View File

@@ -24,7 +24,7 @@ namespace Ramsey\Collection;
*
* Example usage:
*
* ``` php
* ```
* $foo = new \My\Foo();
* $set = new Set(\My\Foo::class);
*

View File

@@ -16,6 +16,7 @@ namespace Ramsey\Collection\Tool;
use Ramsey\Collection\Exception\InvalidPropertyOrMethod;
use Ramsey\Collection\Exception\UnsupportedOperationException;
use ReflectionProperty;
use function is_array;
use function is_object;
@@ -28,6 +29,11 @@ use function sprintf;
*/
trait ValueExtractorTrait
{
/**
* Returns the type associated with this collection.
*/
abstract public function getType(): string;
/**
* Extracts the value of the given property, method, or array key from the
* element.
@@ -64,6 +70,15 @@ trait ValueExtractorTrait
));
}
if (property_exists($element, $propertyOrMethod) && method_exists($element, $propertyOrMethod)) {
$reflectionProperty = new ReflectionProperty($element, $propertyOrMethod);
if ($reflectionProperty->isPublic()) {
return $element->$propertyOrMethod;
}
return $element->{$propertyOrMethod}();
}
if (property_exists($element, $propertyOrMethod)) {
return $element->$propertyOrMethod;
}
@@ -72,6 +87,10 @@ trait ValueExtractorTrait
return $element->{$propertyOrMethod}();
}
if (isset($element->$propertyOrMethod)) {
return $element->$propertyOrMethod;
}
throw new InvalidPropertyOrMethod(sprintf(
'Method or property "%s" not defined in %s',
$propertyOrMethod,

View File

@@ -77,7 +77,8 @@ trait ValueToStringTrait
// __toString() is implemented
if (is_callable([$value, '__toString'])) {
return (string) $value->__toString();
/** @var string */
return $value->__toString();
}
// object of type \DateTime