Skip to content

Commit 1d01657

Browse files
authored
Merge 818416d into 23b86d3
2 parents 23b86d3 + 818416d commit 1d01657

File tree

5 files changed

+245
-12
lines changed

5 files changed

+245
-12
lines changed

src/Exception/InvalidArgumentException.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,41 @@ public static function fromKeyAndInvalidTTL(string $key, $ttl) : self
1515
is_object($ttl) ? get_class($ttl) : gettype($ttl)
1616
));
1717
}
18+
19+
public static function fromInvalidKeyCharacters(string $invalidKey) : self
20+
{
21+
return new self(sprintf(
22+
'Key "%s" is in an invalid format - must not contain characters: {}()/\@:',
23+
$invalidKey
24+
));
25+
}
26+
27+
public static function fromInvalidType($invalidKey) : self
28+
{
29+
return new self(sprintf(
30+
'Key was not a valid type. Expected string, received %s',
31+
is_object($invalidKey) ? get_class($invalidKey) : gettype($invalidKey)
32+
));
33+
}
34+
35+
public static function fromEmptyKey() : self
36+
{
37+
return new self('Requested key was an empty string.');
38+
}
39+
40+
public static function fromNonIterableKeys($invalidKeys) : self
41+
{
42+
return new self(sprintf(
43+
'Keys passed were not iterable (i.e. \Traversable or array), received: %s',
44+
is_object($invalidKeys) ? get_class($invalidKeys) : gettype($invalidKeys)
45+
));
46+
}
47+
48+
public static function fromNonIterableValues($invalidValues) : self
49+
{
50+
return new self(sprintf(
51+
'Values passed were not iterable (i.e. \Traversable or array), received: %s',
52+
is_object($invalidValues) ? get_class($invalidValues) : gettype($invalidValues)
53+
));
54+
}
1855
}

src/SimpleCacheAdapter.php

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,52 @@ public function __construct(DoctrineCache $doctrineCache)
3636
}
3737
}
3838

