Skip to content

Instantly share code, notes, and snippets.

@jeremeamia
Last active August 29, 2015 14:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeremeamia/efc3586e9911fcb0f9c6 to your computer and use it in GitHub Desktop.
Save jeremeamia/efc3586e9911fcb0f9c6 to your computer and use it in GitHub Desktop.
Idea for DynamoDB helper that supports the new document data model.
<?php
namespace Aws\DynamoDb;
/**
* Marshals JSON documents or array representations of JSON documents into the
* parameter structure required by DynamoDB. Also allows for unmarshaling. Does
* not support binary (B) or set (*S) types, since they are not supported
* explicitly in JSON.
*/
class Marshaler
{
/**
* Marshal a JSON document from a string, or JsonSerializable object, to an
* array that is formatted in the proper parameter structure required by
* DynamoDB operations.
*
* @param string|\JsonSerializable $json A valid JSON document.
*
* @return array
* @throws \InvalidArgumentException
*/
public function marshalDocument($json)
{
$document = json_decode($json);
if (!($document instanceof \stdClass)) {
throw new \InvalidArgumentException(
'The JSON document must be valid and be an object at its root.'
);
}
return $this->marshalItem($document);
}
/**
* Marshal a native PHP array of data to a new array that is formatted in
* the proper parameter structure required by DynamoDB operations.
*
* @param array|\stdClass $item An associative array of data.
*
* @return array
*/
public function marshalItem($item)
{
return current($this->marshalValue($item));
}
/**
* Marshal a native PHP value into an array that is formatted in the proper
* parameter structure required by DynamoDB operations.
*
* @param mixed $value A scalar, array, or stdClass value.
*
* @return array
* @throws \UnexpectedValueException
*/
public function marshalValue($value)
{
$type = gettype($value);
if ($type === 'string' && $value !== '') {
$type = 'S';
} elseif ($type === 'integer' || $type === 'double') {
$type = 'N';
$value = (string) $value;
} elseif ($type === 'boolean') {
$type = 'BOOL';
} elseif ($type === 'NULL') {
$type = 'NULL';
$value = true;
} elseif ($type === 'array') {
$type = 'L';
foreach ($value as $k => &$v) {
$v = $this->marshalValue($v);
if (!is_int($k)) {
$type = 'M';
}
}
} elseif ($type === 'object' && $value instanceof \stdClass) {
$type = 'M';
$map = [];
foreach ((array) $value as $k => $v) {
$map[$k] = $this->marshalValue($v);
}
$value = $map;
} else {
throw new \UnexpectedValueException("Unexpected type: {$type}.");
}
return [$type => $value];
}
/**
* Unmarshal a document (item) from a DynamoDB operation result into a JSON
* document string.
*
* @param array $data Item/document from a DynamoDB result.
* @param int $jsonEncodeFlags Flags to use with `json_encode()`.
*
* @return string
*/
public function unmarshalDocument(array $data, $jsonEncodeFlags = 0)
{
return json_encode($this->unmarshalItem($data), $jsonEncodeFlags);
}
/**
* Unmarshal an item from a DynamoDB operation result into a native PHP
* array. If you set $mapAsObject to true, then a stdClass value will be
* returned instead.
*
* @param array $data Item from a DynamoDB result.
* @param bool $mapAsObject Whether maps should be represented as stdClass.
*
* @return array|\stdClass
*/
public function unmarshalItem(array $data, $mapAsObject = false)
{
return $this->unmarshalValue(['M' => $data], $mapAsObject);
}
/**
* Unmarshal a value from a DynamoDB operation result into a native PHP
* value. Will return a scalar, array, or (if you set $mapAsObject to true)
* stdClass value.
*
* @param array $value Value from a DynamoDB result.
* @param bool $mapAsObject Whether maps should be represented as stdClass.
*
* @return mixed
* @throws \UnexpectedValueException
*/
public function unmarshalValue(array $value, $mapAsObject = false)
{
list($type, $value) = each($value);
switch ($type) {
case 'S':
case 'SS':
case 'B':
case 'BS':
case 'BOOL':
return $value;
case 'NULL':
return null;
case 'N':
// Use type coercion to unmarshal numbers to int/float.
return $value + 0;
case 'NS':
foreach ($value as &$v) {
$v += 0;
}
return $value;
case 'M':
if ($mapAsObject) {
$data = new \stdClass;
foreach ($value as $k => $v) {
$data->$k = $this->unmarshalValue($v, $mapAsObject);
}
return $data;
}
// Else, unmarshal M the same way as L.
case 'L':
foreach ($value as &$v) {
$v = $this->unmarshalValue($v, $mapAsObject);
}
return $value;
}
throw new \UnexpectedValueException("Unexpected type: {$type}.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment