Passed
Push — master ( e17332...08a422 )
by Atanas
01:46
created

Response::sendBodyWithLength()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

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