Completed
Push — master ( 442408...cd30c0 )
by Stefano
03:15
created

Response::body()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 2
nop 1
dl 0
loc 6
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
34
35
    public static function charset($charset){
36
        static::$charset = $charset;
37
    }
38
39
    public static function type($mime){
40
        static::header('Content-Type',$mime . (static::$charset ? '; charset='.static::$charset : ''));
41
    }
42
43
    /**
44
     * Force download of Response body
45
     * @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...
46
     * @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...
47
     */
48
    public static function download($filename){
49
        static::$force_dl = $filename;
50
    }
51
52
    /**
53
     * Start capturing output
54
     */
55
    public static function start(){
56
        static::$buffer = ob_start();
57
    }
58
59
    /**
60
     * Enable CORS HTTP headers.
61
     */
62
    public static function enableCORS($origin='*'){
63
64
        // Allow from any origin
65
        if ($origin = $origin ?:( isset($_SERVER['HTTP_ORIGIN'])
66
                    ? $_SERVER['HTTP_ORIGIN']
67
                    : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '*')
68
        )) {
69
          static::header('Access-Control-Allow-Origin',      $origin);
70
          static::header('Access-Control-Allow-Credentials', 'true');
71
          static::header('Access-Control-Max-Age',           86400);
72
        }
73
74
        // Access-Control headers are received during OPTIONS requests
75
        if (filter_input(INPUT_SERVER,'REQUEST_METHOD') == 'OPTIONS') {
76
            static::clean();
77
78
            if (filter_input(INPUT_SERVER,'HTTP_ACCESS_CONTROL_REQUEST_METHOD')) {
79
              static::header('Access-Control-Allow-Methods',
80
                'GET, POST, PUT, DELETE, OPTIONS, HEAD, CONNECT, PATCH, TRACE');
81
            }
82
            if ($req_h = filter_input(INPUT_SERVER,'HTTP_ACCESS_CONTROL_REQUEST_HEADERS')) {
83
              static::header('Access-Control-Allow-Headers',$req_h);
84
            }
85
86
            self::trigger('cors.preflight');
87
            static::send();
88
            exit;
89
        }
90
    }
91
92
    public static function sent() {
93
        return static::$sent;
94
    }
95
96
    /**
97
     * Finish the output buffer capturing.
98
     * @return string The captured buffer
99
     */
100
    public static function end(){
101
        if (static::$buffer){
102
            static::$payload[] = ob_get_contents();
103
            ob_end_clean();
104
            static::$buffer = null;
105
            return end(static::$payload);
106
        }
107
    }
108
109
    /**
110
     * Check if an response output buffering is active.
111
     * @return boolean
112
     */
113
    public static function isBuffering(){
114
        return static::$buffer;
115
    }
116
117
    /**
118
     * Clear the response body
119
     */
120
    public static function clean(){
121
        static::$payload = [];
122
    }
123
124
    /**
125
     * Append a JSON object to the buffer.
126
     * @param  mixed $payload Data to append to the response buffer
127
     */
128
    public static function json($payload){
129
        static::type(static::TYPE_JSON);
130
        static::$payload[] = json_encode($payload, Options::get('core.response.json_flags',JSON_NUMERIC_CHECK));
131
    }
132
133
    /**
134
     * Append a text to the buffer.
135
     * @param  mixed $payload Text to append to the response buffer
136
     */
137
    public static function text(...$args){
138
        static::type(static::TYPE_TEXT);
139
        static::$payload[] = implode('',$args);
140
    }
141
142
    /**
143
     * Append an XML string to the buffer.
144
     * @param  mixed $payload Data to append to the response buffer
145
     */
146
    public static function xml(...$args){
147
        static::type(static::TYPE_XML);
148
        static::$payload[] = implode('', $args);
149
    }
150
151
    /**
152
     * Append a SVG string to the buffer.
153
     * @param  mixed $payload Data to append to the response buffer
154
     */
155
    public static function svg(...$args){
156
        static::type(static::TYPE_SVG);
157
        static::$payload[] = implode('', $args);
158
    }
159
160
    /**
161
     * Append an HTML string to the buffer.
162
     * @param  mixed $payload Data to append to the response buffer
163
     */
164
    public static function html(...$args){
165
        static::type(static::TYPE_HTML);
166
        static::$payload[] = implode('', $args);
167
    }
168
169
    /**
170
     * Append data to the buffer.
171
     *  Rules :
172
     *  - Callables will be called and their results added (recursive)
173
     *  - Views will be rendered
174
     *  - Objects, arrays and bools will be JSON encoded
175
     *  - Strings and numbers will be appendend to the response
176
     *
177
     * @param  mixed $payload Data to append to the response buffer
178
     */
179
    public static function add(){
180
      foreach(func_get_args() as $data){
181
        switch (true) {
182
          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...
183
            return static::add($data());
184
          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...
185
            return static::$payload[] = "$data";
186
          case is_object($data) || is_array($data) || is_bool($data):
187
            return static::json($data);
188
          default:
189
            return static::$payload[] = $data;
190
        }
191
      }
192
    }
193
194
    public static function status($code,$message=''){
195
      static::header('Status',$message?:$code,$code);
196
    }
197
198
    public static function header($name,$value,$code=null){
199
      static::$headers[$name] = [$value,$code];
200
    }
201
202
    public static function error($code=500,$message='Application Error'){
203
      static::trigger('error',$code,$message);
204
      Event::trigger('core.response.error',$code,$message);
205
      static::status($code,$message);
206
    }
207
208
    public static function body($setBody=null){
209
      if ($setBody) static::$payload = [$setBody];
210
      return Filter::with('core.response.body',
211
        is_array(static::$payload) ? implode('',static::$payload) : static::$payload
212
      );
213
    }
214
215
    public static function headers($setHeaders=null){
216
       if ($setHeaders) static::$headers = $setHeaders;
217
       return static::$headers;
218
    }
219
220
    /**
221
     * Save response as an object, for serialization or cache storage
222
     *
223
     * @method save
224
     *
225
     * @return array Headers and body of the response
226
     */
227
    public static function save(){
228
        return [
229
          'head' => static::$headers,
230
          'body' => static::body(),
231
        ];
232
    }
233
234
    /**
235
     * Load response from a saved state
236
     *
237
     * @method load
238
     *
239
     * @param  array $data head/body saved state
240
     */
241
    public static function load($data){
242
      $data = (object)$data;
243
      if (isset($data->head)) static::headers($data->head);
244
      if (isset($data->body)) static::body($data->body);
245
    }
246
247
    public static function send($force = false){
248
      if (!static::$sent || $force) {
249
        static::$sent = true;
250
        static::trigger('send');
251
        Event::trigger('core.response.send');
252
        if (false === headers_sent()) foreach (static::$headers as $name => $value_code) {
253
254
            if (is_array($value_code)) {
255
                list($value, $code) = (count($value_code) > 1) ? $value_code : [current($value_code), 200];
256
            } else {
257
                $value = $value_code;
258
                $code  = null;
259
            }
260
261
            if ($value == 'Status'){
262
              if (function_exists('http_response_code')){
263
                http_response_code($code);
264
              } else {
265
                header("Status: $code", true, $code);
266
              }
267
268
            } else {
269
                $code
270
                ? header("$name: $value", true, $code)
271
                : header("$name: $value", true);
272
            }
273
        }
274
        if (static::$force_dl) header('Content-Disposition: attachment; filename="'.static::$force_dl.'"');
275
        echo static::body();
276
        static::trigger('sent');
277
      }
278
    }
279
280
}
281