Browse Source

Fix access to inner iterator methods in some cases.

A fix was made to 3.5 that allows subclasses of ArrayIterator to have
userland methods defined on collections via IteratorIterator's __call
features. However a conflicting change was made in 3.next to block
access to the in-place mutation methods available on ArrayIterator. This
set of changes attempts to compromise between those two goals.

This also fixes the 3.next build which is currently broken.

Refs #11045
Refs #11396
Mark Story 8 years ago
parent
commit
d4051b93c3
2 changed files with 10 additions and 4 deletions
  1. 9 2
      src/Collection/Collection.php
  2. 1 2
      tests/TestCase/Collection/CollectionTest.php

+ 9 - 2
src/Collection/Collection.php

@@ -73,9 +73,12 @@ class Collection extends IteratorIterator implements CollectionInterface, Serial
     }
 
     /**
-     * Throws an exception.
+     * Dynamic method handler
      *
-     * Collection doesn't permit access to methods of the inner iterator.
+     * Collections do not allow access to methods of the inner iterator,
+     * if that iterator is one of the PHP base classes as many of
+     * these methods allow in-place mutation which breaks the immutability
+     * Collection tries to provide.
      *
      * @param string $name Method name.
      * @param array $args Method arguments.
@@ -84,6 +87,10 @@ class Collection extends IteratorIterator implements CollectionInterface, Serial
      */
     public function __call($name, $args)
     {
+        if (!method_exists(ArrayIterator::class, $name)) {
+            $inner = $this->getInnerIterator();
+            return call_user_func_array([$inner, $name], $args);
+        }
         throw new BadMethodCallException(sprintf('Call to undefined method %s::%s()', get_class($this), $name));
     }
 

+ 1 - 2
tests/TestCase/Collection/CollectionTest.php

@@ -2547,7 +2547,7 @@ class CollectionTest extends TestCase
         $this->assertTrue(method_exists($iterator, 'checkValues'));
         $this->assertTrue($iterator->checkValues());
 
-        //We need to perform at least two collection operation to trigger the issue.
+        // We need to perform at least two collection operation to trigger the issue.
         $newIterator = $iterator
             ->filter(function ($item) {
                 return $item < 5;
@@ -2556,7 +2556,6 @@ class CollectionTest extends TestCase
                 return $item > 2;
             });
 
-        $this->assertTrue(method_exists($newIterator, 'checkValues'), 'Our method has gone missing!');
         $this->assertTrue($newIterator->checkValues());
         $this->assertCount(3, $newIterator->toArray());
     }