1 <?php
2 /**
3 * @package FrameworkOnFramework
4 * @subpackage hal
5 * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
6 * @license GNU General Public License version 2 or later; see LICENSE.txt
7 */
8 defined('FOF_INCLUDED') or die;
9
10 /**
11 * Implementation of the Hypertext Application Language document in PHP. It can
12 * be used to provide hypermedia in a web service context.
13 *
14 * @package FrameworkOnFramework
15 * @since 2.1
16 */
17 class FOFHalDocument
18 {
19 /**
20 * The collection of links of this document
21 *
22 * @var FOFHalLinks
23 */
24 private $_links = null;
25
26 /**
27 * The data (resource state or collection of resource state objects) of the
28 * document.
29 *
30 * @var array
31 */
32 private $_data = null;
33
34 /**
35 * Embedded documents. This is an array of FOFHalDocument instances.
36 *
37 * @var array
38 */
39 private $_embedded = array();
40
41 /**
42 * When $_data is an array we'll output the list of data under this key
43 * (JSON) or tag (XML)
44 *
45 * @var string
46 */
47 private $_dataKey = '_list';
48
49 /**
50 * Public constructor
51 *
52 * @param mixed $data The data of the document (usually, the resource state)
53 */
54 public function __construct($data = null)
55 {
56 $this->_data = $data;
57 $this->_links = new FOFHalLinks;
58 }
59
60 /**
61 * Add a link to the document
62 *
63 * @param string $rel The relation of the link to the document.
64 * See RFC 5988 http://tools.ietf.org/html/rfc5988#section-6.2.2 A document MUST always have
65 * a "self" link.
66 * @param FOFHalLink $link The actual link object
67 * @param boolean $overwrite When false and a link of $rel relation exists, an array of links is created. Otherwise the
68 * existing link is overwriten with the new one
69 *
70 * @see FOFHalLinks::addLink
71 *
72 * @return boolean True if the link was added to the collection
73 */
74 public function addLink($rel, FOFHalLink $link, $overwrite = true)
75 {
76 return $this->_links->addLink($rel, $link, $overwrite);
77 }
78
79 /**
80 * Add links to the document
81 *
82 * @param string $rel The relation of the link to the document. See RFC 5988
83 * @param array $links An array of FOFHalLink objects
84 * @param boolean $overwrite When false and a link of $rel relation exists, an array of
85 * links is created. Otherwise the existing link is overwriten
86 * with the new one
87 *
88 * @see FOFHalLinks::addLinks
89 *
90 * @return boolean
91 */
92 public function addLinks($rel, array $links, $overwrite = true)
93 {
94 return $this->_links->addLinks($rel, $links, $overwrite);
95 }
96
97 /**
98 * Add data to the document
99 *
100 * @param stdClass $data The data to add
101 * @param boolean $overwrite Should I overwrite existing data?
102 *
103 * @return void
104 */
105 public function addData($data, $overwrite = true)
106 {
107 if (is_array($data))
108 {
109 $data = (object) $data;
110 }
111
112 if ($overwrite)
113 {
114 $this->_data = $data;
115 }
116 else
117 {
118 if (!is_array($this->_data))
119 {
120 $this->_data = array($this->_data);
121 }
122
123 $this->_data[] = $data;
124 }
125 }
126
127 /**
128 * Add an embedded document
129 *
130 * @param string $rel The relation of the embedded document to its container document
131 * @param FOFHalDocument $document The document to add
132 * @param boolean $overwrite Should I overwrite existing data with the same relation?
133 *
134 * @return boolean
135 */
136 public function addEmbedded($rel, FOFHalDocument $document, $overwrite = true)
137 {
138 if (!array_key_exists($rel, $this->_embedded) || !$overwrite)
139 {
140 $this->_embedded[$rel] = $document;
141 }
142 elseif (array_key_exists($rel, $this->_embedded) && !$overwrite)
143 {
144 if (!is_array($this->_embedded[$rel]))
145 {
146 $this->_embedded[$rel] = array($this->_embedded[$rel]);
147 }
148
149 $this->_embedded[$rel][] = $document;
150 }
151 else
152 {
153 return false;
154 }
155 }
156
157 /**
158 * Returns the collection of links of this document
159 *
160 * @param string $rel The relation of the links to fetch. Skip to get all links.
161 *
162 * @return array
163 */
164 public function getLinks($rel = null)
165 {
166 return $this->_links->getLinks($rel);
167 }
168
169 /**
170 * Returns the collection of embedded documents
171 *
172 * @param string $rel Optional; the relation to return the embedded documents for
173 *
174 * @return array|FOFHalDocument
175 */
176 public function getEmbedded($rel = null)
177 {
178 if (empty($rel))
179 {
180 return $this->_embedded;
181 }
182 elseif (isset($this->_embedded[$rel]))
183 {
184 return $this->_embedded[$rel];
185 }
186 else
187 {
188 return array();
189 }
190 }
191
192 /**
193 * Return the data attached to this document
194 *
195 * @return array|stdClass
196 */
197 public function getData()
198 {
199 return $this->_data;
200 }
201
202 /**
203 * Instantiate and call a suitable renderer class to render this document
204 * into the specified format.
205 *
206 * @param string $format The format to render the document into, e.g. 'json'
207 *
208 * @return string The rendered document
209 *
210 * @throws RuntimeException If the format is unknown, i.e. there is no suitable renderer
211 */
212 public function render($format = 'json')
213 {
214 $class_name = 'FOFHalRender' . ucfirst($format);
215
216 if (!class_exists($class_name, true))
217 {
218 throw new RuntimeException("Unsupported HAL Document format '$format'. Render aborted.");
219 }
220
221 $renderer = new $class_name($this);
222
223 return $renderer->render(
224 array(
225 'data_key' => $this->_dataKey
226 )
227 );
228 }
229 }
230