Passed
Push — master ( 2f1c34...4bc1b6 )
by Atanas
02:03
created

Response   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 95.45%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 237
ccs 42
cts 44
cp 0.9545
rs 9.8
c 1
b 0
f 0
wmc 31
lcom 2
cbo 5

14 Methods

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