blob: eebc69bd0c50f37feee907a354442808a9f07d58 [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
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 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010011
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020012namespace Carbon\Traits;
13
14use Carbon\Exceptions\InvalidFormatException;
15use ReturnTypeWillChange;
16use Throwable;
17
18/**
19 * Trait Serialization.
20 *
21 * Serialization and JSON stuff.
22 *
23 * Depends on the following properties:
24 *
25 * @property int $year
26 * @property int $month
27 * @property int $daysInMonth
28 * @property int $quarter
29 *
30 * Depends on the following methods:
31 *
32 * @method string|static locale(string $locale = null, string ...$fallbackLocales)
33 * @method string toJSON()
34 */
35trait Serialization
36{
37 use ObjectInitialisation;
38
39 /**
40 * The custom Carbon JSON serializer.
41 *
42 * @var callable|null
43 */
44 protected static $serializer;
45
46 /**
47 * List of key to use for dump/serialization.
48 *
49 * @var string[]
50 */
51 protected $dumpProperties = ['date', 'timezone_type', 'timezone'];
52
53 /**
54 * Locale to dump comes here before serialization.
55 *
56 * @var string|null
57 */
58 protected $dumpLocale;
59
60 /**
61 * Embed date properties to dump in a dedicated variables so it won't overlap native
62 * DateTime ones.
63 *
64 * @var array|null
65 */
66 protected $dumpDateProperties;
67
68 /**
69 * Return a serialized string of the instance.
70 *
71 * @return string
72 */
73 public function serialize()
74 {
75 return serialize($this);
76 }
77
78 /**
79 * Create an instance from a serialized string.
80 *
81 * @param string $value
82 *
83 * @throws InvalidFormatException
84 *
85 * @return static
86 */
87 public static function fromSerialized($value)
88 {
89 $instance = @unserialize((string) $value);
90
91 if (!$instance instanceof static) {
92 throw new InvalidFormatException("Invalid serialized value: $value");
93 }
94
95 return $instance;
96 }
97
98 /**
99 * The __set_state handler.
100 *
101 * @param string|array $dump
102 *
103 * @return static
104 */
105 #[ReturnTypeWillChange]
106 public static function __set_state($dump)
107 {
108 if (\is_string($dump)) {
109 return static::parse($dump);
110 }
111
112 /** @var \DateTimeInterface $date */
113 $date = get_parent_class(static::class) && method_exists(parent::class, '__set_state')
114 ? parent::__set_state((array) $dump)
115 : (object) $dump;
116
117 return static::instance($date);
118 }
119
120 /**
121 * Returns the list of properties to dump on serialize() called on.
122 *
123 * @return array
124 */
125 public function __sleep()
126 {
127 $properties = $this->getSleepProperties();
128
129 if ($this->localTranslator ?? null) {
130 $properties[] = 'dumpLocale';
131 $this->dumpLocale = $this->locale ?? null;
132 }
133
134 return $properties;
135 }
136
137 /**
138 * Set locale if specified on unserialize() called.
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100139 *
140 * @return void
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200141 */
142 #[ReturnTypeWillChange]
143 public function __wakeup()
144 {
145 if (get_parent_class() && method_exists(parent::class, '__wakeup')) {
146 // @codeCoverageIgnoreStart
147 try {
148 parent::__wakeup();
149 } catch (Throwable $exception) {
150 // FatalError occurs when calling msgpack_unpack() in PHP 7.4 or later.
151 ['date' => $date, 'timezone' => $timezone] = $this->dumpDateProperties;
152 parent::__construct($date, unserialize($timezone));
153 }
154 // @codeCoverageIgnoreEnd
155 }
156
157 $this->constructedObjectId = spl_object_hash($this);
158
159 if (isset($this->dumpLocale)) {
160 $this->locale($this->dumpLocale);
161 $this->dumpLocale = null;
162 }
163
164 $this->cleanupDumpProperties();
165 }
166
167 /**
168 * Prepare the object for JSON serialization.
169 *
170 * @return array|string
171 */
172 #[ReturnTypeWillChange]
173 public function jsonSerialize()
174 {
175 $serializer = $this->localSerializer ?? static::$serializer;
176
177 if ($serializer) {
178 return \is_string($serializer)
179 ? $this->rawFormat($serializer)
180 : $serializer($this);
181 }
182
183 return $this->toJSON();
184 }
185
186 /**
187 * @deprecated To avoid conflict between different third-party libraries, static setters should not be used.
188 * You should rather transform Carbon object before the serialization.
189 *
190 * JSON serialize all Carbon instances using the given callback.
191 *
192 * @param callable $callback
193 *
194 * @return void
195 */
196 public static function serializeUsing($callback)
197 {
198 static::$serializer = $callback;
199 }
200
201 /**
202 * Cleanup properties attached to the public scope of DateTime when a dump of the date is requested.
203 * foreach ($date as $_) {}
204 * serializer($date)
205 * var_export($date)
206 * get_object_vars($date)
207 */
208 public function cleanupDumpProperties()
209 {
210 foreach ($this->dumpProperties as $property) {
211 if (isset($this->$property)) {
212 unset($this->$property);
213 }
214 }
215
216 return $this;
217 }
218
219 private function getSleepProperties(): array
220 {
221 $properties = $this->dumpProperties;
222
223 // @codeCoverageIgnoreStart
224 if (!\extension_loaded('msgpack')) {
225 return $properties;
226 }
227
228 if (isset($this->constructedObjectId)) {
229 $this->dumpDateProperties = [
230 'date' => $this->format('Y-m-d H:i:s.u'),
231 'timezone' => serialize($this->timezone ?? null),
232 ];
233
234 $properties[] = 'dumpDateProperties';
235 }
236
237 return $properties;
238 // @codeCoverageIgnoreEnd
239 }
240}