Skip to content

Commit

Permalink
ENHANCEMENT Protect against infinity loops
Browse files Browse the repository at this point in the history
  • Loading branch information
Damian Mooyman authored and Sam Minnée committed Feb 27, 2017
1 parent 2c1a787 commit bedff42
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 7 deletions.
20 changes: 19 additions & 1 deletion src/Collections/CachedConfigCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ class CachedConfigCollection implements ConfigCollectionInterface
*/
protected $flush = false;

/**
* Set to true while building config.
* Used to protect against infinite loops.
*
* @var bool
*/
protected $building = false;

/**
* @return static
*/
Expand Down Expand Up @@ -91,8 +99,18 @@ public function getCollection()
return $this->collection;
}

// Protect against infinity loop
if ($this->building) {
throw new BadMethodCallException("Infinite loop detected. Config could not be bootstrapped.");
}
$this->building = true;

// Cache missed
$this->collection = call_user_func($this->collectionCreator);
try {
$this->collection = call_user_func($this->collectionCreator);
} finally {
$this->building = false;
}

// Note: Config may be yet modified prior to deferred save, but after Core.php
// however no formal api for this yet
Expand Down
43 changes: 37 additions & 6 deletions tests/Collections/CachedConfigCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace SilverStripe\Config\Tests\Collections;

use BadMethodCallException;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use SilverStripe\Config\Collections\CachedConfigCollection;
Expand All @@ -10,7 +11,6 @@
use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\CacheItemInterface;
use SilverStripe\Config\Collections\ConfigCollectionInterface;
use SilverStripe\Config\Collections\MemoryConfigCollection;

class CachedConfigCollectionTest extends TestCase
{
Expand Down Expand Up @@ -82,11 +82,6 @@ public function testCacheMiss()
/** @var CacheItemPoolInterface|ObjectProphecy $mockCache */
$mockCache = $this->prophet->prophesize(CacheItemPoolInterface::class);

/** @var ConfigCollectionInterface|ObjectProphecy $mockCollection */
$mockCollection = $this->prophet->prophesize(ConfigCollectionInterface::class);
$mockCollection->get('test', 'name', 0)->willReturn('value');
$mockCollection->exists('test', 'name', 0)->willReturn(true);

// Mock item for collection key
/** @var CacheItemInterface|ObjectProphecy $mockCacheItem */
$mockCacheItem = $this->prophet->prophesize(CacheItemInterface::class);
Expand All @@ -96,6 +91,11 @@ public function testCacheMiss()
->willReturn(false)
->shouldBeCalled();

/** @var ConfigCollectionInterface|ObjectProphecy $mockCollection */
$mockCollection = $this->prophet->prophesize(ConfigCollectionInterface::class);
$mockCollection->get('test', 'name', 0)->willReturn('value');
$mockCollection->exists('test', 'name', 0)->willReturn(true);

// Save immediately, save again on __destruct
$mockCacheItem->set($mockCollection->reveal())->shouldBeCalledTimes(2);

Expand All @@ -120,4 +120,35 @@ public function testCacheMiss()
// Write back changes to cache
$collection->__destruct();
}

public function testInifiniteLoop()
{
$this->setExpectedException(
BadMethodCallException::class,
"Infinite loop detected. Config could not be bootstrapped."
);

// Mock cache
/** @var CacheItemPoolInterface|ObjectProphecy $mockCache */
$mockCache = $this->prophet->prophesize(CacheItemPoolInterface::class);
/** @var CacheItemInterface|ObjectProphecy $mockCacheItem */
$mockCacheItem = $this->prophet->prophesize(CacheItemInterface::class);
$mockCache
->getItem(CachedConfigCollection::CACHE_KEY)
->willReturn($mockCacheItem->reveal())
->shouldBeCalled();
$mockCacheItem
->isHit()
->willReturn(false)
->shouldBeCalled();

// Build new config
$collection = new CachedConfigCollection();
$collection->setPool($mockCache->reveal());
$collection->setCollectionCreator(function() use ($collection) {
$collection->getCollection();
});
$collection->getCollection();
$this->fail("Expected exception");
}
}

0 comments on commit bedff42

Please sign in to comment.