1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Klein (klein.php) - A fast & flexible router for PHP |
4
|
|
|
* |
5
|
|
|
* @author Chris O'Hara <[email protected]> |
6
|
|
|
* @author Trevor Suarez (Rican7) (contributor and v2 refactorer) |
7
|
|
|
* @copyright (c) Chris O'Hara |
8
|
|
|
* @link https://github.com/klein/klein.php |
9
|
|
|
* @license MIT |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace app\framework\Component\Routing; |
13
|
|
|
|
14
|
|
|
use app\framework\Component\Routing\Exceptions\ResponseAlreadySentException; |
15
|
|
|
use RuntimeException; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Response |
19
|
|
|
* @package app\framework\Component\Routing |
20
|
|
|
*/ |
21
|
|
|
class Response extends AbstractResponse |
22
|
|
|
{ |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Methods |
26
|
|
|
*/ |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Enable response chunking |
30
|
|
|
* |
31
|
|
|
* @link https://github.com/klein/klein.php/wiki/Response-Chunking |
32
|
|
|
* @link http://bit.ly/hg3gHb |
33
|
|
|
* @param string $str An optional string to send as a response "chunk" |
34
|
|
|
* @return Response |
35
|
|
|
*/ |
36
|
|
|
public function chunk($str = null) |
37
|
|
|
{ |
38
|
|
|
parent::chunk(); |
39
|
|
|
|
40
|
|
|
if (null !== $str) { |
41
|
|
|
printf("%x\r\n", strlen($str)); |
42
|
|
|
echo "$str\r\n"; |
43
|
|
|
flush(); |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
return $this; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Dump a variable |
51
|
|
|
* |
52
|
|
|
* @param mixed $obj The variable to dump |
53
|
|
|
* @return Response |
54
|
|
|
*/ |
55
|
|
|
public function dump($obj) |
56
|
|
|
{ |
57
|
|
|
if (is_array($obj) || is_object($obj)) { |
58
|
|
|
$obj = print_r($obj, true); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
$this->append('<pre>' . htmlentities($obj, ENT_QUOTES) . "</pre><br />\n"); |
62
|
|
|
|
63
|
|
|
return $this; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Sends a file |
68
|
|
|
* |
69
|
|
|
* It should be noted that this method disables caching |
70
|
|
|
* of the response by default, as dynamically created |
71
|
|
|
* files responses are usually downloads of some type |
72
|
|
|
* and rarely make sense to be HTTP cached |
73
|
|
|
* |
74
|
|
|
* Also, this method removes any data/content that is |
75
|
|
|
* currently in the response body and replaces it with |
76
|
|
|
* the file's data |
77
|
|
|
* |
78
|
|
|
* @param string $path The path of the file to send |
79
|
|
|
* @param string $filename The file's name |
80
|
|
|
* @param string $mimetype The MIME type of the file |
81
|
|
|
* @throws RuntimeException Thrown if the file could not be read |
82
|
|
|
* @return Response |
83
|
|
|
*/ |
84
|
|
|
public function file($path, $filename = null, $mimetype = null) |
85
|
|
|
{ |
86
|
|
|
if ($this->sent) { |
87
|
|
|
throw new ResponseAlreadySentException('Response has already been sent'); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
$this->body(''); |
91
|
|
|
$this->noCache(); |
92
|
|
|
|
93
|
|
|
if (null === $filename) { |
94
|
|
|
$filename = basename($path); |
95
|
|
|
} |
96
|
|
|
if (null === $mimetype) { |
97
|
|
|
$mimetype = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
$this->header('Content-type', $mimetype); |
101
|
|
|
$this->header('Content-Disposition', 'attachment; filename="'.$filename.'"'); |
102
|
|
|
|
103
|
|
|
// If the response is to be chunked, then the content length must not be sent |
104
|
|
|
// see: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 |
105
|
|
|
if (false === $this->chunked) { |
106
|
|
|
$this->header('Content-length', filesize($path)); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
// Send our response data |
110
|
|
|
$this->sendHeaders(); |
111
|
|
|
|
112
|
|
|
$bytes_read = readfile($path); |
113
|
|
|
|
114
|
|
|
if (false === $bytes_read) { |
115
|
|
|
throw new RuntimeException('The file could not be read'); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
$this->sendBody(); |
119
|
|
|
|
120
|
|
|
// Lock the response from further modification |
121
|
|
|
$this->lock(); |
122
|
|
|
|
123
|
|
|
// Mark as sent |
124
|
|
|
$this->sent = true; |
125
|
|
|
|
126
|
|
|
// If there running FPM, tell the process manager to finish the server request/response handling |
127
|
|
|
if (function_exists('fastcgi_finish_request')) { |
128
|
|
|
fastcgi_finish_request(); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
return $this; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Sends an object as json or jsonp by providing the padding prefix |
136
|
|
|
* |
137
|
|
|
* It should be noted that this method disables caching |
138
|
|
|
* of the response by default, as json responses are usually |
139
|
|
|
* dynamic and rarely make sense to be HTTP cached |
140
|
|
|
* |
141
|
|
|
* Also, this method removes any data/content that is |
142
|
|
|
* currently in the response body and replaces it with |
143
|
|
|
* the passed json encoded object |
144
|
|
|
* |
145
|
|
|
* @param mixed $object The data to encode as JSON |
146
|
|
|
* @param string $jsonp_prefix The name of the JSON-P function prefix |
147
|
|
|
* @return Response |
148
|
|
|
*/ |
149
|
|
|
public function json($object, $jsonp_prefix = null) |
150
|
|
|
{ |
151
|
|
|
$this->body(''); |
152
|
|
|
$this->noCache(); |
153
|
|
|
|
154
|
|
|
$json = json_encode($object); |
155
|
|
|
|
156
|
|
|
if (null !== $jsonp_prefix) { |
157
|
|
|
// Should ideally be application/json-p once adopted |
158
|
|
|
$this->header('Content-Type', 'text/javascript'); |
159
|
|
|
$this->body("$jsonp_prefix($json);"); |
160
|
|
|
} else { |
161
|
|
|
$this->header('Content-Type', 'application/json'); |
162
|
|
|
$this->body($json); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
$this->send(); |
166
|
|
|
|
167
|
|
|
return $this; |
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
|