GithubHelp home page GithubHelp logo

ramsey / collection Goto Github PK

View Code? Open in Web Editor NEW
1.1K 1.1K 56.0 341 KB

:card_index_dividers: A PHP library for representing and manipulating collections.

License: MIT License

PHP 100.00%
array collection hash map php php8 queue set

collection's People

Contributors

5sw avatar akshatsrivastava2 avatar dependabot[bot] avatar eclipxe13 avatar error17191 avatar finagin avatar freezy-sk avatar grahamcampbell avatar icanhazstring avatar jmauerhan avatar k-phoen avatar lewiscowles1986 avatar peter279k avatar pmlkvch avatar ramsey avatar raphaelstolt avatar raphaelts3 avatar reedy avatar samnela avatar simotod avatar smitmartijn avatar yannoff avatar zf2timo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

collection's Issues

Add Doctrine ArrayCollection::partition() like

Is your feature request related to a problem? Please describe.

Yes, when I need to split a CollectionInterface in two, I have to use CollectionInterface::filter twice with opposite callbacks, which is not very efficient.

Describe the solution you'd like

I found out that Doctrine ArrayCollection is using a partition method which doesn't itinerate twice in the collection. So it could be very useful to have this method in this project! I can submit a PR if needed.

Describe alternatives you've considered

I am not sure if it is better to implement the method in ArrayInterface or CollectionInterface (and related abstract method of course). What do you think?

Additional context

Or maybe there is a way to do the same without modifying the project?

Thanks

Thanks for your work ;)

How to restrict collection data values

How do I impose restrictions on data values added to a collection?

I need a collection that not only limits the type of data added, but also imposes restrictions on the data values. I browsed through the Wiki, ReadMe and tests but could not find any indication that this is possible. Any ideas?

Example code

Call functions with global namespace

In my PR #44 I got feed back to use global namespace function calls . There are two things we can do here.

  1. Opcode optimizations
    For example: \is_array instead of is_array.
    There is a list of functions that have an opcode benefit from those optimizations.
    @see https://github.com/php/php-src/blob/master/Zend/zend_compile.c#L3803

  2. Minor speed advantage without opcode change
    Others might only have a minor advantage in speed like: \array_merge vs array_merge
    There is a benchmark comparing the global namespace call vs the function lookup
    @see http://veewee.github.io/blog/optimizing-php-performance-by-fq-function-calls/