39+
/**
40+
* @param mixed $key
41+
* @throws \Roave\DoctrineSimpleCache\Exception\InvalidArgumentException
42+
*/
43+
private function validateKey($key) : void
44+
{
45+
if (!is_string($key)) {
46+
throw Exception\InvalidArgumentException::fromInvalidType($key);
47+
}
48+
49+
if ('' === $key) {
50+
throw Exception\InvalidArgumentException::fromEmptyKey();
51+
}
52+
53+
if (preg_match('/[' . preg_quote('{}()/\@:', '/') . ']/', $key)) {
54+
throw Exception\InvalidArgumentException::fromInvalidKeyCharacters($key);
55+
}
56+
}
57+
58+
/**
59+
* @param mixed $keys
60+
* @return array
61+
* @throws \Roave\DoctrineSimpleCache\Exception\InvalidArgumentException
62+
*/
63+
private function filterValidateMultipleKeys($keys) : array
64+
{
65+
if ($keys instanceof \Traversable) {
66+
$keys = iterator_to_array($keys);
67+
}
68+
69+
if (!is_array($keys)) {
70+
throw Exception\InvalidArgumentException::fromNonIterableKeys($keys);
71+
}
72+
73+
array_map([$this, 'validateKey'], $keys);
74+
75+
return $keys;
76+
}
77+
3978
/**
4079
* {@inheritDoc}
4180
*/
4281
public function get($key, $default = null)
4382
{
83+
$this->validateKey($key);
84+
4485
$value = $this->doctrineCache->fetch($key);
4586
if ($value === false) {
4687
return $default;
@@ -54,6 +95,8 @@ public function get($key, $default = null)
5495
*/
5596
public function set($key, $value, $ttl = null) : bool
5697
{
98+
$this->validateKey($key);
99+
57100
if ($ttl === null) {
58101
return $this->doctrineCache->save($key, $value);
59102
}
@@ -78,6 +121,7 @@ public function set($key, $value, $ttl = null) : bool
78121
*/
79122
public function delete($key) : bool
80123
{
124+
$this->validateKey($key);
81125
return $this->doctrineCache->delete($key);
82126
}
83127

@@ -90,18 +134,33 @@ public function clear() : bool
90134
}
91135

92136
/**
93-
* {@inheritDoc}
137+
* @param array|\Traversable $keys
138+
* @param mixed $default
139+
* @return array
140+
* @throws \Roave\DoctrineSimpleCache\Exception\InvalidArgumentException
94141
*/
95142
public function getMultiple($keys, $default = null)
96143
{
144+
$keys = $this->filterValidateMultipleKeys($keys);
97145
return array_merge(array_fill_keys($keys, $default), $this->doctrineCache->fetchMultiple($keys));
98146
}
99147

100148
/**
101-
* {@inheritDoc}
149+
* @param array|\Traversable $values
150+
* @param null|int|\DateInterval $ttl
151+
* @return bool
152+
* @throws \Roave\DoctrineSimpleCache\Exception\InvalidArgumentException
102153
*/
103154
public function setMultiple($values, $ttl = null) : bool
104155
{
156+
if (!$values instanceof \Traversable && !is_array($values)) {
157+
throw Exception\InvalidArgumentException::fromNonIterableKeys($values);
158+
}
159+
160+
foreach ($values as $k => $v) {
161+
$this->validateKey($k);
162+
}
163+
105164
if ($ttl === null) {
106165
return $this->doctrineCache->saveMultiple($values);
107166
}
@@ -122,12 +181,15 @@ public function setMultiple($values, $ttl = null) : bool
122181
}
123182

124183
/**
125-
* {@inheritDoc}
184+
* @param array|\Traversable $keys
185+
* @return bool
186+
* @throws \Roave\DoctrineSimpleCache\Exception\InvalidArgumentException
126187
*/
127188
public function deleteMultiple($keys) : bool
128189
{
129-
$success = true;
190+
$keys = $this->filterValidateMultipleKeys($keys);
130191

192+
$success = true;
131193
foreach ($keys as $key) {
132194
if (!$this->delete($key)) {
133195
$success = false;
@@ -142,6 +204,7 @@ public function deleteMultiple($keys) : bool
142204
*/
143205
public function has($key) : bool
144206
{
207+
$this->validateKey($key);
145208
return $this->doctrineCache->contains($key);
146209
}
147210

test/unit/CacheIntegrationTest.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,8 @@ protected function setUp() : void
2727
// @todo: Let's make these tests pass
2828
$this->skippedTests['testSetMultipleWithGenerator'] = true;
2929
$this->skippedTests['testGetMultipleWithGenerator'] = true;
30-
$this->skippedTests['testGetInvalidKeys'] = true;
31-
$this->skippedTests['testGetMultipleInvalidKeys'] = true;
3230
$this->skippedTests['testGetMultipleNoIterable'] = true;
33-
$this->skippedTests['testSetInvalidKeys'] = true;
34-
$this->skippedTests['testSetMultipleInvalidKeys'] = true;
3531
$this->skippedTests['testSetMultipleNoIterable'] = true;
36-
$this->skippedTests['testHasInvalidKeys'] = true;
37-
$this->skippedTests['testDeleteInvalidKeys'] = true;
38-
$this->skippedTests['testDeleteMultipleInvalidKeys'] = true;
3932
$this->skippedTests['testDeleteMultipleNoIterable'] = true;
4033
$this->skippedTests['testObjectDoesNotChangeInCache'] = true;
4134
$this->skippedTests['testDataTypeBoolean'] = true;

test/unit/Exception/InvalidArgumentExceptionTest.php

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace RoaveTest\DoctrineSimpleCache\Exception;
55

66
use Roave\DoctrineSimpleCache\Exception\InvalidArgumentException;
7+
use Psr\SimpleCache\InvalidArgumentException as PsrInvalidArgumentException;
78

89
/**
910
* @covers \Roave\DoctrineSimpleCache\Exception\InvalidArgumentException
@@ -15,13 +16,50 @@ public function testFromKeyAndInvalidTTLObject()
1516
$invalidTTL = new \stdClass();
1617
$exception = InvalidArgumentException::fromKeyAndInvalidTTL('key', $invalidTTL);
1718
self::assertInstanceOf(InvalidArgumentException::class, $exception);
18-
self::assertInstanceOf(\Psr\SimpleCache\InvalidArgumentException::class, $exception);
19+
self::assertInstanceOf(PsrInvalidArgumentException::class, $exception);
1920

2021
self::assertStringMatchesFormat(
2122
'TTL for "%s" should be defined by an integer or a DateInterval, but stdClass is given.',
2223
$exception->getMessage()
2324
);
2425
}
26+
public function testFromInvalidKeyCharacters()
27+
{
28+
$invalidKey = uniqid('invalidKey', true);
29+
$exception = InvalidArgumentException::fromInvalidKeyCharacters($invalidKey);
30+
self::assertInstanceOf(InvalidArgumentException::class, $exception);
31+
self::assertInstanceOf(PsrInvalidArgumentException::class, $exception);
32+
self::assertSame(
33+
sprintf(
34+
'Key "%s" is in an invalid format - must not contain characters: {}()/\@:',
35+
$invalidKey
36+
),
37+
$exception->getMessage()
38+
);
39+
}
40+
41+
public function testFromInvalidType()
42+
{
43+
$invalidKey = random_int(100, 200);
44+
$exception = InvalidArgumentException::fromInvalidType($invalidKey);
45+
self::assertInstanceOf(InvalidArgumentException::class, $exception);
46+
self::assertInstanceOf(PsrInvalidArgumentException::class, $exception);
47+
self::assertSame(
48+
'Key was not a valid type. Expected string, received integer',
49+
$exception->getMessage()
50+
);
51+
}
52+
53+
public function testFromEmptyKey()
54+
{
55+
$exception = InvalidArgumentException::fromEmptyKey();
56+
self::assertInstanceOf(InvalidArgumentException::class, $exception);
57+
self::assertInstanceOf(PsrInvalidArgumentException::class, $exception);
58+
self::assertSame(
59+
'Requested key was an empty string.',
60+
$exception->getMessage()
61+
);
62+
}
2563

2664
public function testFromKeyAndInvalidTTLNonObject()
2765
{
@@ -35,4 +73,28 @@ public function testFromKeyAndInvalidTTLNonObject()
3573
$exception->getMessage()
3674
);
3775
}
76+
77+
public function testFromNonIterableKeys()
78+
{
79+
$invalidKey = random_int(100, 200);
80+
$exception = InvalidArgumentException::fromNonIterableKeys($invalidKey);
81+
self::assertInstanceOf(InvalidArgumentException::class, $exception);
82+
self::assertInstanceOf(PsrInvalidArgumentException::class, $exception);
83+
self::assertSame(
84+
'Keys passed were not iterable (i.e. \Traversable or array), received: integer',
85+
$exception->getMessage()
86+
);
87+
}
88+
89+
public function testFromNonIterableValues()
90+
{
91+
$invalidValue = random_int(100, 200);
92+
$exception = InvalidArgumentException::fromNonIterableValues($invalidValue);
93+
self::assertInstanceOf(InvalidArgumentException::class, $exception);
94+
self::assertInstanceOf(PsrInvalidArgumentException::class, $exception);
95+
self::assertSame(
96+
'Values passed were not iterable (i.e. \Traversable or array), received: integer',
97+
$exception->getMessage()
98+
);
99+
}
38100
}

test/unit/SimpleCacheAdapterTest.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,38 @@ public function invalidTTLs() : array
3333
];
3434
}
3535

36+
public function validKeys()
37+
{
38+
return [
39+
['AbC19_.'],
40+
['1234567890123456789012345678901234567890123456789012345678901234'],
41+
];
42+
}
43+
44+
public function invalidKeys()
45+
{
46+
return [
47+
[''],
48+
[true],
49+
[false],
50+
[null],
51+
[2],
52+
[2.5],
53+
['{str'],
54+
['rand{'],
55+
['rand{str'],
56+
['rand}str'],
57+
['rand(str'],
58+
['rand)str'],
59+
['rand/str'],
60+
['rand\\str'],
61+
['rand@str'],
62+
['rand:str'],
63+
[new \stdClass()],
64+
[['array']],
65+
];
66+
}
67+
3668
public function testConstructorThrowsExceptionWhenNotMultiPuttableCacheIsUsed()
3769
{
3870
/** @var NotMultiPuttableCache|\PHPUnit_Framework_MockObject_MockObject $doctrineCache */
@@ -202,6 +234,44 @@ public function testGetMultipleWithPartialKeys()
202234
self::assertSame($values, $psrCache->getMultiple($keys, $default));
203235
}
204236

237+
/**
238+
* @param mixed $key
239+
* @dataProvider invalidKeys
240+
*/
241+
public function testGetMultipleThrowsExceptionWithInvalidKeys($key)
242+
{
243+
$this->expectException(InvalidArgumentException::class);
244+
245+
$psrCache = new SimpleCacheAdapter(new ArrayCache());
246+
$psrCache->getMultiple([$key]);
247+
}
248+
249+
/**
250+
* @param mixed $key
251+
* @dataProvider validKeys
252+
*/
253+
public function testGetMultipleAcceptsTraversable($key)
254+
{
255+
$values = [
256+
$key => uniqid('value', true),
257+
];
258+
259+
/** @var FullyImplementedCache|\PHPUnit_Framework_MockObject_MockObject $doctrineCache */
260+
$doctrineCache = $this->createMock(FullyImplementedCache::class);
261+
$doctrineCache->expects(self::once())->method('fetchMultiple')->with(array_keys($values))->willReturn($values);
262+
263+
$psrCache = new SimpleCacheAdapter($doctrineCache);
264+
$psrCache->getMultiple(new \ArrayObject(array_keys($values)));
265+
}
266+
267+
public function testGetMultipleThrowsExceptionWhenNotArrayOrTraversable()
268+
{
269+
$this->expectException(InvalidArgumentException::class);
270+
271+
$psrCache = new SimpleCacheAdapter(new ArrayCache());
272+
$psrCache->getMultiple(uniqid('string', true));
273+
}
274+
205275
public function testSetMultipleProxiesToSaveMultiple()
206276
{
207277
$values = [
@@ -268,6 +338,14 @@ public function testSetMultipleWithInvalidTTL($ttl)
268338
$psrCache->setMultiple($values, $ttl);
269339
}
270340

341+
public function testSetMultipleThrowsExceptionWhenNotArrayOrTraversable()
342+
{
343+
$this->expectException(InvalidArgumentException::class);
344+
345+
$psrCache = new SimpleCacheAdapter(new ArrayCache());
346+
$psrCache->setMultiple(uniqid('string', true));
347+
}
348+
271349
public function testDeleteMultipleReturnsTrueWhenAllDeletesSucceed()
272350
{
273351
$keys = [

0 commit comments

Comments
 (0)