<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\web;

use DOMDocument;
use DOMElement;
use DOMText;
use yii\base\Arrayable;
use yii\base\Component;
use yii\helpers\StringHelper;

/**
 * XmlResponseFormatter formats the given data into an XML response content.
 *
 * It is used by [[Response]] to format response data.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class XmlResponseFormatter extends Component implements ResponseFormatterInterface
{
	/**
	 * @var string the Content-Type header for the response
	 */
	public $contentType = 'application/xml';
	/**
	 * @var string the XML version
	 */
	public $version = '1.0';
	/**
	 * @var string the XML encoding. If not set, it will use the value of [[Response::charset]].
	 */
	public $encoding;
	/**
	 * @var string the name of the root element.
	 */
	public $rootTag = 'response';
	/**
	 * @var string the name of the elements that represent the array elements with numeric keys.
	 */
	public $itemTag = 'item';

	/**
	 * Formats the specified response.
	 * @param Response $response the response to be formatted.
	 */
	public function format($response)
	{
		$response->getHeaders()->set('Content-Type', $this->contentType);
		$dom = new DOMDocument($this->version, $this->encoding === null ? $response->charset : $this->encoding);
		$root = new DOMElement($this->rootTag);
		$dom->appendChild($root);
		$this->buildXml($root, $response->data);
		$response->content = $dom->saveXML();
	}

	/**
	 * @param DOMElement $element
	 * @param mixed $data
	 */
	protected function buildXml($element, $data)
	{
		if (is_object($data)) {
			$child = new DOMElement(StringHelper::basename(get_class($data)));
			$element->appendChild($child);
			if ($data instanceof Arrayable) {
				$this->buildXml($child, $data->toArray());
			} else {
				$array = [];
				foreach ($data as $name => $value) {
					$array[$name] = $value;
				}
				$this->buildXml($child, $array);
			}
		} elseif (is_array($data)) {
			foreach ($data as $name => $value) {
				if (is_int($name) && is_object($value)) {
					$this->buildXml($element, $value);
				} elseif (is_array($value) || is_object($value)) {
					$child = new DOMElement(is_int($name) ? $this->itemTag : $name);
					$element->appendChild($child);
					$this->buildXml($child, $value);
				} else {
					$child = new DOMElement(is_int($name) ? $this->itemTag : $name);
					$element->appendChild($child);
					$child->appendChild(new DOMText((string)$value));
				}
			}
		} else {
			$element->appendChild(new DOMText((string)$data));
		}
	}
}
