This repository has been archived by the owner on Jan 8, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feature/2718' into develop
Close #2718
- Loading branch information
Showing
2 changed files
with
266 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |