|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace WPEmerge\Responses; |
|
4
|
|
|
|
|
5
|
|
|
use GuzzleHttp\Psr7; |
|
6
|
|
|
use GuzzleHttp\Psr7\Response as Psr7Response; |
|
7
|
|
|
use WPEmerge\Facades\Framework; |
|
8
|
|
|
use WPEmerge\Helpers\Mixed; |
|
9
|
|
|
use WPEmerge\Responses\RedirectResponse; |
|
10
|
|
|
use WPEmerge\View\ViewInterface; |
|
11
|
|
|
use Psr\Http\Message\ResponseInterface; |
|
12
|
|
|
use Psr\Http\Message\StreamInterface; |
|
13
|
|
|
|
|
14
|
|
|
/** |
|
15
|
|
|
* A collection of tools for the creation of responses |
|
16
|
|
|
*/ |
|
17
|
|
|
class Response { |
|
18
|
|
|
/** |
|
19
|
|
|
* Send output based on a response object. |
|
20
|
|
|
* @credit heavily modified version of slimphp/slim - Slim/App.php |
|
21
|
|
|
* |
|
22
|
|
|
* @codeCoverageIgnore |
|
23
|
|
|
* @param ResponseInterface $response |
|
24
|
|
|
* @return void |
|
25
|
|
|
*/ |
|
26
|
|
|
public static function respond( ResponseInterface $response ) { |
|
27
|
|
|
if ( ! headers_sent() ) { |
|
28
|
|
|
static::sendHeaders( $response ); |
|
29
|
|
|
} |
|
30
|
|
|
static::sendBody( $response ); |
|
31
|
|
|
} |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* Send a request's headers to the client. |
|
35
|
|
|
* |
|
36
|
|
|
* @codeCoverageIgnore |
|
37
|
|
|
* @param ResponseInterface $response |
|
38
|
|
|
* @return void |
|
39
|
|
|
*/ |
|
40
|
|
|
protected static function sendHeaders( ResponseInterface $response ) { |
|
41
|
|
|
// Status |
|
42
|
|
|
header( sprintf( |
|
43
|
|
|
'HTTP/%s %s %s', |
|
44
|
|
|
$response->getProtocolVersion(), |
|
45
|
|
|
$response->getStatusCode(), |
|
46
|
|
|
$response->getReasonPhrase() |
|
47
|
|
|
) ); |
|
48
|
|
|
|
|
49
|
|
|
// Headers |
|
50
|
|
|
foreach ( $response->getHeaders() as $name => $values ) { |
|
51
|
|
|
foreach ( $values as $value ) { |
|
52
|
|
|
header( sprintf( '%s: %s', $name, $value ), false ); |
|
53
|
|
|
} |
|
54
|
|
|
} |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Get a response's body stream so it is ready to be read. |
|
59
|
|
|
* |
|
60
|
|
|
* @codeCoverageIgnore |
|
61
|
|
|
* @param ResponseInterface $response |
|
62
|
|
|
* @return StreamInterface |
|
63
|
|
|
*/ |
|
64
|
|
|
protected static function getBody( ResponseInterface $response ) { |
|
65
|
|
|
$body = $response->getBody(); |
|
66
|
|
|
if ( $body->isSeekable() ) { |
|
67
|
|
|
$body->rewind(); |
|
68
|
|
|
} |
|
69
|
|
|
return $body; |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
|
|
/** |
|
73
|
|
|
* Get a response's body's content length. |
|
74
|
|
|
* |
|
75
|
|
|
* @codeCoverageIgnore |
|
76
|
|
|
* @param ResponseInterface $response |
|
77
|
|
|
* @return integer |
|
78
|
|
|
*/ |
|
79
|
|
|
protected static function getBodyContentLength( ResponseInterface $response ) { |
|
80
|
|
|
$content_length = $response->getHeaderLine( 'Content-Length' ); |
|
81
|
|
|
|
|
82
|
|
|
if ( ! $content_length ) { |
|
83
|
|
|
$body = static::getBody( $response ); |
|
84
|
|
|
$content_length = $body->getSize(); |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
if ( ! is_numeric( $content_length ) ) { |
|
88
|
|
|
$content_length = 0; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
return (integer) $content_length; |
|
92
|
|
|
} |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* Send a request's body to the client. |
|
96
|
|
|
* |
|
97
|
|
|
* @codeCoverageIgnore |
|
98
|
|
|
* @param ResponseInterface $response |
|
99
|
|
|
* @param integer $chunk_size |
|
100
|
|
|
* @return void |
|
101
|
|
|
*/ |
|
102
|
|
|
protected static function sendBody( ResponseInterface $response, $chunk_size = 4096 ) { |
|
103
|
|
|
$body = static::getBody( $response ); |
|
104
|
|
|
$content_length = static::getBodyContentLength( $response ); |
|
105
|
|
|
|
|
106
|
|
|
if ( $content_length > 0 ) { |
|
107
|
|
|
static::sendBodyWithLength( $body, $content_length, $chunk_size ); |
|
108
|
|
|
} else { |
|
109
|
|
|
static::sendBodyWithoutLength( $body, $chunk_size ); |
|
110
|
|
|
} |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
/** |
|
114
|
|
|
* Send a body with an unknown length to the client. |
|
115
|
|
|
* |
|
116
|
|
|
* @codeCoverageIgnore |
|
117
|
|
|
* @param StreamInterface $body |
|
118
|
|
|
* @param integer $chunk_size |
|
119
|
|
|
* @return void |
|
120
|
|
|
*/ |
|
121
|
|
|
protected static function sendBodyWithoutLength( StreamInterface $body, $chunk_size ) { |
|
122
|
|
|
while ( ! $body->eof() ) { |
|
123
|
|
|
echo $body->read( $chunk_size ); |
|
124
|
|
|
|
|
125
|
|
|
if ( connection_status() != CONNECTION_NORMAL ) { |
|
126
|
|
|
break; |
|
127
|
|
|
} |
|
128
|
|
|
} |
|
129
|
|
|
} |
|
130
|
|
|
|
|
131
|
|
|
/** |
|
132
|
|
|
* Send a body with a known length to the client. |
|
133
|
|
|
* |
|
134
|
|
|
* @codeCoverageIgnore |
|
135
|
|
|
* @param StreamInterface $body |
|
136
|
|
|
* @param integer $length |
|
137
|
|
|
* @param integer $chunk_size |
|
138
|
|
|
* @return void |
|
139
|
|
|
*/ |
|
140
|
|
|
protected static function sendBodyWithLength( StreamInterface $body, $length, $chunk_size ) { |
|
141
|
|
|
$content_left = $length; |
|
142
|
|
|
|
|
143
|
|
|
while ( $content_left > 0 ) { |
|
144
|
|
|
$read = min( $chunk_size, $content_left ); |
|
145
|
|
|
|
|
146
|
|
|
if ( $read <= 0 ) { |
|
147
|
|
|
break; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
echo $body->read( $read ); |
|
151
|
|
|
|
|
152
|
|
|
$content_left -= $read; |
|
153
|
|
|
|
|
154
|
|
|
if ( connection_status() != CONNECTION_NORMAL ) { |
|
155
|
|
|
break; |
|
156
|
|
|
} |
|
157
|
|
|
} |
|
158
|
|
|
} |
|
159
|
|
|
|
|
160
|
|
|
/** |
|
161
|
|
|
* Create a new response object. |
|
162
|
|
|
* |
|
163
|
|
|
* @return ResponseInterface |
|
164
|
|
|
*/ |
|
165
|
1 |
|
public static function response() { |
|
166
|
1 |
|
return new Psr7Response(); |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
/** |
|
170
|
|
|
* Get a cloned response with the passed string as the body. |
|
171
|
|
|
* |
|
172
|
|
|
* @param string $output |
|
173
|
|
|
* @return ResponseInterface |
|
174
|
|
|
*/ |
|
175
|
1 |
|
public static function output( $output ) { |
|
176
|
1 |
|
$response = static::response(); |
|
177
|
1 |
|
$response = $response->withBody( Psr7\stream_for( $output ) ); |
|
178
|
1 |
|
return $response; |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
/** |
|
182
|
|
|
* Get a cloned response, json encoding the passed data as the body. |
|
183
|
|
|
* |
|
184
|
|
|
* @param mixed $data |
|
185
|
|
|
* @return ResponseInterface |
|
186
|
|
|
*/ |
|
187
|
1 |
|
public static function json( $data ) { |
|
188
|
1 |
|
$response = static::response(); |
|
189
|
1 |
|
$response = $response->withHeader( 'Content-Type', 'application/json' ); |
|
190
|
1 |
|
$response = $response->withBody( Psr7\stream_for( wp_json_encode( $data ) ) ); |
|
191
|
1 |
|
return $response; |
|
192
|
|
|
} |
|
193
|
|
|
|
|
194
|
|
|
/** |
|
195
|
|
|
* Get a cloned response, with location and status headers. |
|
196
|
|
|
* |
|
197
|
|
|
* @return RedirectResponse |
|
198
|
|
|
*/ |
|
199
|
1 |
|
public static function redirect() { |
|
200
|
1 |
|
return new RedirectResponse( Framework::resolve( WPEMERGE_REQUEST_KEY ) ); |
|
201
|
|
|
} |
|
202
|
|
|
|
|
203
|
|
|
/** |
|
204
|
|
|
* Get a cloned response, with location header equal to the current url and status header. |
|
205
|
|
|
* |
|
206
|
|
|
* @param \WPEmerge\Requests\Request $request |
|
207
|
|
|
* @param integer $status |
|
208
|
|
|
* @return ResponseInterface |
|
209
|
|
|
*/ |
|
210
|
|
|
public static function reload( $request, $status = 302 ) { |
|
211
|
|
|
return static::redirect( $request->getUrl(), $status ); |
|
|
|
|
|
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
/** |
|
215
|
|
|
* Get a view convertible to a response. |
|
216
|
|
|
* |
|
217
|
|
|
* @param string|string[] $views |
|
218
|
|
|
* @param array $context |
|
219
|
|
|
* @return ViewInterface |
|
220
|
|
|
*/ |
|
221
|
1 |
|
public static function view( $views, $context = [] ) { |
|
222
|
1 |
|
$views = Mixed::toArray( $views ); |
|
223
|
1 |
|
$engine = Framework::resolve( WPEMERGE_VIEW_ENGINE_KEY ); |
|
224
|
1 |
|
return $engine->make( $views, $context ); |
|
225
|
|
|
} |
|
226
|
|
|
|
|
227
|
|
|
/** |
|
228
|
|
|
* Get an error response, with status headers and rendering a suitable view as the body. |
|
229
|
|
|
* |
|
230
|
|
|
* @param integer $status |
|
231
|
|
|
* @return ResponseInterface |
|
232
|
|
|
*/ |
|
233
|
1 |
|
public static function error( $status ) { |
|
234
|
1 |
|
global $wp_query; |
|
|
|
|
|
|
235
|
1 |
|
if ( $status === 404 ) { |
|
236
|
1 |
|
$wp_query->set_404(); |
|
237
|
|
|
} |
|
238
|
|
|
|
|
239
|
1 |
|
return static::view( [$status, 'error', 'index'] ) |
|
240
|
1 |
|
->toResponse() |
|
241
|
1 |
|
->withStatus( $status ); |
|
242
|
|
|
} |
|
243
|
|
|
} |
|
244
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.