Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Commit

Permalink
Merge branch 'feature/2718' into develop
Browse files Browse the repository at this point in the history
Close #2718
  • Loading branch information
weierophinney committed Jan 14, 2013
2 parents 2fc547d + 5c9914e commit 4a195bf
Show file tree
Hide file tree
Showing 2 changed files with 266 additions and 0 deletions.
116 changes: 116 additions & 0 deletions library/Zend/Authentication/Storage/Chain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_Authentication
*/

namespace Zend\Authentication\Storage;

use Zend\Stdlib\PriorityQueue;
use Zend\Authentication\Storage\StorageInterface;

/**
* @category Zend
* @package Zend_Authentication
* @subpackage Storage
*/
class Chain implements StorageInterface
{
/**
* Contains all storage that this authentication method uses. A storage
* placed in the priority queue with a higher priority is always used
* before using a storage with a lower priority.
*
* @var PriorityQueue
*/
protected $storageChain;

/**
* Initializes the priority queue.
*/
public function __construct()
{
$this->storageChain = new PriorityQueue();
}

/**
* @param StorageInterface $storage
* @param integer $priority
*/
public function add(StorageInterface $storage, $priority = 1)
{
$this->storageChain->insert($storage, $priority);
}

/**
* Loop over the queue of storage until a storage is found that is non-empty. If such
* storage is not found, then this chain storage itself is empty.
*
* In case a non-empty storage is found then this chain storage is also non-empty. Report
* that, but also make sure that all storage with higher priorty that are empty
* are filled.
*
* @see StorageInterface::isEmpty()
*/
public function isEmpty()
{
$storageWithHigherPriority = array();

// Loop invariant: $storageWithHigherPriority contains all storage with higher priorty
// than the current one.
foreach ($this->storageChain as $storage) {
if ($storage->isEmpty()) {
$storageWithHigherPriority[] = $storage;
continue;
}

$storageValue = $storage->read();
foreach ($storageWithHigherPriority as $higherPriorityStorage) {
$higherPriorityStorage->write($storageValue);
}

return false;
}

return true;
}

/**
* If the chain is non-empty then the storage with the top priority is guaranteed to be
* filled. Return its value.
*
* @see StorageInterface::read()
*/
public function read()
{
return $this->storageChain->top()->read();
}

/**
* Write the new $contents to all storage in the chain.
*
* @see StorageInterface::write()
*/
public function write($contents)
{
foreach ($this->storageChain as $storage) {
$storage->write($contents);
}
}

/**
* Clear all storage in the chain.
*
* @see StorageInterface::clear()
*/
public function clear()
{
foreach ($this->storageChain as $storage) {
$storage->clear();
}
}
}
150 changes: 150 additions & 0 deletions tests/ZendTest/Authentication/Storage/ChainTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_Uri
*/

namespace ZendTest\Authentication\Storage;

use Zend\Authentication\Storage\Chain,
Zend\Authentication\Storage\StorageInterface,
Zend\Authentication\Storage\NonPersistent;

use PHPUnit_Framework_TestCase as TestCase;

/**
* @category Zend
* @package Zend_Auth
* @subpackage UnitTests
* @group Zend_Auth
*/
class ChainTest extends TestCase
{
const ID = 1337;

/**
* Ensure chain without storage behavious as empty storage.
*/
public function testEmptyChain()
{
$chain = new Chain;

$this->assertTrue($chain->isEmpty());
}

/**
* Ensure chain with single empty storage behavious as expected.
*/
public function testSingularChainEmpty()
{
$chain = new Chain;
$chain->add($this->storageFactory());

$this->assertTrue($chain->isEmpty());
}

/**
* Ensure chain with single non-empty storage behavious as expected.
*/
public function testSingularChainNonEmpty()
{
$chain = new Chain;
$chain->add($this->storageFactory(self::ID));

$this->assertFalse($chain->isEmpty());
$this->assertEquals(self::ID, $chain->read());
}

/**
* Ensure the priority of storage engines is correctly used.
*/
public function testChainPriority()
{
$storageA = $this->storageFactory();
$storageB = $this->storageFactory(self::ID);

$chain = new Chain;
$chain->add($storageA); // Defaults to 1
$chain->add($storageB, 10);
$chain->isEmpty();

// Storage B has higher priority AND is non-empty. Thus
// storage A should been used at all and remain empty.
$this->assertTrue($storageA->isEmpty());
}

/**
* Ensure that a chain with empty storages is considered empty and
* won't populated any of its underlying storages.
*/
public function testEmptyChainIsEmpty()
{
$emptyStorageA = $this->storageFactory();
$emptyStorageB = $this->storageFactory();

$chain = new Chain;
$chain->add($emptyStorageA);
$chain->add($emptyStorageB);

$this->assertTrue($chain->isEmpty());

// Storage A and B remain empty
$this->assertTrue($emptyStorageA->isEmpty());
$this->assertTrue($emptyStorageB->isEmpty());
}

/**
* Ensure that chain will yield non-empty if one of its underlying storage
* engines is non-empty.
*
* Make sure that storage engines with higher priority then the first non-empty
* storage engine get populated with that same content.
*/
public function testSuccessfullReadWillPopulateStoragesWithHigherPriority()
{
$emptyStorageA = $this->storageFactory();
$emptyStorageB = $this->storageFactory();
$storageC = $this->storageFactory(self::ID);
$emptyStorageD = $this->storageFactory();

$chain = new Chain;
$chain->add($emptyStorageA);
$chain->add($emptyStorageB);
$chain->add($storageC);
$chain->add($emptyStorageD);

// Chain is non empty
$this->assertFalse($chain->isEmpty());
$this->assertEquals(self::ID, $chain->read());

// Storage A and B are filled
$this->assertFalse($emptyStorageA->isEmpty());
$this->assertEquals(self::ID, $emptyStorageA->read());
$this->assertFalse($emptyStorageA->isEmpty());
$this->assertEquals(self::ID, $emptyStorageB->read());

// Storage C and D remain identical
$this->assertFalse($storageC->isEmpty());
$this->assertEquals(self::ID, $storageC->read());
$this->assertTrue($emptyStorageD->isEmpty());
}

/**
* @param mixed $identity
* @return StorageInterface
*/
protected function storageFactory($identity = null)
{
$storage = new NonPersistent();

if ($identity !== null) {
$storage->write($identity);
}

return $storage;
}
}

0 comments on commit 4a195bf

Please sign in to comment.