PluginProbe ʕ •ᴥ•ʔ
Transferito: WP Migration / trunk
Transferito: WP Migration vtrunk
trunk 11.4.0 12.0.0 13.1.0 14.0.0 14.0.11 14.0.7 14.1.0 14.1.1 14.1.2 14.1.3 14.1.4
transferito / vendor / aws / aws-sdk-php / src / DynamoDb / Marshaler.php
transferito / vendor / aws / aws-sdk-php / src / DynamoDb Last commit date
Exception 11 months ago BinaryValue.php 11 months ago DynamoDbClient.php 11 months ago LockingSessionConnection.php 11 months ago Marshaler.php 11 months ago NumberValue.php 11 months ago SessionConnectionConfigTrait.php 11 months ago SessionConnectionInterface.php 11 months ago SessionHandler.php 11 months ago SetValue.php 11 months ago StandardSessionConnection.php 11 months ago WriteRequestBatch.php 11 months ago
Marshaler.php
321 lines
1 <?php
2 namespace Aws\DynamoDb;
3
4 use Psr\Http\Message\StreamInterface;
5
6 /**
7 * Marshals and unmarshals JSON documents and PHP arrays into DynamoDB items.
8 */
9 class Marshaler
10 {
11 /** @var array Default options to merge into provided options. */
12 private static $defaultOptions = [
13 'ignore_invalid' => false,
14 'nullify_invalid' => false,
15 'wrap_numbers' => false,
16 ];
17
18 /** @var array Marshaler options. */
19 private $options;
20
21 /**
22 * Instantiates a DynamoDB Marshaler.
23 *
24 * The following options are valid.
25 *
26 * - ignore_invalid: (bool) Set to `true` if invalid values should be
27 * ignored (i.e., not included) during marshaling.
28 * - nullify_invalid: (bool) Set to `true` if invalid values should be set
29 * to null.
30 * - wrap_numbers: (bool) Set to `true` to wrap numbers with `NumberValue`
31 * objects during unmarshaling to preserve the precision.
32 *
33 * @param array $options Marshaler options
34 */
35 public function __construct(array $options = [])
36 {
37 $this->options = $options + self::$defaultOptions;
38 }
39
40 /**
41 * Creates a special object to represent a DynamoDB binary (B) value.
42 *
43 * This helps disambiguate binary values from string (S) values.
44 *
45 * @param mixed $value A binary value compatible with Guzzle streams.
46 *
47 * @return BinaryValue
48 * @see GuzzleHttp\Stream\Stream::factory
49 */
50 public function binary($value)
51 {
52 return new BinaryValue($value);
53 }
54
55 /**
56 * Creates a special object to represent a DynamoDB number (N) value.
57 *
58 * This helps maintain the precision of large integer/float in PHP.
59 *
60 * @param string|int|float $value A number value.
61 *
62 * @return NumberValue
63 */
64 public function number($value)
65 {
66 return new NumberValue($value);
67 }
68
69 /**
70 * Creates a special object to represent a DynamoDB set (SS/NS/BS) value.
71 *
72 * This helps disambiguate set values from list (L) values.
73 *
74 * @param array $values The values of the set.
75 *
76 * @return SetValue
77 *
78 */
79 public function set(array $values)
80 {
81 return new SetValue($values);
82 }
83
84 /**
85 * Marshal a JSON document from a string to a DynamoDB item.
86 *
87 * The result is an array formatted in the proper parameter structure
88 * required by the DynamoDB API for items.
89 *
90 * @param string $json A valid JSON document.
91 *
92 * @return array Item formatted for DynamoDB.
93 * @throws \InvalidArgumentException if the JSON is invalid.
94 */
95 public function marshalJson($json)
96 {
97 $data = json_decode($json);
98 if (!($data instanceof \stdClass)) {
99 throw new \InvalidArgumentException(
100 'The JSON document must be valid and be an object at its root.'
101 );
102 }
103
104 return current($this->marshalValue($data));
105 }
106
107 /**
108 * Marshal a native PHP array of data to a DynamoDB item.
109 *
110 * The result is an array formatted in the proper parameter structure
111 * required by the DynamoDB API for items.
112 *
113 * @param array|\stdClass $item An associative array of data.
114 *
115 * @return array Item formatted for DynamoDB.
116 */
117 public function marshalItem($item)
118 {
119 return current($this->marshalValue($item));
120 }
121
122 /**
123 * Marshal a native PHP value into a DynamoDB attribute value.
124 *
125 * The result is an associative array that is formatted in the proper
126 * `[TYPE => VALUE]` parameter structure required by the DynamoDB API.
127 *
128 * @param mixed $value A scalar, array, or `stdClass` value.
129 *
130 * @return array Attribute formatted for DynamoDB.
131 * @throws \UnexpectedValueException if the value cannot be marshaled.
132 */
133 public function marshalValue($value)
134 {
135 $type = gettype($value);
136
137 // Handle string values.
138 if ($type === 'string') {
139 return ['S' => $value];
140 }
141
142 // Handle number values.
143 if ($type === 'integer'
144 || $type === 'double'
145 || $value instanceof NumberValue
146 ) {
147 return ['N' => (string) $value];
148 }
149
150 // Handle boolean values.
151 if ($type === 'boolean') {
152 return ['BOOL' => $value];
153 }
154
155 // Handle null values.
156 if ($type === 'NULL') {
157 return ['NULL' => true];
158 }
159
160 // Handle set values.
161 if ($value instanceof SetValue) {
162 if (count($value) === 0) {
163 return $this->handleInvalid('empty sets are invalid');
164 }
165 $previousType = null;
166 $data = [];
167 foreach ($value as $v) {
168 $marshaled = $this->marshalValue($v);
169 $setType = key($marshaled);
170 if (!$previousType) {
171 $previousType = $setType;
172 } elseif ($setType !== $previousType) {
173 return $this->handleInvalid('sets must be uniform in type');
174 }
175 $data[] = current($marshaled);
176 }
177
178 return [$previousType . 'S' => array_values(array_unique($data))];
179 }
180
181 // Handle list and map values.
182 $dbType = 'L';
183 if ($value instanceof \stdClass) {
184 $type = 'array';
185 $dbType = 'M';
186 }
187 if ($type === 'array' || $value instanceof \Traversable) {
188 $data = [];
189 $index = 0;
190 foreach ($value as $k => $v) {
191 if ($v = $this->marshalValue($v)) {
192 $data[$k] = $v;
193 if ($dbType === 'L' && (!is_int($k) || $k != $index++)) {
194 $dbType = 'M';
195 }
196 }
197 }
198 return [$dbType => $data];
199 }
200
201 // Handle binary values.
202 if (is_resource($value) || $value instanceof StreamInterface) {
203 $value = $this->binary($value);
204 }
205 if ($value instanceof BinaryValue) {
206 return ['B' => (string) $value];
207 }
208
209 // Handle invalid values.
210 return $this->handleInvalid('encountered unexpected value');
211 }
212
213 /**
214 * Unmarshal a document (item) from a DynamoDB operation result into a JSON
215 * document string.
216 *
217 * @param array $data Item/document from a DynamoDB result.
218 * @param int $jsonEncodeFlags Flags to use with `json_encode()`.
219 *
220 * @return string
221 */
222 public function unmarshalJson(array $data, $jsonEncodeFlags = 0)
223 {
224 return json_encode(
225 $this->unmarshalValue(['M' => $data], true),
226 $jsonEncodeFlags
227 );
228 }
229
230 /**
231 * Unmarshal an item from a DynamoDB operation result into a native PHP
232 * array. If you set $mapAsObject to true, then a stdClass value will be
233 * returned instead.
234 *
235 * @param array $data Item from a DynamoDB result.
236 * @param bool $mapAsObject Whether maps should be represented as stdClass.
237 *
238 * @return array|\stdClass
239 */
240 public function unmarshalItem(array $data, $mapAsObject = false)
241 {
242 return $this->unmarshalValue(['M' => $data], $mapAsObject);
243 }
244
245 /**
246 * Unmarshal a value from a DynamoDB operation result into a native PHP
247 * value. Will return a scalar, array, or (if you set $mapAsObject to true)
248 * stdClass value.
249 *
250 * @param array $value Value from a DynamoDB result.
251 * @param bool $mapAsObject Whether maps should be represented as stdClass.
252 *
253 * @return mixed
254 * @throws \UnexpectedValueException
255 */
256 public function unmarshalValue(array $value, $mapAsObject = false)
257 {
258 $type = key($value);
259 $value = $value[$type];
260 switch ($type) {
261 case 'S':
262 case 'BOOL':
263 return $value;
264 case 'NULL':
265 return null;
266 case 'N':
267 if ($this->options['wrap_numbers']) {
268 return new NumberValue($value);
269 }
270
271 // Use type coercion to unmarshal numbers to int/float.
272 return $value + 0;
273 case 'M':
274 if ($mapAsObject) {
275 $data = new \stdClass;
276 foreach ($value as $k => $v) {
277 $data->$k = $this->unmarshalValue($v, $mapAsObject);
278 }
279 return $data;
280 }
281 // NOBREAK: Unmarshal M the same way as L, for arrays.
282 case 'L':
283 foreach ($value as $k => $v) {
284 $value[$k] = $this->unmarshalValue($v, $mapAsObject);
285 }
286 return $value;
287 case 'B':
288 return new BinaryValue($value);
289 case 'SS':
290 case 'NS':
291 case 'BS':
292 foreach ($value as $k => $v) {
293 $value[$k] = $this->unmarshalValue([$type[0] => $v]);
294 }
295 return new SetValue($value);
296 }
297
298 throw new \UnexpectedValueException("Unexpected type: {$type}.");
299 }
300
301 /**
302 * Handle invalid value based on marshaler configuration.
303 *
304 * @param string $message Error message
305 *
306 * @return array|null
307 */
308 private function handleInvalid($message)
309 {
310 if ($this->options['ignore_invalid']) {
311 return null;
312 }
313
314 if ($this->options['nullify_invalid']) {
315 return ['NULL' => true];
316 }
317
318 throw new \UnexpectedValueException("Marshaling error: {$message}.");
319 }
320 }
321