There a many repositories who already have this implemented.
Like PHP-CS-Fixer (PHP-CS-Fixer/PHP-CS-Fixer#3048)

Which might be a good addition to this repository to simply keep everything in check an even set the global namespace call for functions.


Since I didn't want to change the whole implementation with the mentioned PR I simply propose this here and would also file a PR if necessary.

Replace abandoned package

The package fzaninotto/faker is abandoned but Laravel maintainers create fork.

My feature title

Replace abandoned package fzaninotto/faker with fakerphp/faker

Background/problem

The old package will no longer develop, receive bug fixes and work with PHP8

Proposal/solution

Replace composer dependency

Alternatives

Use alternative package

Additional context

"require-dev": {
-       "fzaninotto/faker": "^1.5",
+       "fakerphp/faker": "^1.9.1",
        "jangregor/phpstan-prophecy": "^0.6",

Fix phpstan errors causing failing builds

https://travis-ci.org/ramsey/collection/builds/435792905

$ ./vendor/bin/phpstan analyse src tests --level=4
 33/33 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 ------ ------------------------------------------------------------------------- 
 Line    src/Map/TypedMap.php
 ------ ------------------------------------------------------------------------- 
  119    PHPDoc tag @param for parameter $keyType with type mixed is not subtype
         of native type string
  119    PHPDoc tag @param for parameter $valueType with type mixed is not
         subtype of native type string
  121    Casting to string something that's already string.                       
  122    Casting to string something that's already string.                       
 ------ ------------------------------------------------------------------------- 

 ------ ----------------------------------------------------------------------- 
  Line   src/QueueInterface.php
 ------ ----------------------------------------------------------------------- 
  62     PHPDoc tag @return with type mixed is not subtype of native type bool
 ------ ----------------------------------------------------------------------- 


 [ERROR] Found 5 errors

AbstractSet::add() Performance Optimization

AbstractSet::add() is slow for large collections.

AbstractSet::add() internally invokes AbstractCollection::add() which forwards to AbstractSet::offsetSet(). This flow causes AbstractCollection::contains() to be invoked twice which, for extremely large collections, can significantly impact performance.

Background/problem

Proposal/solution

Alternatives

Additional context

$tests = 10000;

$addCollection       = new Ramsey\Collection\Set( 'int' );
$offsetSetCollection = new Ramsey\Collection\Set( 'int' );

$addTime = microtime( true );
for( $i = 0; $i < $tests; ++$i )
	$addCollection->add( $i );
$addTime = microtime( true ) - $addTime;

$offsetSetTime = microtime( true );
for( $i = 0; $i < $tests; ++$i )
	$offsetSetCollection[] = $i;
$offsetSetTime = microtime( true ) - $offsetSetTime;

// ~98% slower
var_dump( ( $addTime - $offsetSetTime ) / $offsetSetTime * 100 );

New release, please?

Hi! First and foremost, thanks for such an amazing library! I love it <3

There has been a PR (#87) that was merged past October, but a new release has not been done yet.

I don't want to be a PITA, but could we pretty please get a new revision?

Thank you again! :-D

Should PhpStorm complete in foreach?

PhpStorm 2021.2 support for generics in PHP. But when I use foreach above collection PhpStomr does not recognize correct element type.

Should PhpStorm autocomplete inner types in foreach?

It is important when I want to change method names.

Example code

use Ramsey\Collection\AbstractCollection;

/**
 * @extends AbstractCollection<Specific>
 */
class SpecificCollection extends AbstractCollection
{
	public function getType(): string
	{
		return Specific::class;
	}
}


class Specific
{
	public function __construct(private string $code)
	{}


	public function getCode(): string
	{
		return $this->code;
	}
}


class Test {

	public function test1(): void
	{
		$collection = new SpecificCollection([new Specific('code1')]);

		foreach ($collection as $item) {
			$item->getCode(); // IDE do not recognize
		}
	}


	/**
	 * @param SpecificCollection<Specific> $collection
	 */
	public function test2(SpecificCollection $collection): void
	{
		foreach ($collection as $item) {
			$item->getCode(); // IDE do not recognize
		}
	}


	/**
	 * @param SpecificCollection<Specific>|array<Specific> $collection
	 */
	public function test3(SpecificCollection $collection): void
	{
		foreach ($collection as $item) {
			$item->getCode(); // IDE do not recognize
		}
	}


	public function test4(SpecificCollection $collection): void
	{
		foreach ($collection as $item) {
			/** @var Specific $item */
			$item->getCode(); // Only this IDE recognize
		}
	}


	public function test5(SpecificCollection $collection): void
	{
		foreach ($collection as $item) {
			\assert($item instanceof Specific);
			$item->getCode(); // Only this IDE recognize
		}
	}
}

Possible exploit using AbstractArray#unserialize()

Using unserialize without second parameter might can be targeted to use remote code execution.

More details about this here: https://github.com/kalessil/phpinspectionsea/blob/master/docs/security.md#exploiting-unserialize

Since we use the Serializable interface it won't be possible to provide a second parameter.
So I would suggest two things:

  1. For AbstractArray we will prevent unserialize to init any class. By setting allowed_classesto false. (Mentioned as a solution in the docs linked above).

  2. In all child classes of AbstractArray you can override the unserialize method and set allowed_classes to the type of the collection. So unserialize will only initialize the known type of the collection.

For Example:

abstract class AbstractArray implements ArrayInterface
{
...
    public function unserialize($serialized)
    {
        return unserialize($serialized, ['allowed_classes' => false]);
    }
...
}

abstract class AbstractCollection extends AbstractArray
{
...
    public function unserialize($serialized)
    {
        return unserialize($serialized, ['allowed_classes' => [$this->getType()]]);
    }
...
}

AbstractCollection::diff() returns inconsistent results depending on the item order in the original collections

Describe the bug

AbstractCollection::diff() does not return the symmetric difference of 2 collections if their items are not already sorted using the same logical order.
This is due to the comparator function used in array_udiff which, as per official documentation, is supposed to return -1 ,0 or 1 but it only returns -1 or 0.
array_udiff uses the comparator function to sort the elements of each array before comparing them to minimize the number of operations.

To Reproduce...

  1. Create script foo.php and add the following:

    <?php
        require_once 'vendor/autoload.php';
    
        ###########################
    
        class Bar
        {
            public $name;
    
            public function __construct(string $name)
            {
                $this->name = $name;
            }
        }
    
        class BarCollection extends Ramsey\Collection\AbstractCollection
        {
            public function getType(): string
            {
                return Bar::class;
            }
        }
    
        ###########################
    
        $bar1 = new Bar('one');
        $bar2 = new Bar('two');
    
        $collection1 = new BarCollection([$bar1]);
        $collection2 = new BarCollection([$bar1, $bar2]);
        $collection3 = new BarCollection([$bar2, $bar1]);
    
        print_r($collection1->diff($collection2)); // This will print the correct result
        echo '-----'.PHP_EOL;
        print_r($collection1->diff($collection3)); // This will print the wrong result
  2. Execute the script from a terminal:

    $ php foo.php
  3. See output similar to the following:

    BarCollection Object
    (
        [data:protected] => Array
            (
                [0] => Bar Object
                    (
                        [name] => two
                    )
            )
    )
    -----
    BarCollection Object
    (
        [data:protected] => Array
            (
                [0] => Bar Object
                    (
                        [name] => one
                    )
                [1] => Bar Object
                    (
                        [name] => two
                    )
            )
    )

Expected behavior

    BarCollection Object
    (
        [data:protected] => Array
            (
                [0] => Bar Object
                    (
                        [name] => two
                    )
            )
    )
    -----
    BarCollection Object
    (
        [data:protected] => Array
            (
                [0] => Bar Object
                    (
                        [name] => two
                    )
            )
    )

Environment details

  • OS: macOS Catilina (10.15.2)
  • PHP version: 7.3.9
  • ramsey/collection version: 1.0.1

Additional context

I'm happy to help with the resolution of this bug. Let me know if you are open to receive PRs.

Contains Models laravel return false

Describe the bug

Does not return correct value when using contasins with a collection of laravel models

To Reproduce...

Steps to reproduce the behavior (include code examples, if applicable):

  1. Create script foo.php and add the following:

    <?php
    require_once 'vendor/autoload.php';
    
    $collection = new Ramsey\Collection\Collection(\App\User::class);
    
    $model = \App\User::find(1);
    $collection->add($model);
    $model2 =  \App\User::find(1);
    $collection->contains($model2); //Return false
  2. Execute the script from a terminal:

    $ php foo.php
  3. See output similar to the following:

    The ouput the output must be true ...
    

Environment details

  • OS: Linux Ubuntu 18.04.1
  • PHP version: 7.4
  • ramsey/collection version 1.1.0

Additional context

Add any other context about the problem here.

Support DataStructure (ds) extension

Is your feature request related to a problem? Please describe.

No 😺

Describe the solution you'd like

Integration with the Data Structure extension Ext Github Ext Docs

This would be a tremendous win for memory efficiency, in particular around the Map/Set Collection, but even more generally if you use a Vector, I would explain some more, but there is a nice medium post that does an even better job than I could here

It also has some nice builtin queues like:

It even has a polyfill (right now, this might go away in 2.0, fair warning)

DS Polyfill

One other (quick) suggestion, a Doctrine Collections style Collections library based on this, but with added support for Criteria and predefined Doctrine types 😀

Describe alternatives you've considered

Data Structures Extension, as noted.

Additional context

For context, I deal a lot with shoveling data around from very large databases and I have found the ext-ds a life saver, but sometimes I need some plain PHP style collections (like those offered in this library) and even the short time i have used this library I have found it relatively straightforward to work with.

I'd be happy over the coming weeks to collaborate on this 👍

Just wanted to open the conversation 😄

Add compatibility with Doctrine Collection interface

Is your feature request related to a problem? Please describe.

On our projects we use strictly typed collections for Doctrine. I think it will be nice if AbstractCollection class can implement methods from Doctrine Collection interface.

Describe the solution you'd like

I suggest to add methods to AbstractCollection and/or AbstractArray that will be compatible with Collection

  • public function removeElement($element)
  • public function containsKey($key)
  • public function get($key)
  • public function getKeys()
  • public function getValues()
  • public function set($key, $value)
  • public function key()
  • public function current()
  • public function next()
  • public function exists(Closure $p)
  • public function forAll(Closure $p)
  • public function partition(Closure $p)
  • public function indexOf($element)
  • public function slice($offset, $length = null)

Then user of the library can do something like this.

class ConcreteClassCollection extends Ramsey\Collection\AbstractCollection implements Doctrine\Common\Collections\Collection {
    public function getType(): string
    {
        return ConcreteClass::class;
    }
}

Describe alternatives you've considered

Maybe better approach is to add a trait?

class ConcreteClassCollection extends Ramsey\Collection\AbstractCollection implements Doctrine\Common\Collections\Collection {
    use DoctrineCollectionTrait;

    public function getType(): string
    {
        return ConcreteClass::class;
    }
}

[Proposal] Typed Queue

I use in a Project the Collection Class with a Entity as Type. This work really good and is really straightforward.
But i think a Queue (FIFO Principle) would match my needs even better.

Therefore, my consideration of whether it is a good extension for this project.
Is this a benefit for this Project?

TypedMap class

I'm interested on push the TypedMap class, is like a Map but keys and values are typed. This would follow the generics mimics of Map<K key, V value>

You could use the concrete class directly:

$map = new TypedMap('string', \My\Foo::class, $data);
$map['foo'] = new \My\Foo();

// this throw an exception since key is not a string
$map[1] = new \My\Foo();

// this throw an exception since value stdClass is not a \My\Foo
$map['baz'] = new \stdClass()

Or extending the AbstractTypedMap:

class FooTypedMap extends AbstractTypedMap
{
    public function getKeyType()
    {
        return "int"
    }

    public function getValueType()
    {
         return Foo::class;
    }
}

$map = new FooTypedMap([
    100 => new Foo(),
    200 => new Foo(2),
]);

Question: Should I place the class inside src/Map? Looks like this is the place of Maps since it contains the concrete class NamedParameterMap.
As this contains the interface, an abstract class and a concrete implementation looks more organized on its own namespace Ramsey\Collection\TypedMap that points to src/TypedMap.

Thanks and please advice.

Collection Manipulation Methods: sort, filter, diff, and other array function wrappers

I propose adding new methods to AbstractCollection to allow easy manipulation of the contents. We could then use these methods in place of using for/foreach loops and temporary variables for building new collections of data.

Current Problem

For example, given a class Foo and a typed Collection FooCollection:

Class Foo
{
    public $id;
    public $name;
    public $createdAt;

    public function __construct(int $id, string $name, DateTime $createdAt=null)
    {
        $this->id        = $id;
        $this->name      = $name;
        $this->createdAt = $createdAt;
    }
}

Class FooCollection extends \Ramsey\Collection\AbstractCollection
{
    public function getType()
    {
        return Foo::class;
    }
}

And you have created a collection of those Foos:

$foos = [
    new Foo(1, 'bar', new DateTime('now')),
    new Foo(2, 'baz', new DateTime('1 week ago')),
    new Foo(3, 'fizz', new DateTime('3 months ago')),
    new Foo(4, 'buzz', new DateTime('2 days ago')),
];

$fooCollection = new FooCollection($foos);

And you have a method which was passed as an argument a FooCollection, and you want to sort your collection by the createdAt time, you'd have to do something like this:

Class Bar{
    public function doSomething(FooCollection $fooCollection){
        $values = $fooCollection->toArray();
        usort($values, function($a, $b){
            return ($a->createdAt > $b->createdAt);
        });

        $fooCollection = new FooCollection($values);
        //Do something else with the Foos.
    }
}

Proposed Solution

I propose a sort method would be much simpler for users:

Class Bar{
    public function doSomething(FooCollection $fooCollection){
        $fooCollection->sort('createdAt');
        //Do something else with the Foos.
    }
}

Suggested Methods

Alters Contents / Fluent Interface

sort

/**
 * Orders the items by a property or return value of the method in the class
 * @param string property
 * @param string order 'desc'/'asc'
 * @return self
 */
sort($property, $order='asc')

Examples:

$fooCollection->sort('createdAt', 'desc'); //sorts by property
$fooCollection->sort('getName'); //will call the method 

filter

/**
 * Filters out items using a callback method
 * @param function $callback
 * @return self
 */
filter($callback)

Examples:

$foos = [
    new Foo(1, 'bar'),
    new Foo(2, 'baz'),
    new Foo(3, 'fizz'),
    new Foo(4, 'buzz'),
];
$fooCollection = new FooCollection($foos);

//$fooCollection will now contain only the 'bar' and 'baz' foos.
$fooCollection->filter(function($foo){
    return strlen($foo->name) <= 3;
});

where

/**
 * Filters to items where the property or return value of a method equals the given value
 * @param string property 
 * @param string value
 * @return self
 */
where($property, $value)

Examples:

$fooCollection->where('name', 'foo'); //accesses a property
$fooCollection->where('getName', 'foo'); //will call the method 

each

/**
 * Applies a callback method to each item
 * @param function $callback
 * @return self
 */
each($callback)

Examples:

//Every Foo's name in the collection will be updated to end in '.beta'
$fooCollection->each(function($foo){
    $foo->name .= '.beta';
});

diff

/**
 * Calculates the difference of two collections by comparing the objects - must be the same type
 * @param AbstractCollection
 */
diff($collection)

Examples:

$someFoos = new FooCollection([
    new Foo(1, 'bar'),
    new Foo(2, 'baz')
]);

$moreFoos = new FooCollection([
    new Foo(2, 'baz'),
    new Foo(3, 'fizz'),
    new Foo(4, 'buzz'),
]);

// $diffCollection should contain the 'bar', 'fizz' and 'buzz' Foos - the difference between the two. 
$diffCollection = $someFoos->diff($moreFoos);

intersect

/**
 * Calculates the intersection of two collections by comparing the objects - must be the same type
 * @param AbstractCollection
 */
intersect($collection)

Examples:

$someFoos = new FooCollection([
    new Foo(1, 'bar'),
    new Foo(2, 'baz')
]);

$moreFoos = new FooCollection([
    new Foo(2, 'baz'),
    new Foo(3, 'fizz'),
    new Foo(4, 'buzz'),
]);

// $intersectCollection should contain the 'baz' Foo - the intersection between the two. 
$intersectCollection = $someFoos->intersect($moreFoos);

unique

/**
 * Filters out non-unique values either by comparing the objects, or an optional property/method accessor
 * @param string property
 */
unique($property=null)

Examples:

$foos = new FooCollection([
    new Foo(1, 'bar'),
    new Foo(2, 'baz')  //Original "baz"
    new Foo(3, 'baz'), //a duplicate "baz" with different id
    new Foo(4, 'fizz'),
    new Foo(5, 'buzz'), //Original "buzz"
    new Foo(5, 'buzz'), //Exact copy of "buzz" 
]);

// $uniqueFoos will contain 5 foos, the extra "buzz" having been removed.
$uniqueFoos = $uniqueFoos->unique();


// $uniqueFoos will contain 4 foos, the extra "buzz" and "baz" (#3) having been removed
$uniqueFoos = $uniqueFoos->unique('name');

merge

/**
 * Merges the two collections - must be the same type
 * @param AbstractCollection
 */
merge($collection)

Examples:

$someFoos = new FooCollection([
    new Foo(1, 'bar'),
    new Foo(2, 'baz')
]);

$moreFoos = new FooCollection([
    new Foo(3, 'fizz'),
    new Foo(4, 'buzz'),
]);

// $allFoos should contain all 4 foos
$allFoos = $someFoos->intersect($moreFoos);

merge

/**
 * Merges the two collections - must be the same type
 * @param AbstractCollection
 */
merge($collection)

Examples:

$someFoos = new FooCollection([
    new Foo(1, 'bar'),
    new Foo(2, 'baz')
]);

$moreFoos = new FooCollection([
    new Foo(3, 'fizz'),
    new Foo(4, 'buzz'),
]);

// $allFoos should contain all 4 foos
$allFoos = $someFoos->intersect($moreFoos);

Analyzes/Return Data, does not alter contents.

column

/**
 * Wrapper for array_column: returns the values from a single property/method
 * @param string property 
 * @return array
 */
column($property)

Examples:

$names = $fooCollection->column('name'); //accesses a property
$names = $fooCollection->column('getName'); //will call the method 

first

/**
 * Returns the first item in the collection
 * @return stdClass
 */
first()

Examples:

$firstFoo = $fooCollection->first();

Cannot manipulate on Generic Collection

Describe the bug

I don't know this is a bug or a feature, but it looks strange for me.

To Reproduce...

I wrote a simple test case to reproduce the issue.

   <?php

   public function testDiffGenericCollection(): void
   {
       $bar1 = new Bar(1, 'a');
       $bar2 = new Bar(2, 'b');

       $barCollection1 = new Collection(Bar::class);
       $barCollection2 = new Collection(Bar::class);

       //Add 1 element to $barCollection1
       $barCollection1->add($bar1);

       //Add 2 elements to $barCollection2
       $barCollection2->add($bar1);
       $barCollection2->add($bar2);

       $diffCollection = $barCollection1->diff($barCollection2);

       $this->assertNotSame($diffCollection, $barCollection1);
       $this->assertNotSame($diffCollection, $barCollection2);
       $this->assertEquals([$bar2], $diffCollection->toArray());

       // Make sure original collections are untouched
       $this->assertEquals([$bar1], $barCollection1->toArray());
       $this->assertEquals([$bar1, $bar2], $barCollection2->toArray());
   }

And that test will fail because of:

1) Ramsey\Collection\Test\CollectionManipulationTest::testDiffGenericCollection
TypeError: Argument 1 passed to Ramsey\Collection\Collection::__construct() must be of the type string, array given, called in collection\src\AbstractCollection.php on line 294

Yes, because inside of AbstractCollection 294 we have this:

return new static(array_merge($diffAtoB, $diffBtoA));

Expected behavior

I expect that type of collection will be saved and this test case should pass.
I expect something like this:

<?php
        if (static::class === Collection::class) {
            return new static(static::getType(), array_merge($diffAtoB, $diffBtoA));
        }

 return new static(array_merge($diffAtoB, $diffBtoA));

Maybe I'm wrong, but Generic collections should also be able to support
diff, intersect, merge etc. methods.

Environment details

  • OS: Win 10
  • PHP 7.4
  • ramsey/collection version: 1.0.1

Map not working in String Collection

map method in AbstractCollection not working for Collection with type string
https://github.com/ramsey/collection/blob/master/src/AbstractCollection.php#L261

I have StringCollection

class StringCollection extends AbstractCollection
{
    public function getType(): string
    {
        return 'string';
    }
}

map method doesn't work when I want to change elements case.

$stringCollection = new StringCollection(['foo', 'bar']);

$upperStringCollection = $stringCollection->map(function($item) {
    return strtoupper($item);
}); 

print_r($upperStringCollection->toArray());

Expected $upperStringCollection value

Array
(
    [0] => FOO
    [1] => BAR
)

Actual $upperStringCollection value

Array
(
    [0] => foo
    [1] => bar
)

Merging sets does not remove duplicates

Description

If you merge a Set containing [X, Y] with a Set containing [Y, Z], the result Set will be [X, Y, Y, Z]. Which is incorrect.

Steps to reproduce

  1. Run the following code
$set1 = new Set('string', ['X', 'Y']);
$set2 = new Set('string', ['Y', 'Z']);
$set3 = $set1->merge($set2);
foreach($set3 as $el)
{
    echo($el . "\n");
}

  1. See how element 'Y' occurs twice.

Expected behavior

Element 'Y' should only occur once in set3

Screenshots or output

PHPStorm inspector output
Schermafbeelding 2021-10-07 om 11 37 51

Environment details

  • version of this package: 1.1.3
  • PHP version: 7.4.16
  • OS: macOS Big Sur (11.6)

Add support for CollectionInterface::reduce()

Background/problem

The library supports map() on CollectionInterface, but there is no reduce().

Proposal/solution

Add CollectionInterface::reduce() with the following signature:

/**
 * @template TCarry
 *
 * @param callable(TCarry, T):TCarry $callback A callable to apply to each
 *     item of the collection to reduce it to a single value.
 * @param TCarry|null $initial If provided, this is the initial value provided
 *     to the callback.
 *
 * @return TCarry
 */
public function reduce(callable $callback, mixed $initial = null): mixed;

Additional context

Since this is a change to the interface, it requires a major version bump.

PHPStorm type hinting for column()?

In column(), you can specify either a variable or a function name for the collection to column with. Given that I am using a typed collection, is there a way for PHPStorm to understand and code complete in this case?

ValueExtractorTrait does not extract from public methods with same name as private properties

Description

When AbstractCollection contains objects which have both a method and property with the same name, the value can never be extracted from the method through ValueExtractorTrait::extractValue().

Steps to reproduce

class Person
{
    public function __construct(private string $name)
    {
    }

    public function name(): string
    {
        return $this->name;
    }
}

$collection = new Ramsey\Collection\Collection();
$collection->add(new Person('Bob'));

print_r($collection->columns('name'));

The above triggers an error:

Error: Cannot access private property Person::$name

Expected behavior

Array
(
    [0] => Bob
)

Environment details

  • version of this package: 2.0.0
  • PHP version: 8.2.5

Cause

This seems to be because ValueExtractorTrait::extractValue() does not check if the property is actually accessible before trying to access it. It then proceeds to access it, causing the error, and then it never gets to checking whether a method exists with that same name which actually is accessible.

Possible solutions

One option would be to verify accessibility before attempting to access the property or method, but this would require use of reflection with obvious performance downsides.

Alternatively if it is detected that both the method and property exist of the same name, one could use a try/catch block to attempt to access them and then only throw an exception if both attempts failed.

Finally another "fix" could be to swap the position of accessing the property with that of accessing the method, which would work for the above use case (which is probably the most common). However, this would then cause a similar issue for objects with a private method and public property with the same name.

PHP 8.1 compatibility issue: return type of AbstractArray::offsetGet($offset)

Description

Hello! I use ramsey/uuid library in my code, and when I run my test on PHP 8.1 beta1 I'm getting the following error:

PHP Fatal error:  During inheritance of ArrayAccess: Uncaught Return type of Ramsey\Collection\AbstractArray::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/collection/src/AbstractArray.php:87
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/collection/src/AbstractArray.php:30
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/collection/src/AbstractCollection.php:48
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/uuid/src/Builder/BuilderCollection.php:31
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/uuid/src/FeatureSet.php:435
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/uuid/src/FeatureSet.php:170
/builds/tsp/quality-assurance/phpunit-allure/vendor/ramsey/uuid/src/UuidFactory.php:108

Steps to reproduce

Just extend \Ramsey\Collection\AbstractArray and try to create instance under PHP 8.1 beta.

Expected behavior

The instance is successfully created.

Environment details

  • version of this package: 1.1.4
  • PHP version: 8.1.0 beta1

AbstractCollection constructor overrides AbstractArray constructor behavior

An AbstractCollection is extending AbstractArray, so a derivated class from AbstractCollection, like 'Collection' class should behave just as an AbstractArray. But is not because the constructors behave in different ways.

You are defining the AbstractArray constructor as receive an array to populate the object, but in AbstractCollection you define the constructor to receive the type of the collection.

I suggest to you to not break compatibility with AbstractArray and to let the objects be instantiated with an array of certain type by removing the AbstractCollection constructor or move it to Collection class. As you are using collectionType property it would be possible to just extend AbstractCollection and override this property (of changed to protected), so, when the FooCollection class in instantiated it will allow only members of the correct type and when the object is populated then it will enforce the type policy.

Of course this would break the compatibility with your current implementations of AbstractCollection and Collection, depending of what way did you use to set the type policy.

What do you think?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.