1 <?php
2 /* --------------------------------------------------------------
3 HttpViewController.inc.php 2015-03-12 gm
4 Gambio GmbH
5 http://www.gambio.de
6 Copyright (c) 2015 Gambio GmbH
7 Released under the GNU General Public License (Version 2)
8 [http://www.gnu.org/licenses/gpl-2.0.html]
9 --------------------------------------------------------------
10 */
11
12 MainFactory::load_class('HttpViewControllerInterface');
13
14 /**
15 * Class HttpViewController
16 *
17 * This class contains some helper methods for handling view requests. Be careful
18 * always when outputting raw user data to HTML or when handling POST requests because
19 * insufficient protection will lead to XSS and CSRF vulnerabilities.
20 *
21 * @link http://en.wikipedia.org/wiki/Cross-site_scripting
22 * @link http://en.wikipedia.org/wiki/Cross-site_request_forgery
23 *
24 * @category System
25 * @package Http
26 * @implements HttpViewControllerInterface
27 */
28 class HttpViewController implements HttpViewControllerInterface
29 {
30 /**
31 * @var HttpContextReaderInterface
32 */
33 protected $httpContextReader;
34
35 /**
36 * @var HttpResponseProcessorInterface
37 */
38 protected $httpResponseProcessor;
39
40 /**
41 * @var ContentViewInterface
42 */
43 protected $contentView;
44
45 /**
46 * @var array
47 */
48 protected $queryParametersArray;
49
50 /**
51 * @var array
52 */
53 protected $postDataArray;
54
55 /**
56 * @var AssetCollectionInterface Contain the assets needed to be included in the view HTML.
57 */
58 protected $assets;
59
60
61 /**
62 * @param HttpContextReaderInterface $httpContextReader
63 * @param HttpResponseProcessorInterface $httpResponseProcessor
64 * @param ContentViewInterface $defaultContentView
65 */
66 public function __construct(HttpContextReaderInterface $httpContextReader,
67 HttpResponseProcessorInterface $httpResponseProcessor,
68 ContentViewInterface $defaultContentView)
69 {
70 $this->httpContextReader = $httpContextReader;
71 $this->httpResponseProcessor = $httpResponseProcessor;
72 $this->contentView = $defaultContentView;
73 $this->assets = MainFactory::create('AssetCollection');
74 }
75
76
77 /**
78 * Processes a http response object which is get by invoking an action method.
79 * The action method is determined by the http context reader instance and the current request context.
80 * Re-implement this method in child classes to enable XSS and CSRF protection on demand.
81 *
82 * @param HttpContextInterface $httpContext Http context object which hold the request variables.
83 *
84 * @see HttpResponseProcessorInterface::proceed
85 * @see HttpContextReaderInterface::getActionName
86 *
87 * @throws LogicException When no action method is found by the http context reader.
88 */
89 public function proceed(HttpContextInterface $httpContext)
90 {
91 $this->queryParametersArray = $this->httpContextReader->getQueryParameters($httpContext);
92 $this->postDataArray = $this->httpContextReader->getPostData($httpContext);
93
94 $actionName = $this->httpContextReader->getActionName($httpContext);
95 $response = $this->_callActionMethod($actionName);
96
97 $this->httpResponseProcessor->proceed($response);
98 }
99
100
101 /**
102 * Default action method.
103 * Every controller child class requires at least the default action method, which is invoked when
104 * the ::_getQueryParameterData('do') value is not separated by a trailing slash.
105 *
106 * Every action method have to return an instance which implements the http controller response interface.
107 *
108 * @return \HttpControllerResponseInterface
109 */
110 public function actionDefault()
111 {
112 return new HttpControllerResponse('');
113 }
114
115
116 /**
117 * Invokes an action method by the given action name.
118 *
119 * @param string $actionName Name of action method to call, without 'action'-Suffix.
120 *
121 * @throws LogicException If no action method of the given name exists.
122 * @return HttpControllerResponseInterface Response message.
123 */
124 protected function _callActionMethod($actionName)
125 {
126 if(empty($actionName))
127 {
128 $methodName = 'actionDefault';
129 }
130 else
131 {
132 $methodName = 'action' . $actionName;
133 }
134
135 if(method_exists($this, $methodName) === false)
136 {
137 throw new LogicException('Action method not found for: ' . htmlspecialchars($actionName));
138 }
139
140 $response = call_user_func(array($this, $methodName));
141
142 return $response;
143 }
144
145
146 /**
147 * Renders and returns a template file.
148 *
149 * @param string $templateFile Template file to render.
150 * @param array $contentArray Content array which represent the variables of the template.
151 *
152 * @return string Rendered template.
153 */
154 protected function _render($templateFile, array $contentArray)
155 {
156 $this->contentView->set_content_template($templateFile);
157
158 foreach($contentArray as $contentItemKey => $contentItemValue)
159 {
160 $this->contentView->set_content_data($contentItemKey, $contentItemValue);
161 }
162
163 return $this->contentView->get_html();
164 }
165
166
167 /**
168 * Creates and returns a key value collection which represent the global $_GET array.
169 *
170 * @return KeyValueCollection
171 */
172 protected function _getQueryParametersCollection()
173 {
174 return MainFactory::create('KeyValueCollection', $this->queryParametersArray);
175 }
176
177
178 /**
179 * Creates and returns a key value collection which represent the global $_POST array.
180 *
181 * @return KeyValueCollection
182 */
183 protected function _getPostDataCollection()
184 {
185 return MainFactory::create('KeyValueCollection', $this->postDataArray);
186 }
187
188
189 /**
190 * Returns the expected $_GET value by the given key name.
191 * This method is the object oriented layer for $_GET[$keyName].
192 *
193 * @param string $keyName Expected key of query parameter.
194 *
195 * @return string|null Either the expected value or null, of not found.
196 */
197 protected function _getQueryParameter($keyName)
198 {
199 if(!array_key_exists($keyName, $this->queryParametersArray))
200 {
201 return null;
202 }
203
204 return $this->queryParametersArray[$keyName];
205 }
206
207
208 /**
209 * Returns the expected $_POST value by the given key name.
210 * This method is the object oriented layer for $_POST[$keyName].
211 *
212 * @param string $keyName Expected key of post parameter.
213 *
214 * @return string|null Either the expected value or null, of not found.
215 */
216 protected function _getPostData($keyName)
217 {
218 if(!array_key_exists($keyName, $this->postDataArray))
219 {
220 return null;
221 }
222
223 return $this->postDataArray[$keyName];
224 }
225
226
227 /**
228 * Check if the $_POST['pageToken'] or $_GET['pageToken'] variable is provided and if it's valid.
229 *
230 * Example:
231 * public function proceed(HttpContextInterface $httpContext)
232 * {
233 * parent::proceed($httpContext); // proceed http context from parent class
234 * if($_SERVER['REQUEST_METHOD'] === 'POST')
235 * {
236 * $this->_validatePageToken(); // CSRF Protection
237 * }
238 * }
239 *
240 * @param string $customExceptionMessage (optional) You can specify a custom exception message.
241 *
242 * @throws Exception If the validation fails.
243 */
244 protected function _validatePageToken($customExceptionMessage = null)
245 {
246 $pageToken = $this->_getPostData('pageToken') ? : $this->_getQueryParameter('pageToken');
247
248 if($pageToken === null)
249 {
250 throw new Exception($customExceptionMessage ? : '$_POST["pageToken"] variable was not provided with the POST request.');
251 }
252
253 if(!$_SESSION['coo_page_token']->is_valid($pageToken))
254 {
255 throw new Exception($customExceptionMessage ? : 'Provided $_POST["pageToken"] variable is not valid.');
256 }
257 }
258 }