Response::body()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Response
5
 *
6
 * Handles the HTTP Response for the current execution.
7
 *
8
 * @package core
9
 * @author [email protected]
10
 * @copyright Caffeina srl - 2015 - http://caffeina.it
11
 */
12
13
class Response {
14
    use Module, Events;
15
16
    const TYPE_JSON               = 'application/json',
17
          TYPE_HTML               = 'text/html',
18
          TYPE_TEXT               = 'text/plain',
19
          TYPE_CSS                = 'text/css',
20
          TYPE_XML                = 'text/xml',
21
          TYPE_SVG                = 'image/svg+xml',
22
          TYPE_JS                 = 'application/javascript',
23
          TYPE_BIN                = 'application/octet-stream';
24
25
    protected static $payload     = [],
26
                     $status      = 200,
27
                     $charset     = "utf-8",
28
                     $headers     = ['Content-Type' => ['text/html; charset=utf-8']],
29
                     $buffer      = null,
30
                     $force_dl    = false,
31
                     $link        = null,
32
                     $sent        = false,
33
                     $links       = [];
34
35
36
    public static function charset($charset){
37
        static::$charset = $charset;
38
    }
39
40
    public static function type($mime){
41
        static::header('Content-Type',$mime . (static::$charset ? '; charset='.static::$charset : ''));
42
    }
43
44
    /**
45
     * Force download of Response body
46
     * @param  mixed $data Pass a falsy value to disable download, pass a filename for exporting content or array with raw string data
47
     * @return void
48
     */
49
    public static function download($data){
50
        if (is_array($data)) {
51
            if (isset($data['filename'])) static::$force_dl = $data['filename'];
52
            if (isset($data['charset'])) static::charset($data['charset']);
53
            if (isset($data['mime'])) static::type($data['mime']);
54
            if (isset($data['body'])) static::body($data['body']);
55
        } else static::$force_dl = $data;
56
    }
57
58
    /**
59
     * Start capturing output
60
     */
61
    public static function start(){
62
        static::$buffer = ob_start();
63
    }
64
65
    /**
66
     * Enable CORS HTTP headers.
67
     */
68
    public static function enableCORS($origin='*'){
69
70
        // Allow from any origin
71
        if ($origin = $origin ?:( isset($_SERVER['HTTP_ORIGIN'])
72
                    ? $_SERVER['HTTP_ORIGIN']
73
                    : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '*')
74
        )) {
75
          static::header('Access-Control-Allow-Origin',      $origin);
76
          static::header('Access-Control-Allow-Credentials', 'true');
77
          static::header('Access-Control-Max-Age',           86400);
78
        }
79
80
        // Access-Control headers are received during OPTIONS requests
81
        if (filter_input(INPUT_SERVER,'REQUEST_METHOD') == 'OPTIONS') {
82
            static::clean();
83
84
            if (filter_input(INPUT_SERVER,'HTTP_ACCESS_CONTROL_REQUEST_METHOD')) {
85
              static::header('Access-Control-Allow-Methods',
86
                'GET, POST, PUT, DELETE, OPTIONS, HEAD, CONNECT, PATCH, TRACE');
87
            }
88
            if ($req_h = filter_input(INPUT_SERVER,'HTTP_ACCESS_CONTROL_REQUEST_HEADERS')) {
89
              static::header('Access-Control-Allow-Headers',$req_h);
90
            }
91
92
            self::trigger('cors.preflight');
93
            static::send();
94
            exit;
95
        }
96
    }
97
98
    public static function sent() {
99
        return static::$sent;
100
    }
101
102
    /**
103
     * Finish the output buffer capturing.
104
     * @return string The captured buffer
105
     */
106
    public static function end(){
107
        if (static::$buffer){
108
            static::$payload[] = ob_get_contents();
109
            ob_end_clean();
110
            static::$buffer = null;
111
            return end(static::$payload);
112
        }
113
    }
114
115
    /**
116
     * Check if an response output buffering is active.
117
     * @return boolean
118
     */
119
    public static function isBuffering(){
120
        return static::$buffer;
121
    }
122
123
    /**
124
     * Clear the response body
125
     */
126
    public static function clean(){
127
        static::$payload = [];
128
        static::$headers = [];
129
    }
130
131
    /**
132
     * Append a JSON object to the buffer.
133
     * @param  mixed $payload Data to append to the response buffer
134
     */
135
    public static function json($payload){
136
        static::type(static::TYPE_JSON);
137
        static::$payload[] = json_encode($payload, Options::get('core.response.json_flags',JSON_NUMERIC_CHECK|JSON_BIGINT_AS_STRING));
138
    }
139
140
    /**
141
     * Append a text to the buffer.
142
     * @param  mixed $payload Text to append to the response buffer
143
     */
144
    public static function text(...$args){
145
        static::type(static::TYPE_TEXT);
146
        static::$payload[] = implode('',$args);
147
    }
148
149
    /**
150
     * Append an XML string to the buffer.
151
     * @param  mixed $payload Data to append to the response buffer
0 ignored issues
show
Bug introduced by
There is no parameter named $payload. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
152
     */
153
    public static function xml(...$args){
154
        static::type(static::TYPE_XML);
155
        static::$payload[] = implode('', $args);
156
    }
157
158
    /**
159
     * Append a SVG string to the buffer.
160
     * @param  mixed $payload Data to append to the response buffer
0 ignored issues
show
Bug introduced by
There is no parameter named $payload. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
161
     */
162
    public static function svg(...$args){
163
        static::type(static::TYPE_SVG);
164
        static::$payload[] = implode('', $args);
165
    }
166
167
    /**
168
     * Append an HTML string to the buffer.
169
     * @param  mixed $payload Data to append to the response buffer
0 ignored issues
show
Bug introduced by
There is no parameter named $payload. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
170
     */
171
    public static function html(...$args){
172
        static::type(static::TYPE_HTML);
173
        static::$payload[] = implode('', $args);
174
    }
175
176
    /**
177
     * Append data to the buffer.
178
     *  Rules :
179
     *  - Callables will be called and their results added (recursive)
180
     *  - Views will be rendered
181
     *  - Objects, arrays and bools will be JSON encoded
182
     *  - Strings and numbers will be appendend to the response
183
     *
184
     * @param  mixed $payload Data to append to the response buffer
0 ignored issues
show
Bug introduced by
There is no parameter named $payload. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
185
     */
186
    public static function add(){
187
      foreach(func_get_args() as $data){
188
        switch (true) {
189
          case is_callable($data) :
190
            return static::add($data());
191
          case is_a($data, 'View') :
192
            return static::$payload[] = "$data";
193
          case is_object($data) || is_array($data) || is_bool($data):
194
            return static::json($data);
195
          default:
196
            return static::$payload[] = $data;
197
        }
198
      }
199
    }
200
201
    public static function status($code,$message=''){
202
      static::header('Status',$message?:$code,$code);
203
    }
204
205
    public static function header($name,$value,$code=null){
206
      if (empty(static::$headers[$name])){
207
        static::$headers[$name] = [[$value,$code]];
208
      } else {
209
        static::$headers[$name][] = [$value,$code];
210
      }
211
    }
212
213
    public static function error($code=500,$message='Application Error'){
214
      static::trigger('error',$code,$message);
215
      Event::trigger('core.response.error',$code,$message);
216
      static::status($code,$message);
217
    }
218
219
    public static function body($setBody=null){
220
      if ($setBody) static::$payload = [$setBody];
221
      return Filter::with('core.response.body',
222
        is_array(static::$payload) ? implode('',static::$payload) : static::$payload
223
      );
224
    }
225
226
    public static function headers($setHeaders=null){
227
       if ($setHeaders) static::$headers = $setHeaders;
228
       return static::$headers;
229
    }
230
231
    /**
232
     * Save response as an object, for serialization or cache storage
233
     *
234
     * @method save
235
     *
236
     * @return array Headers and body of the response
237
     */
238
    public static function save(){
239
        return [
240
          'head'  => static::$headers,
241
          'body'  => static::body(),
242
        ];
243
    }
244
245
    /**
246
     * Load response from a saved state
247
     *
248
     * @method load
249
     *
250
     * @param  array $data head/body saved state
251
     */
252
    public static function load($data){
253
      $data = (object)$data;
254
      if (isset($data->head)) static::headers($data->head);
255
      if (isset($data->body)) static::body($data->body);
256
    }
257
258
    public static function send($force = false){
259
      if (!static::$sent || $force) {
260
        static::$sent = true;
261
        static::trigger('send');
262
        Event::trigger('core.response.send');
263
        if (false === headers_sent()) foreach (static::$headers as $name => $family)
264
          foreach ($family as $value_code) {
265
266
            if (is_array($value_code)) {
267
                list($value, $code) = (count($value_code) > 1) ? $value_code : [current($value_code), 200];
268
            } else {
269
                $value = $value_code;
270
                $code  = null;
271
            }
272
273
            switch($value){
274
              case "Status":
275
                if (function_exists('http_response_code')){
276
                  http_response_code($code);
277
                } else {
278
                  header("Status: $code", true, $code);
279
                }
280
              break;
281
              case "Link":
282
                  header("Link: $value", false);
283
              break;
284
              default:
285
                if ($code) {
286
                  header("$name: $value", true, $code);
287
                } else {
288
                  header("$name: $value", true);
289
                }
290
              break;
291
            }
292
        }
293
        if (static::$force_dl) header('Content-Disposition: attachment; filename="'.static::$force_dl.'"');
294
        echo static::body();
295
        static::trigger('sent');
296
      }
297
    }
298
299
300
    /**
301
     * Push resources to client (HTTP/2 spec)
302
     * @param  string/array $links The link(s) to the resources to push.
0 ignored issues
show
Documentation introduced by
The doc-type string/array could not be parsed: Unknown type name "string/array" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
303
     * @return Response     The Route object
304
     */
305
    public static function push($links, $type='text'){
306
      if (is_array($links)){
307
        foreach($links as $_type => $link) {
308
            // Extract URL basename extension (query-safe version)
309
            if (is_numeric($_type)) switch(strtolower(substr(strrchr(strtok(basename($link),'?'),'.'),1))) {
310
                case 'js': $_type = 'script'; break;
311
                case 'css': $_type = 'style'; break;
312
                case 'png': case 'svg': case 'gif': case 'jpg': $_type = 'image'; break;
313
                case 'woff': case 'woff2': case 'ttf': case 'eof': $_type = 'font'; break;
314
                default: $_type = 'text'; break;
315
            }
316
            foreach ((array)$link as $link_val) {
317
              static::header("Link","<$link_val>; rel=preload; as=$_type");
318
            }
319
        }
320
      } else {
321
        static::header("Link","<".((string)$links).">; rel=preload; as=$type");
322
      }
323
    }
324
325
}
326