3 // Copyright (c) 2011 Blue Static
5 // This program is free software: you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or any later version.
9 // This program is distributed in the hope that it will be useful, but WITHOUT
10 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 // You should have received a copy of the GNU General Public License along with
15 // this program. If not, see <http://www.gnu.org/licenses/>.
17 namespace hoplite\http
;
19 require_once HOPLITE_ROOT
. '/base/weak_interface.php';
20 require_once HOPLITE_ROOT
. '/http/response_code.php';
21 require_once HOPLITE_ROOT
. '/views/template_loader.php';
24 The OutputFilter is executed after all Actions have been processed. The
25 primary function is to generate the actual HTTP response body from the model
26 data contained within the http\Response. Depending on how the Request was
27 sent, this class will encode the output properly and perform any necessary
28 processing (e.g. templates).
32 /*! @var RootController */
35 /*! @var WeakInterface<OutputFilterDelegate> */
38 /*! @const The key in Response#context that indicates which type of output to
39 produce, regardless of the request type.
41 const RESPONSE_TYPE
= 'response_type';
43 /*! @const The key in a Response#context that is set by the ::FilterOutput. It
44 is derived from {@see RESPONSE_TYPE} and controls the actual output
47 const OUTPUT_FILTER_TYPE
= '_output_filter_type';
49 /*! @const A key in Response#context to render a template with the
50 Response#data when creating a HTML body.
52 const RENDER_TEMPLATE
= 'template';
55 Constructor that takes a reference to the RootController.
57 public function __construct(RootController
$controller)
59 $this->controller
= $controller;
60 $this->delegate
= new \hoplite
\base\
WeakInterface('hoplite\http\OutputFilterDelegate');
63 /*! Accessor for the RootController. */
64 public function controller() { return $this->controller
; }
66 /*! Accessors for the delegate. */
67 public function set_delegate($delegate)
69 $this->delegate
->Bind($delegate);
71 public function delegate() { return $this->delegate
->Get(); }
73 /*! @brief Main entry point for output filtering
74 This is called from the RootController to begin processing the output and
75 generating the response.
77 public function FilterOutput(Request
$request, Response
$response)
79 $response->context
[self
::OUTPUT_FILTER_TYPE
] = $this->_GetResponseType($request, $response);
81 // If there was an error during the processing of an action, allow hooking
83 if ($response->response_code
!= ResponseCode
::OK
)
84 if ($this->delegate
->OverrideOutputFiltering($request, $response))
87 // If there's already raw data for the body, just output that. Otherwise,
88 // construct the body based on how the Request was received and any other
89 // information in the response.
91 $this->_CreateBodyForResponse($request, $response);
93 // Now just output the response.
94 header("Status: {$response->response_code}", true
, $response->response_code
);
95 foreach ($response->headers
as $header => $value)
96 header("$header: $value");
97 print $response->body
;
101 Fills out the Response#data field. This could be an evaluated HTML template,
102 a JSON payload, XML, or any other type of response for the client.
104 private function _CreateBodyForResponse(Request
$request,
107 $type = $response->context
[self
::OUTPUT_FILTER_TYPE
];
108 if ($type == 'json') {
109 $response->headers
['Content-Type'] = 'application/json';
110 $response->body
= json_encode($response->data
, JSON_NUMERIC_CHECK
);
111 } else if ($type == 'xml') {
112 $response->headers
['Content-Type'] = 'application/xml';
113 $response->body
= $this->_EncodeXML($response->data
);
114 } else if ($type == 'html') {
115 $response->headers
['Content-Type'] = 'text/html';
116 if (isset($response->context
[self
::RENDER_TEMPLATE
])) {
117 $template = \hoplite
\views\TemplateLoader
::Fetch($response->context
[self
::RENDER_TEMPLATE
]);
118 $response->body
= $template->Render($response->data
);
124 Determines based on the Request what format the response should be in.
126 private function _GetResponseType(Request
$request, Response
$response)
128 // Check if an Action specified an overriding response type.
129 if (isset($response->context
[self
::RESPONSE_TYPE
]))
130 return $response->context
[self
::RESPONSE_TYPE
];
132 // See if the HTTP request contains the desired output format.
133 if (isset($request->data
['format'])) {
134 if ($request->data
['format'] == 'xml')
136 else if ($request->data
['format'] == 'json')
140 // If the request didn't specify a type, try and figure it out using
143 // If this was from an XHR, assume JSON.
144 if (isset($request->data
['_SERVER']['HTTP_X_REQUESTED_WITH']))
147 // If no type has been determined, just assume HTML.
152 Creates an XML tree from an array. Equivalent to json_encode.
154 protected function _EncodeXML($data)
156 $response = new \
SimpleXMLElement('<response/>');
158 $writer = function($elm, $parent) use (&$writer) {
159 foreach ($elm as $key => $value) {
160 if (is_scalar($value)) {
161 $parent->AddChild($key, $value);
163 $new_parent = $parent->AddChild($key);
164 $writer($value, $new_parent);
169 $writer($data, $response);
170 return $response->AsXML();
175 Delegate interface for the OutputFilter. Called via a WeakInterface, and all
176 methods are optional.
178 interface OutputFilterDelegate
181 The delegate can abort output filtering of the request and execute custom
182 logic by returning TRUE from this function.
184 If the request did not generate an 200 response code, the filter gives the
185 client an opportunity to override the normal output control flow and perform
186 some other task. If you want the control flow to continue executing as
187 normal, return FALSE; otherwise, return TRUE to exit from ::FilterOutput().
189 @return bool TRUE if the OutputFilter should stop processing the response.
190 FALSE for default output filtering behavior.
192 public function OverrideOutputFiltering(Request
$request, Response
$response);