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