1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 2.1.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\View;
16:
17: use Cake\Core\Configure;
18: use Cake\Utility\Hash;
19: use Cake\Utility\Xml;
20:
21: /**
22: * A view class that is used for creating XML responses.
23: *
24: * By setting the '_serialize' key in your controller, you can specify a view variable
25: * that should be serialized to XML and used as the response for the request.
26: * This allows you to omit views + layouts, if your just need to emit a single view
27: * variable as the XML response.
28: *
29: * In your controller, you could do the following:
30: *
31: * ```
32: * $this->set(['posts' => $posts, '_serialize' => true]);
33: * ```
34: *
35: * When the view is rendered, the `$posts` view variable will be serialized
36: * into XML.
37: *
38: * **Note** The view variable you specify must be compatible with Xml::fromArray().
39: *
40: * You can also define `'_serialize'` as an array. This will create an additional
41: * top level element named `<response>` containing all the named view variables:
42: *
43: * ```
44: * $this->set(compact('posts', 'users', 'stuff'));
45: * $this->set('_serialize', true);
46: * ```
47: *
48: * The above would generate a XML object that looks like:
49: *
50: * `<response><posts>...</posts><users>...</users></response>`
51: *
52: * You can also set `'_serialize'` to a string or array to serialize only the
53: * specified view variables.
54: *
55: * If you don't use the `_serialize` key, you will need a view. You can use extended
56: * views to provide layout like functionality.
57: */
58: class XmlView extends SerializedView
59: {
60:
61: /**
62: * XML layouts are located in the xml sub directory of `Layouts/`
63: *
64: * @var string
65: */
66: protected $layoutPath = 'xml';
67:
68: /**
69: * XML views are located in the 'xml' sub directory for controllers' views.
70: *
71: * @var string
72: */
73: protected $subDir = 'xml';
74:
75: /**
76: * Response type.
77: *
78: * @var string
79: */
80: protected $_responseType = 'xml';
81:
82: /**
83: * List of special view vars.
84: *
85: * @var array
86: */
87: protected $_specialVars = ['_serialize', '_rootNode', '_xmlOptions'];
88:
89: /**
90: * Serialize view vars.
91: *
92: * ### Special parameters
93: * `_xmlOptions` You can set an array of custom options for Xml::fromArray() this way, e.g.
94: * 'format' as 'attributes' instead of 'tags'.
95: *
96: * @param array|string $serialize The name(s) of the view variable(s) that need(s) to be serialized
97: * @return string The serialized data
98: */
99: protected function _serialize($serialize)
100: {
101: $rootNode = isset($this->viewVars['_rootNode']) ? $this->viewVars['_rootNode'] : 'response';
102:
103: if ($serialize === true) {
104: $serialize = array_diff(
105: array_keys($this->viewVars),
106: $this->_specialVars
107: );
108:
109: if (empty($serialize)) {
110: $serialize = null;
111: } elseif (count($serialize) === 1) {
112: $serialize = current($serialize);
113: }
114: }
115:
116: if (is_array($serialize)) {
117: $data = [$rootNode => []];
118: foreach ($serialize as $alias => $key) {
119: if (is_numeric($alias)) {
120: $alias = $key;
121: }
122: if (array_key_exists($key, $this->viewVars)) {
123: $data[$rootNode][$alias] = $this->viewVars[$key];
124: }
125: }
126: } else {
127: $data = isset($this->viewVars[$serialize]) ? $this->viewVars[$serialize] : null;
128: if (is_array($data) && Hash::numeric(array_keys($data))) {
129: $data = [$rootNode => [$serialize => $data]];
130: }
131: }
132:
133: $options = [];
134: if (isset($this->viewVars['_xmlOptions'])) {
135: $options = $this->viewVars['_xmlOptions'];
136: }
137: if (Configure::read('debug')) {
138: $options['pretty'] = true;
139: }
140:
141: if (isset($options['return']) && strtolower($options['return']) === 'domdocument') {
142: return Xml::fromArray($data, $options)->saveXML();
143: }
144:
145: return Xml::fromArray($data, $options)->asXML();
146: }
147: }
148: