Passed
Push — master ( b7467b...db04eb )
by Atanas
02:41
created

Response::sendBody()   B

Complexity

Conditions 6
Paths 20

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

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