From a4894be5529a2b7472865efe31dad6f0081855c2 Mon Sep 17 00:00:00 2001 From: David Strauss Date: Tue, 20 Sep 2016 12:25:53 -0700 Subject: [PATCH] UnserializationException: Throw when failed on get(). --- src/APCuL1.php | 2 +- src/DatabaseL2.php | 6 ++-- src/StaticL2.php | 4 +-- src/UnserializationException.php | 26 +++++++++++++++ tests/LCacheTest.php | 57 +++++++++++++++++++++++++++++--- 5 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 src/UnserializationException.php diff --git a/src/APCuL1.php b/src/APCuL1.php index 543c851..2c1a586 100644 --- a/src/APCuL1.php +++ b/src/APCuL1.php @@ -116,7 +116,7 @@ public function delete($event_id, Address $address) return apcu_clear_cache(); } elseif ($address->isEntireBin()) { $prefix = $this->getLocalKey($address); - $pattern = '/^' . $prefix . '.*/'; + $pattern = '/^' . preg_quote($prefix) . '.*/'; $matching = $this->getIterator($pattern, APC_ITER_KEY); if (!$matching) { // @codeCoverageIgnoreStart diff --git a/src/DatabaseL2.php b/src/DatabaseL2.php index da4833a..d732c86 100644 --- a/src/DatabaseL2.php +++ b/src/DatabaseL2.php @@ -177,11 +177,9 @@ public function getEntry(Address $address) $unserialized_value = @unserialize($last_matching_entry->value); - // If unserialization failed, miss. + // If unserialization failed, raise an exception. if ($unserialized_value === false && $last_matching_entry->value !== serialize(false)) { - // @TODO: Warn or throw an exception. - $this->misses++; - return null; + throw new UnserializationException($address, $last_matching_entry->value); } $last_matching_entry->value = $unserialized_value; diff --git a/src/StaticL2.php b/src/StaticL2.php index 5004504..36597c4 100644 --- a/src/StaticL2.php +++ b/src/StaticL2.php @@ -69,9 +69,7 @@ public function getEntry(Address $address) // If unserialization failed, miss. if ($unserialized_value === false && $last_matching_entry->value !== serialize(false)) { - // @TODO: Warn or throw an exception. - $this->misses++; - return null; + throw new UnserializationException($address, $last_matching_entry->value); } $last_matching_entry->value = $unserialized_value; diff --git a/src/UnserializationException.php b/src/UnserializationException.php new file mode 100644 index 0000000..0e7747e --- /dev/null +++ b/src/UnserializationException.php @@ -0,0 +1,26 @@ +address = $address; + $this->serialized_data = $serialized_data; + parent::__construct('Failed to unserialize on cache get'); + } + + public function __toString() + { + return __CLASS__ . ': Cache bin "' . $this->address->getBin() . '" and key "' . $this->address->getKey() . '"' . PHP_EOL; + } + + public function getSerializedData() + { + return $this->serialized_data; + } +} diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index ec222dd..f014fa0 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -627,7 +627,7 @@ public function testAddressSerialization() $this->assertEquals(strpos($entire_mybin->serialize(), $mybin_mykey->serialize()), 0); } - public function performFailedUnserializationTest($l2) + protected function performFailedUnserializationTest($l2) { $l1 = new StaticL1(); $pool = new Integrated($l1, $l2); @@ -651,11 +651,26 @@ public function performFailedUnserializationTest($l2) $this->assertNull($l1->get($myaddr)); $this->assertEquals(0, $l1->getHits()); $this->assertEquals(1, $l1->getMisses()); + } - $myaddr2 = new Address('mybin', 'mykey2'); - $l2->set('anypool', $myaddr2, $invalid_object, null, [], true); - $this->assertNull($pool->get($myaddr2)); - $this->assertNull($l1->get($myaddr2)); + protected function performCaughtUnserializationOnGetTest($l2) + { + $l1 = new StaticL1(); + $pool = new Integrated($l1, $l2); + $invalid_object = 'O:10:"HelloWorl":0:{}'; + $myaddr = new Address('mybin', 'performCaughtUnserializationOnGetTest'); + $l2->set('anypool', $myaddr, $invalid_object, null, [], true); + try { + $pool->get($myaddr); + $this->assertTrue(false); // Should not reach here. + } catch (UnserializationException $e) { + $this->assertEquals($invalid_object, $e->getSerializedData()); + + // The text of the exception should include the class name, bin, and key. + $this->assertRegExp('/^' . preg_quote('LCache\UnserializationException: Cache') . '/', strval($e)); + $this->assertRegExp('/bin "' . preg_quote($myaddr->getBin()) . '"/', strval($e)); + $this->assertRegExp('/key "' . preg_quote($myaddr->getKey()) . '"/', strval($e)); + } } public function testDatabaseL2FailedUnserialization() @@ -663,12 +678,44 @@ public function testDatabaseL2FailedUnserialization() $this->createSchema(); $l2 = new DatabaseL2($this->dbh); $this->performFailedUnserializationTest($l2); + $this->performCaughtUnserializationOnGetTest($l2); } public function testStaticL2FailedUnserialization() { $l2 = new StaticL2(); $this->performFailedUnserializationTest($l2); + $this->performCaughtUnserializationOnGetTest($l2); + } + + // Callers should expect an UnserializationException. + protected function performFailedUnserializationOnGetTest($l2) + { + $l1 = new StaticL1(); + $pool = new Integrated($l1, $l2); + $invalid_object = 'O:10:"HelloWorl":0:{}'; + $myaddr = new Address('mybin', 'performFailedUnserializationOnGetTest'); + $l2->set('anypool', $myaddr, $invalid_object, null, [], true); + $pool->get($myaddr); + } + + /** + * @expectedException LCache\UnserializationException + */ + public function testDatabaseL2FailedUnserializationOnGet() + { + $this->createSchema(); + $l2 = new DatabaseL2($this->dbh); + $this->performFailedUnserializationOnGetTest($l2); + } + + /** + * @expectedException LCache\UnserializationException + */ + public function testStaticL2FailedUnserializationOnGet() + { + $l2 = new StaticL2(); + $this->performFailedUnserializationOnGetTest($l2); } public function performGarbageCollectionTest($l2)