SerializationTest.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of the Carbon package.
  5. *
  6. * (c) Brian Nesbitt <brian@nesbot.com>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Tests\CarbonImmutable;
  12. use Carbon\CarbonImmutable as Carbon;
  13. use DateTimeImmutable;
  14. use InvalidArgumentException;
  15. use ReflectionClass;
  16. use ReflectionObject;
  17. use ReflectionProperty;
  18. use Tests\AbstractTestCase;
  19. use Throwable;
  20. class SerializationTest extends AbstractTestCase
  21. {
  22. /**
  23. * @var string
  24. */
  25. protected $serialized;
  26. protected function setUp(): void
  27. {
  28. parent::setUp();
  29. $this->serialized = \extension_loaded('msgpack')
  30. ? [
  31. "O:22:\"Carbon\CarbonImmutable\":4:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:15:\"America/Toronto\";s:18:\"dumpDateProperties\";a:2:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:8:\"timezone\";s:96:\"O:21:\"Carbon\CarbonTimeZone\":2:{s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:15:\"America/Toronto\";}\";}}",
  32. "O:22:\"Carbon\CarbonImmutable\":4:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:13:\"timezone_type\";i:3;s:8:\"timezone\";s:15:\"America/Toronto\";s:18:\"dumpDateProperties\";a:2:{s:4:\"date\";s:26:\"2016-02-01 13:20:25.000000\";s:8:\"timezone\";s:23:\"s:15:\"America/Toronto\";\";}}",
  33. ]
  34. : ['O:22:"Carbon\CarbonImmutable":3:{s:4:"date";s:26:"2016-02-01 13:20:25.000000";s:13:"timezone_type";i:3;s:8:"timezone";s:15:"America/Toronto";}'];
  35. }
  36. protected function cleanSerialization(string $serialization): string
  37. {
  38. return preg_replace('/s:\d+:\"[^"]*dumpDateProperties\"/', 's:18:"dumpDateProperties"', $serialization);
  39. }
  40. public function testSerialize()
  41. {
  42. $dt = Carbon::create(2016, 2, 1, 13, 20, 25);
  43. $message = "not in:\n".implode("\n", $this->serialized);
  44. $this->assertContains($this->cleanSerialization($dt->serialize()), $this->serialized, $message);
  45. $this->assertContains($this->cleanSerialization(serialize($dt)), $this->serialized, $message);
  46. }
  47. public function testFromUnserialized()
  48. {
  49. $dt = Carbon::fromSerialized($this->serialized[0]);
  50. $this->assertCarbon($dt, 2016, 2, 1, 13, 20, 25);
  51. $dt = unserialize($this->serialized[0]);
  52. $this->assertCarbon($dt, 2016, 2, 1, 13, 20, 25);
  53. }
  54. public function testSerialization()
  55. {
  56. $this->assertEquals(Carbon::now(), unserialize(serialize(Carbon::now())));
  57. }
  58. public static function dataForTestFromUnserializedWithInvalidValue()
  59. {
  60. return [
  61. [null],
  62. [true],
  63. [false],
  64. [123],
  65. ['foobar'],
  66. ];
  67. }
  68. /**
  69. * @param mixed $value
  70. *
  71. * @dataProvider \Tests\CarbonImmutable\SerializationTest::dataForTestFromUnserializedWithInvalidValue
  72. */
  73. public function testFromUnserializedWithInvalidValue($value)
  74. {
  75. $this->expectExceptionObject(new InvalidArgumentException(
  76. "Invalid serialized value: $value"
  77. ));
  78. Carbon::fromSerialized($value);
  79. }
  80. public function testDateSerializationReflectionCompatibility()
  81. {
  82. $tz = $this->firstValidTimezoneAmong(['America/Los_Angeles', 'US/Pacific'])->getName();
  83. try {
  84. $reflection = (new ReflectionClass(DateTimeImmutable::class))->newInstanceWithoutConstructor();
  85. @$reflection->date = '1990-01-17 10:28:07';
  86. @$reflection->timezone_type = 3;
  87. @$reflection->timezone = $tz;
  88. $date = unserialize(serialize($reflection));
  89. } catch (Throwable $exception) {
  90. $this->markTestSkipped(
  91. "It fails on DateTime so Carbon can't support it, error was:\n".$exception->getMessage()
  92. );
  93. }
  94. $this->assertSame('1990-01-17 10:28:07', $date->format('Y-m-d h:i:s'));
  95. $reflection = (new ReflectionClass(Carbon::class))->newInstanceWithoutConstructor();
  96. @$reflection->date = '1990-01-17 10:28:07';
  97. @$reflection->timezone_type = 3;
  98. @$reflection->timezone = $tz;
  99. $date = unserialize(serialize($reflection));
  100. $this->assertSame('1990-01-17 10:28:07', $date->format('Y-m-d h:i:s'));
  101. $reflection = new ReflectionObject(Carbon::parse('1990-01-17 10:28:07'));
  102. $target = (new ReflectionClass(Carbon::class))->newInstanceWithoutConstructor();
  103. /** @var ReflectionProperty[] $properties */
  104. $properties = [];
  105. foreach ($reflection->getProperties() as $property) {
  106. $property->setAccessible(true);
  107. $properties[$property->getName()] = $property;
  108. }
  109. $setValue = function ($key, $value) use (&$properties, &$target) {
  110. if (isset($properties[$key])) {
  111. $properties[$key]->setValue($target, $value);
  112. return;
  113. }
  114. @$target->$key = $value;
  115. };
  116. $setValue('date', '1990-01-17 10:28:07');
  117. $setValue('timezone_type', 3);
  118. $setValue('timezone', $tz);
  119. $date = unserialize(serialize($target));
  120. $this->assertSame('1990-01-17 10:28:07', $date->format('Y-m-d h:i:s'));
  121. }
  122. public function testMsgPackExtension(): void
  123. {
  124. if (!\extension_loaded('msgpack')) {
  125. $this->markTestSkipped('This test needs msgpack extension to be enabled.');
  126. }
  127. $string = '2018-06-01 21:25:13.321654 Europe/Vilnius';
  128. $date = Carbon::parse('2018-06-01 21:25:13.321654 Europe/Vilnius');
  129. $message = @msgpack_pack($date);
  130. $copy = msgpack_unpack($message);
  131. $this->assertSame($string, $copy->format('Y-m-d H:i:s.u e'));
  132. }
  133. public function testSleepRawMethod(): void
  134. {
  135. $date = Carbon::parse('2018-06-01 21:25:13.321654 Europe/Vilnius');
  136. $expected = ['date', 'timezone_type', 'timezone'];
  137. if (\extension_loaded('msgpack')) {
  138. $expected[] = 'dumpDateProperties';
  139. }
  140. $this->assertSame($expected, $date->__sleep());
  141. $date->locale('fr_FR');
  142. $expected[] = 'dumpLocale';
  143. $this->assertSame($expected, $date->__sleep());
  144. }
  145. public function testSerializeRawMethod(): void
  146. {
  147. $date = Carbon::parse('2018-06-01 21:25:13.321654 Europe/Vilnius');
  148. $expected = [
  149. 'date' => '2018-06-01 21:25:13.321654',
  150. 'timezone_type' => 3,
  151. 'timezone' => 'Europe/Vilnius',
  152. ];
  153. if (\extension_loaded('msgpack')) {
  154. $expected['dumpDateProperties'] = [
  155. 'date' => $date->format('Y-m-d H:i:s.u'),
  156. 'timezone' => serialize($date->timezone),
  157. ];
  158. }
  159. $this->assertSame($expected, $date->__serialize());
  160. $date->locale('lt_LT');
  161. $expected['dumpLocale'] = 'lt_LT';
  162. $this->assertSame($expected, $date->__serialize());
  163. }
  164. public function testWakeupRawMethod(): void
  165. {
  166. $tz = $this->firstValidTimezoneAmong(['America/Los_Angeles', 'US/Pacific'])->getName();
  167. /** @var Carbon $date */
  168. $date = (new ReflectionClass(Carbon::class))->newInstanceWithoutConstructor();
  169. @$date->date = '1990-01-17 10:28:07';
  170. @$date->timezone_type = 3;
  171. @$date->timezone = $tz;
  172. @$date->dumpLocale = 'es';
  173. $date->__wakeup();
  174. $this->assertSame('1990-01-17 10:28:07 '.$tz, $date->format('Y-m-d H:i:s e'));
  175. $this->assertSame('es', $date->locale);
  176. }
  177. public function testUnserializeRawMethod(): void
  178. {
  179. /** @var Carbon $date */
  180. $date = (new ReflectionClass(Carbon::class))->newInstanceWithoutConstructor();
  181. $date->__unserialize([
  182. 'date' => '2018-06-01 21:25:13.321654',
  183. 'timezone_type' => 3,
  184. 'timezone' => 'Europe/Vilnius',
  185. ]);
  186. $this->assertSame('2018-06-01 21:25:13.321654 Europe/Vilnius', $date->format('Y-m-d H:i:s.u e'));
  187. $this->assertSame('en', $date->locale);
  188. /** @var Carbon $date */
  189. $date = (new ReflectionClass(Carbon::class))->newInstanceWithoutConstructor();
  190. $date->__unserialize([
  191. 'date' => '2018-06-01 21:25:13.321654',
  192. 'timezone_type' => 3,
  193. 'timezone' => 'Europe/Vilnius',
  194. 'dumpLocale' => 'lt_LT',
  195. ]);
  196. $this->assertSame('2018-06-01 21:25:13.321654 Europe/Vilnius', $date->format('Y-m-d H:i:s.u e'));
  197. $this->assertSame('lt_LT', $date->locale);
  198. }
  199. }