Passed
Push — master ( 3d2072...7dac2b )
by Atanas
01:46
created

Response::resolveView()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

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