Browse Source

Make add() and remove() operate in an immutable way.

Make the cookie collection modifier methods work in an immutable
fashion. This is necessary to preserve the immutability of the
Request/Response objects that we plan on using cookie collections with.

We still need to make the cookies immutable, but that is a separate
task.
Mark Story 9 years ago
parent
commit
91aeee154e

+ 13 - 8
src/Http/Cookie/CookieCollection.php

@@ -20,6 +20,9 @@ use IteratorAggregate;
 
 /**
  * Cookie Collection
+ *
+ * Provides an immutable collection of cookies objects. Adding or removing
+ * to a collection returns a *new* collection that you must retain.
  */
 class CookieCollection implements IteratorAggregate, Countable
 {
@@ -57,17 +60,18 @@ class CookieCollection implements IteratorAggregate, Countable
     }
 
     /**
-     * Add a cookie to the collection
+     * Add a cookie and get an updated collection.
      *
      * @param \Cake\Http\Cookie\CookieInterface $cookie Cookie instance to add.
-     * @return $this
+     * @return static
      */
     public function add(CookieInterface $cookie)
     {
         $key = mb_strtolower($cookie->getName());
-        $this->cookies[$key] = $cookie;
+        $new = clone $this;
+        $new->cookies[$key] = $cookie;
 
-        return $this;
+        return $new;
     }
 
     /**
@@ -103,18 +107,19 @@ class CookieCollection implements IteratorAggregate, Countable
     }
 
     /**
-     * Remove a cookie from the collection
+     * Remove a cookie from the collection and get a new collection
      *
      * If the cookie is not in the collection, this method will do nothing.
      *
      * @param string $name The name of the cookie to remove.
-     * @return $this
+     * @return static
      */
     public function remove($name)
     {
-        unset($this->cookies[mb_strtolower($name)]);
+        $new = clone $this;
+        unset($new->cookies[mb_strtolower($name)]);
 
-        return $this;
+        return $new;
     }
 
     /**

+ 17 - 9
tests/TestCase/Http/Cookie/CookieCollectionTest.php

@@ -83,14 +83,19 @@ class CookieCollectionTest extends TestCase
         $this->assertCount(0, $collection);
 
         $remember = new Cookie('remember_me', 'a');
-        $this->assertSame($collection, $collection->add($remember));
-        $this->assertCount(1, $collection);
-        $this->assertTrue($collection->has('remember_me'));
-        $this->assertSame($remember, $collection->get('remember_me'));
+        $new = $collection->add($remember);
+        $this->assertNotSame($new, $collection->add($remember));
+        $this->assertCount(0, $collection, 'Original instance not modified');
+        $this->assertCount(1, $new);
+        $this->assertFalse($collection->has('remember_me'), 'Original instance not modified');
+        $this->assertTrue($new->has('remember_me'));
+        $this->assertSame($remember, $new->get('remember_me'));
 
         $rememberNo = new Cookie('remember_me', 'no');
-        $this->assertSame($collection, $collection->add($remember)->add($rememberNo));
-        $this->assertSame($rememberNo, $collection->get('remember_me'));
+        $second = $new->add($remember)->add($rememberNo);
+        $this->assertCount(1, $second);
+        $this->assertNotSame($second, $new);
+        $this->assertSame($rememberNo, $second->get('remember_me'));
     }
 
     /**
@@ -125,9 +130,12 @@ class CookieCollectionTest extends TestCase
 
         $collection = new CookieCollection($cookies);
         $this->assertInstanceOf(Cookie::class, $collection->get('REMEMBER_me'), 'case insensitive cookie names');
-        $this->assertSame($collection, $collection->remove('remember_me'));
-        $this->assertFalse($collection->has('remember_me'));
-        $this->assertNull($collection->get('remember_me'));
+        $new = $collection->remove('remember_me');
+        $this->assertTrue($collection->has('remember_me'), 'old instance not modified');
+
+        $this->assertNotSame($new, $collection);
+        $this->assertFalse($new->has('remember_me'), 'should be removed');
+        $this->assertNull($new->get('remember_me'), 'should be removed');
     }
 
     /**