| 
<?phpnamespace Aws\Api\Serializer;
 
 use Aws\Api\MapShape;
 use Aws\Api\Service;
 use Aws\Api\Shape;
 use Aws\Api\StructureShape;
 use Aws\Api\ListShape;
 use Aws\Api\TimestampShape;
 use XMLWriter;
 
 /**
 * @internal Formats the XML body of a REST-XML services.
 */
 class XmlBody
 {
 /** @var \Aws\Api\Service */
 private $api;
 
 /**
 * @param Service $api API being used to create the XML body.
 */
 public function __construct(Service $api)
 {
 $this->api = $api;
 }
 
 /**
 * Builds the XML body based on an array of arguments.
 *
 * @param Shape $shape Operation being constructed
 * @param array $args  Associative array of arguments
 *
 * @return string
 */
 public function build(Shape $shape, array $args)
 {
 $xml = new XMLWriter();
 $xml->openMemory();
 $xml->startDocument('1.0', 'UTF-8');
 $this->format($shape, $shape['locationName'], $args, $xml);
 $xml->endDocument();
 
 return $xml->outputMemory();
 }
 
 private function startElement(Shape $shape, $name, XMLWriter $xml)
 {
 $xml->startElement($name);
 
 if ($ns = $shape['xmlNamespace']) {
 $xml->writeAttribute(
 isset($ns['prefix']) ? "xmlns:{$ns['prefix']}" : 'xmlns',
 $shape['xmlNamespace']['uri']
 );
 }
 }
 
 private function format(Shape $shape, $name, $value, XMLWriter $xml)
 {
 // Any method mentioned here has a custom serialization handler.
 static $methods = [
 'add_structure' => true,
 'add_list'      => true,
 'add_blob'      => true,
 'add_timestamp' => true,
 'add_boolean'   => true,
 'add_map'       => true,
 'add_string'    => true
 ];
 
 $type = 'add_' . $shape['type'];
 if (isset($methods[$type])) {
 $this->{$type}($shape, $name, $value, $xml);
 } else {
 $this->defaultShape($shape, $name, $value, $xml);
 }
 }
 
 private function defaultShape(Shape $shape, $name, $value, XMLWriter $xml)
 {
 $this->startElement($shape, $name, $xml);
 $xml->writeRaw($value);
 $xml->endElement();
 }
 
 private function add_structure(
 StructureShape $shape,
 $name,
 array $value,
 \XMLWriter $xml
 ) {
 $this->startElement($shape, $name, $xml);
 
 foreach ($this->getStructureMembers($shape, $value) as $k => $definition) {
 $this->format(
 $definition['member'],
 $definition['member']['locationName'] ?: $k,
 $definition['value'],
 $xml
 );
 }
 
 $xml->endElement();
 }
 
 private function getStructureMembers(StructureShape $shape, array $value)
 {
 $members = [];
 
 foreach ($value as $k => $v) {
 if ($v !== null && $shape->hasMember($k)) {
 $definition = [
 'member' => $shape->getMember($k),
 'value'  => $v,
 ];
 
 if ($definition['member']['xmlAttribute']) {
 // array_unshift_associative
 $members = [$k => $definition] + $members;
 } else {
 $members[$k] = $definition;
 }
 }
 }
 
 return $members;
 }
 
 private function add_list(
 ListShape $shape,
 $name,
 array $value,
 XMLWriter $xml
 ) {
 $items = $shape->getMember();
 
 if ($shape['flattened']) {
 $elementName = $name;
 } else {
 $this->startElement($shape, $name, $xml);
 $elementName = $items['locationName'] ?: 'member';
 }
 
 foreach ($value as &$v) {
 $this->format($items, $elementName, $v, $xml);
 }
 
 if (!$shape['flattened']) {
 $xml->endElement();
 }
 }
 
 private function add_map(
 MapShape $shape,
 $name,
 array $value,
 XMLWriter $xml
 ) {
 $xmlEntry = $shape['flattened'] ? $shape['locationName'] : 'entry';
 $xmlKey = $shape->getKey()['locationName'] ?: 'key';
 $xmlValue = $shape->getValue()['locationName'] ?: 'value';
 
 $this->startElement($shape, $name, $xml);
 
 foreach ($value as $key => $v) {
 $this->startElement($shape, $xmlEntry, $xml);
 $this->format($shape->getKey(), $xmlKey, $key, $xml);
 $this->format($shape->getValue(), $xmlValue, $v, $xml);
 $xml->endElement();
 }
 
 $xml->endElement();
 }
 
 private function add_blob(Shape $shape, $name, $value, XMLWriter $xml)
 {
 $this->startElement($shape, $name, $xml);
 $xml->writeRaw(base64_encode($value));
 $xml->endElement();
 }
 
 private function add_timestamp(
 TimestampShape $shape,
 $name,
 $value,
 XMLWriter $xml
 ) {
 $this->startElement($shape, $name, $xml);
 $xml->writeRaw(TimestampShape::format($value, 'iso8601'));
 $xml->endElement();
 }
 
 private function add_boolean(
 Shape $shape,
 $name,
 $value,
 XMLWriter $xml
 ) {
 $this->startElement($shape, $name, $xml);
 $xml->writeRaw($value ? 'true' : 'false');
 $xml->endElement();
 }
 
 private function add_string(
 Shape $shape,
 $name,
 $value,
 XMLWriter $xml
 ) {
 if ($shape['xmlAttribute']) {
 $xml->writeAttribute($shape['locationName'] ?: $name, $value);
 } else {
 $this->defaultShape($shape, $name, $value, $xml);
 }
 }
 }
 
 |