Skip to content

Commit 2f747f9

Browse files
committed
ASV: re-validate unserialized value
1 parent c7497a3 commit 2f747f9

File tree

3 files changed

+64
-4
lines changed

3 files changed

+64
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
v2.2.0 (2019-11)
22

3-
* Test serialization/unserialization
3+
* Improved unserialization
4+
* Re-validate serialized value
5+
* Tests
46

57
v2.1.0 (2019-11-13)
68

src/AbstractSerializableValue.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,33 @@ public function jsonSerialize()
3535
return $this->value();
3636
}
3737

38+
/**
39+
* This method ensures that unserialized instances are still valid.
40+
*
41+
* Serialization may be stored in databases for a long time;
42+
* if the class definition changes in between,
43+
* it's possible that a formerly-valid serialization
44+
* contains a value which is no longer considered valid.
45+
* That's why this method re-applies the {@see isValid} check.
46+
*
47+
* @internal
48+
*/
49+
public function __wakeup()
50+
{
51+
/*
52+
* The serialization contained both $value and $isSet
53+
* and there's nothing left to assign.
54+
* But we'll still run the validation
55+
* just in case the serialized value is no longer considered valid.
56+
*/
57+
58+
$storedValue = $this->value();
59+
if (!static::isValid($storedValue)) {
60+
$storedValue = (is_string($storedValue) || is_int($storedValue) || is_float($storedValue))
61+
? "'{$storedValue}'"
62+
: gettype($storedValue);
63+
throw new InvalidArgumentException("not a valid serialized " . static::class . ": {$storedValue}");
64+
}
65+
}
66+
3867
}

test/15-SerializableValueTest.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace mle86\Value\Tests;
44

55
use mle86\Value\AbstractSerializableValue;
6+
use mle86\Value\InvalidArgumentException;
67
use mle86\Value\Tests\Helpers\TestSWrapper6;
78
use mle86\Value\Value;
89
use PHPUnit\Framework\TestCase;
@@ -14,9 +15,10 @@
1415
class SerializableValueTest extends TestCase
1516
{
1617

17-
const VALID_INPUT = "61234";
18-
const INVALID_INPUT = "61234 ";
19-
const VALID_INPUT2 = "69999";
18+
const VALID_INPUT = "61234";
19+
const INVALID_INPUT = "61234 ";
20+
const VALID_INPUT2 = "69999";
21+
const INVALID_INPUT2 = "79990";
2022

2123

2224
public function testClassExists()
@@ -118,4 +120,31 @@ public function testPhpUnserialize(string $serialization, AbstractSerializableVa
118120
"Original object doesn't equal an unserialized object anymore!");
119121
}
120122

123+
/**
124+
* @depends testPhpSerialize
125+
* @depends testPhpUnserialize
126+
*/
127+
public function testSerializedValueValidity()
128+
{
129+
$validInput = self::VALID_INPUT2;
130+
$invalidInput = self::INVALID_INPUT2;
131+
if (strlen($validInput) !== strlen($invalidInput)) {
132+
// We just want to replace the stored string without breaking the serialization format.
133+
// That only works if the replacement string is of the same length
134+
// because the serialization contains a length indicator.
135+
$this->markTestSkipped("Cannot test invalid serialization; strings are not of same length");
136+
}
137+
138+
$validSerialization = serialize(new TestSWrapper6($validInput));
139+
140+
$reValidInput = '/\b' . preg_quote($validInput, '/') . '\b/';
141+
$invalidSerialization = preg_replace($reValidInput, $invalidInput, $validSerialization, -1, $nReplaced);
142+
if ($nReplaced !== 1) {
143+
$this->markTestSkipped("Cannot test invalid serialization; manipulation of serialization string failed");
144+
}
145+
146+
$this->expectException(InvalidArgumentException::class); // !
147+
unserialize($invalidSerialization);
148+
}
149+
121150
}

0 commit comments

Comments
 (0)