Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 76 additions & 1 deletion src/Storage/Adapter/AdapterOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
*/
class AdapterOptions extends AbstractOptions
{
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = [];

/**
* The adapter using these options
*
Expand Down Expand Up @@ -79,7 +87,7 @@ public function setAdapter(StorageInterface $adapter = null)
/**
* Set key pattern
*
* @param null|string $keyPattern
* @param string $keyPattern
* @throws Exception\InvalidArgumentException
* @return AdapterOptions
*/
Expand Down Expand Up @@ -261,4 +269,71 @@ protected function normalizeTtl(&$ttl)
throw new Exception\InvalidArgumentException("TTL can't be negative");
}
}

/**
* Cast to array
*
* @return array
*/
public function toArray()
{
$array = [];
$transform = function ($letters) {
$letter = array_shift($letters);
return '_' . strtolower($letter);
};
foreach ($this as $key => $value) {
if ($key === '__strictMode__' || $key === '__prioritizedProperties__') {
continue;
}
$normalizedKey = preg_replace_callback('/([A-Z])/', $transform, $key);
$array[$normalizedKey] = $value;
}
return $array;
}

/**
* {@inheritdoc}
*
* NOTE: This method was overwritten just to support prioritized properties
* {@link https://github.com/zendframework/zf2/issues/6381}
*
* @param array|Traversable|AbstractOptions $options
* @throws Exception\InvalidArgumentException
* @return AbstractOptions Provides fluent interface
*/
public function setFromArray($options)
{
if ($this->__prioritizedProperties__) {
if ($options instanceof AbstractOptions) {
$options = $options->toArray();
}

if ($options instanceof Traversable) {
$options = iterator_to_array($options);
} elseif (!is_array($options)) {
throw new Exception\InvalidArgumentException(
sprintf(
'Parameter provided to %s must be an %s, %s or %s',
__METHOD__,
'array',
'Traversable',
'Zend\Stdlib\AbstractOptions'
)
);
}

// Sort prioritized options to top
$options = array_change_key_case($options, CASE_LOWER);
foreach (array_reverse($this->__prioritizedProperties__) as $key) {
if (isset($options[$key])) {
$options = [$key => $options[$key]] + $options;
} elseif (isset($options[($key = str_replace('_', '', $key))])) {
$options = [$key => $options[$key]] + $options;
}
}
}

return parent::setFromArray($options);
}
}
8 changes: 8 additions & 0 deletions src/Storage/Adapter/MemcacheOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
*/
class MemcacheOptions extends AdapterOptions
{
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = ['resource_manager', 'resource_id'];

/**
* The namespace separator
* @var string
Expand Down
8 changes: 8 additions & 0 deletions src/Storage/Adapter/MemcachedOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
*/
class MemcachedOptions extends AdapterOptions
{
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = ['resource_manager', 'resource_id'];

/**
* The namespace separator
* @var string
Expand Down
8 changes: 8 additions & 0 deletions src/Storage/Adapter/MongoDbOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@

class MongoDbOptions extends AdapterOptions
{
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = ['resource_manager', 'resource_id'];

/**
* The namespace separator
*
Expand Down
8 changes: 8 additions & 0 deletions src/Storage/Adapter/RedisOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@

class RedisOptions extends AdapterOptions
{
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once
*
* @var string[]
*/
protected $__prioritizedProperties__ = ['resource_manager', 'resource_id'];

/**
* The namespace separator
* @var string
Expand Down
168 changes: 168 additions & 0 deletions test/Storage/Adapter/AdapterOptionsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace ZendTest\Cache\Storage\Adapter;

use Zend\Cache\Storage\StorageInterface;
use Zend\Cache\Storage\Event;
use Zend\Cache\Storage\Adapter\AbstractAdapter;
use Zend\Cache\Storage\Adapter\AdapterOptions;
use Zend\Cache\Exception;

/**
* @group Zend_Cache
* @covers Zend\Cache\Storage\Adapter\AdapterOptions<extended>
*/
class AdapterOptionsTest extends \PHPUnit_Framework_TestCase
{
/**
* Mock of the storage
*
* @var StorageInterface
*/
protected $storage;

/**
* Adapter options
*
* @var null|AdapterOptions
*/
protected $options;

public function setUp()
{
$this->options = new AdapterOptions();
}

public function testKeyPattern()
{
// test default value
$this->assertSame('', $this->options->getKeyPattern());

$this->assertSame($this->options, $this->options->setKeyPattern('/./'));
$this->assertSame('/./', $this->options->getKeyPattern());
}

public function testSetKeyPatternAllowEmptyString()
{
// first change to something different as an empty string is the default
$this->options->setKeyPattern('/.*/');

$this->options->setKeyPattern('');
$this->assertSame('', $this->options->getKeyPattern());
}

public function testSetKeyPatternThrowsInvalidArgumentExceptionOnInvalidPattern()
{
$this->setExpectedException(Exception\InvalidArgumentException::class);
$this->options->setKeyPattern('foo bar');
}

public function testNamespace()
{
$this->assertSame($this->options, $this->options->setNamespace('foobar'));
$this->assertSame('foobar', $this->options->getNamespace());
}

public function testReadable()
{
$this->assertSame($this->options, $this->options->setReadable(false));
$this->assertSame(false, $this->options->getReadable());

$this->assertSame($this->options, $this->options->setReadable(true));
$this->assertSame(true, $this->options->getReadable());
}

public function testWritable()
{
$this->assertSame($this->options, $this->options->setWritable(false));
$this->assertSame(false, $this->options->getWritable());

$this->assertSame($this->options, $this->options->setWritable(true));
$this->assertSame(true, $this->options->getWritable());
}

public function testTtl()
{
// infinite default value
$this->assertSame(0, $this->options->getTtl());

$this->assertSame($this->options, $this->options->setTtl(12345));
$this->assertSame(12345, $this->options->getTtl());
}

public function testSetTtlThrowsInvalidArgumentExceptionOnNegativeValue()
{
$this->setExpectedException(Exception\InvalidArgumentException::class);
$this->options->setTtl(-1);
}

public function testSetTtlAutoconvertToIntIfPossible()
{
$this->options->setTtl(12345.0);
$this->assertSame(12345, $this->options->getTtl());

$this->options->setTtl(12345.678);
$this->assertSame(12345.678, $this->options->getTtl());
}

public function testTriggerOptionEvent()
{
// setup an adapter implements EventsCapableInterface
$adapter = $this->getMockForAbstractClass(AbstractAdapter::class);
$this->options->setAdapter($adapter);

// setup event listener
$calledArgs = null;
$adapter->getEventManager()->attach('option', function () use (& $calledArgs) {
$calledArgs = func_get_args();
});

// trigger by changing an option
$this->options->setWritable(false);

// assert (hopefully) called listener and arguments
$this->assertCount(1, $calledArgs, '"option" event was not triggered or got a wrong number of arguments');
$this->assertInstanceOf(Event::class, $calledArgs[0]);
$this->assertEquals(['writable' => false], $calledArgs[0]->getParams()->getArrayCopy());
}

public function testSetFromArrayWithoutPrioritizedOptions()
{
$this->assertSame($this->options, $this->options->setFromArray([
'kEy_pattERN' => '/./',
'nameSPACE' => 'foobar',
]));
$this->assertSame('/./', $this->options->getKeyPattern());
$this->assertSame('foobar', $this->options->getNamespace());
}

public function testSetFromArrayWithPrioritizedOptions()
{
$options = $this->getMock(AdapterOptions::class, ['setKeyPattern', 'setNamespace', 'setWritable']);

// set key_pattern and namespace to be a prioritized options
$optionsRef = new \ReflectionObject($options);
$propRef = $optionsRef->getProperty('__prioritizedProperties__');
$propRef->setAccessible(true);
$propRef->setValue($options, ['key_pattern', 'namespace']);

// expected order of setter be called
$options->expects($this->at(0))->method('setKeyPattern')->with($this->equalTo('/./'));
$options->expects($this->at(1))->method('setNamespace')->with($this->equalTo('foobar'));
$options->expects($this->at(2))->method('setWritable')->with($this->equalTo(false));

// send unordered options array
$this->assertSame($options, $options->setFromArray([
'nAmeSpace' => 'foobar',
'WriTAble' => false,
'KEY_paTTern' => '/./',
]));
}
}