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: * XML layouts are located in the xml sub directory of `Layouts/`
62: *
63: * @var string
64: */
65: protected $layoutPath = 'xml';
66:
67: /**
68: * XML views are located in the 'xml' sub directory for controllers' views.
69: *
70: * @var string
71: */
72: protected $subDir = 'xml';
73:
74: /**
75: * Response type.
76: *
77: * @var string
78: */
79: protected $_responseType = 'xml';
80:
81: /**
82: * List of special view vars.
83: *
84: * @var array
85: */
86: protected $_specialVars = ['_serialize', '_rootNode', '_xmlOptions'];
87:
88: /**
89: * Serialize view vars.
90: *
91: * ### Special parameters
92: * `_xmlOptions` You can set an array of custom options for Xml::fromArray() this way, e.g.
93: * 'format' as 'attributes' instead of 'tags'.
94: *
95: * @param array|string $serialize The name(s) of the view variable(s) that need(s) to be serialized
96: * @return string The serialized data
97: */
98: protected function _serialize($serialize)
99: {
100: $rootNode = isset($this->viewVars['_rootNode']) ? $this->viewVars['_rootNode'] : 'response';
101:
102: if ($serialize === true) {
103: $serialize = array_diff(
104: array_keys($this->viewVars),
105: $this->_specialVars
106: );
107:
108: if (empty($serialize)) {
109: $serialize = null;
110: } elseif (count($serialize) === 1) {
111: $serialize = current($serialize);
112: }
113: }
114:
115: if (is_array($serialize)) {
116: $data = [$rootNode => []];
117: foreach ($serialize as $alias => $key) {
118: if (is_numeric($alias)) {
119: $alias = $key;
120: }
121: if (array_key_exists($key, $this->viewVars)) {
122: $data[$rootNode][$alias] = $this->viewVars[$key];
123: }
124: }
125: } else {
126: $data = isset($this->viewVars[$serialize]) ? $this->viewVars[$serialize] : null;
127: if (is_array($data) && Hash::numeric(array_keys($data))) {
128: $data = [$rootNode => [$serialize => $data]];
129: }
130: }
131:
132: $options = [];
133: if (isset($this->viewVars['_xmlOptions'])) {
134: $options = $this->viewVars['_xmlOptions'];
135: }
136: if (Configure::read('debug')) {
137: $options['pretty'] = true;
138: }
139:
140: if (isset($options['return']) && strtolower($options['return']) === 'domdocument') {
141: return Xml::fromArray($data, $options)->saveXML();
142: }
143:
144: return Xml::fromArray($data, $options)->asXML();
145: }
146: }
147: