Completed
Push — master ( 13968e...09f82b )
by Stefano
02:42
created

Response::end()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 0
dl 0
loc 8
rs 9.4285
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 {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
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     = [],
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
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  string/bool $filename Pass a falsy value to disable download or pass a filename for exporting content
0 ignored issues
show
Documentation introduced by
The doc-type string/bool could not be parsed: Unknown type name "string/bool" 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...
47
     * @return [type]        [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" 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...
48
     */
49
    public static function download($filename){
50
        static::$force_dl = $filename;
51
    }
52
53
    /**
54
     * Start capturing output
55
     */
56
    public static function start(){
57
        static::$buffer = ob_start();
58
    }
59
60
    /**
61
     * Enable CORS HTTP headers.
62
     */
63
    public static function enableCORS($origin='*'){
64
65
        // Allow from any origin
66
        if ($origin = $origin ?:( isset($_SERVER['HTTP_ORIGIN'])
67
                    ? $_SERVER['HTTP_ORIGIN']
68
                    : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '*')
69
        )) {
70
          static::header('Access-Control-Allow-Origin',      $origin);
71
          static::header('Access-Control-Allow-Credentials', 'true');
72
          static::header('Access-Control-Max-Age',           86400);
73
        }
74
75
        // Access-Control headers are received during OPTIONS requests
76
        if (filter_input(INPUT_SERVER,'REQUEST_METHOD') == 'OPTIONS') {
77
            static::clean();
78
79
            if (filter_input(INPUT_SERVER,'HTTP_ACCESS_CONTROL_REQUEST_METHOD')) {
80
              static::header('Access-Control-Allow-Methods',
81
                'GET, POST, PUT, DELETE, OPTIONS, HEAD, CONNECT, PATCH, TRACE');
82
            }
83
            if ($req_h = filter_input(INPUT_SERVER,'HTTP_ACCESS_CONTROL_REQUEST_HEADERS')) {
84
              static::header('Access-Control-Allow-Headers',$req_h);
85
            }
86
87
            self::trigger('cors.preflight');
88
            static::send();
89
            exit;
90
        }
91
    }
92
93
    public static function sent() {
94
        return static::$sent;
95
    }
96
97
    /**
98
     * Finish the output buffer capturing.
99
     * @return string The captured buffer
100
     */
101
    public static function end(){
102
        if (static::$buffer){
103
            static::$payload[] = ob_get_contents();
104
            ob_end_clean();
105
            static::$buffer = null;
106
            return end(static::$payload);
107
        }
108
    }
109
110
    /**
111
     * Check if an response output buffering is active.
112
     * @return boolean
113
     */
114
    public static function isBuffering(){
115
        return static::$buffer;
116
    }
117
118
    /**
119
     * Clear the response body
120
     */
121
    public static function clean(){
122
        static::$payload = [];
123
        static::$headers = [];
124
    }
125
126
    /**
127
     * Append a JSON object to the buffer.
128
     * @param  mixed $payload Data to append to the response buffer
129
     */
130
    public static function json($payload){
131
        static::type(static::TYPE_JSON);
132
        static::$payload[] = json_encode($payload, Options::get('core.response.json_flags',JSON_NUMERIC_CHECK));
133
    }
134
135
    /**
136
     * Append a text to the buffer.
137
     * @param  mixed $payload Text to append to the response buffer
138
     */
139
    public static function text(...$args){
140
        static::type(static::TYPE_TEXT);
141
        static::$payload[] = implode('',$args);
142
    }
143
144
    /**
145
     * Append an XML string to the buffer.
146
     * @param  mixed $payload Data to append to the response buffer
147
     */
148
    public static function xml(...$args){
149
        static::type(static::TYPE_XML);
150
        static::$payload[] = implode('', $args);
151
    }
152
153
    /**
154
     * Append a SVG string to the buffer.
155
     * @param  mixed $payload Data to append to the response buffer
156
     */
157
    public static function svg(...$args){
158
        static::type(static::TYPE_SVG);
159
        static::$payload[] = implode('', $args);
160
    }
161
162
    /**
163
     * Append an HTML string to the buffer.
164
     * @param  mixed $payload Data to append to the response buffer
165
     */
166
    public static function html(...$args){
167
        static::type(static::TYPE_HTML);
168
        static::$payload[] = implode('', $args);
169
    }
170
171
    /**
172
     * Append data to the buffer.
173
     *  Rules :
174
     *  - Callables will be called and their results added (recursive)
175
     *  - Views will be rendered
176
     *  - Objects, arrays and bools will be JSON encoded
177
     *  - Strings and numbers will be appendend to the response
178
     *
179
     * @param  mixed $payload Data to append to the response buffer
180
     */
181
    public static function add(){
182
      foreach(func_get_args() as $data){
183
        switch (true) {
184
          case is_callable($data) :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
185
            return static::add($data());
186
          case is_a($data, 'View') :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

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

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
306
                case 'css': $_type = 'style'; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
307
                case 'png': case 'svg': case 'gif': case 'jpg': $_type = 'image'; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
308
                case 'woff': case 'woff2': case 'ttf': case 'eof': $_type = 'font'; break;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
309
                default: $_type = 'text'; break;
0 ignored issues
show
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
310
            }
311
            foreach ((array)$link as $link_val) {
312
              static::header("Link","<$link_val>; rel=preload; as=$_type");
313
            }
314
        }
315
      } else {
316
        static::header("Link","<".((string)$links).">; rel=preload; as=$type");
317
      }
318
    }
319
320
}
321