Failed Conditions
Branch refactor/kernels (fbf61a)
by Atanas
01:47
created

src/Responses/ResponseService.php (2 issues)

Labels
Severity
1
<?php
2
/**
3
 * @package   WPEmerge
4
 * @author    Atanas Angelov <[email protected]>
5
 * @copyright 2018 Atanas Angelov
6
 * @license   https://www.gnu.org/licenses/gpl-2.0.html GPL-2.0
7
 * @link      https://wpemerge.com/
8
 */
9
10
namespace WPEmerge\Responses;
11
12
use GuzzleHttp\Psr7;
13
use GuzzleHttp\Psr7\Response as Psr7Response;
14
use WPEmerge\Facades\View;
15
use WPEmerge\Requests\RequestInterface;
16
use WPEmerge\View\ViewInterface;
17
use Psr\Http\Message\ResponseInterface;
18
use Psr\Http\Message\StreamInterface;
19
20
/**
21
 * A collection of tools for the creation of responses.
22
 */
23
class ResponseService {
24
	/**
25
	 * Current request.
26
	 *
27
	 * @var RequestInterface
28
	 */
29
	protected $request = null;
30
31
	/**
32
	 * Constructor.
33
	 *
34
	 * @codeCoverageIgnore
35
	 * @param RequestInterface $request
36
	 */
37
	public function __construct( RequestInterface $request ) {
38
		$this->request = $request;
39
	}
40
41
	/**
42
	 * Send output based on a response object.
43
	 * @credit heavily modified version of slimphp/slim - Slim/App.php
44
	 *
45
	 * @codeCoverageIgnore
46
	 * @param  ResponseInterface $response
47
	 * @return void
48
	 */
49
	public function respond( ResponseInterface $response ) {
50
		if ( ! headers_sent() ) {
51
			$this->sendHeaders( $response );
52
		}
53
		$this->sendBody( $response );
54
	}
55
56
	/**
57
	 * Send a request's headers to the client.
58
	 *
59
	 * @codeCoverageIgnore
60
	 * @param  ResponseInterface $response
61
	 * @return void
62
	 */
63
	public function sendHeaders( ResponseInterface $response ) {
64
		// Status
65
		header( sprintf(
66
			'HTTP/%s %s %s',
67
			$response->getProtocolVersion(),
68
			$response->getStatusCode(),
69
			$response->getReasonPhrase()
70
		) );
71
72
		// Headers
73
		foreach ( $response->getHeaders() as $name => $values ) {
74
			foreach ( $values as $value ) {
75
				header( sprintf( '%s: %s', $name, $value ), false );
76
			}
77
		}
78
	}
79
80
	/**
81
	 * Get a response's body stream so it is ready to be read.
82
	 *
83
	 * @codeCoverageIgnore
84
	 * @param  ResponseInterface $response
85
	 * @return StreamInterface
86
	 */
87
	protected function getBody( ResponseInterface $response ) {
88
		$body = $response->getBody();
89
		if ( $body->isSeekable() ) {
90
			$body->rewind();
91
		}
92
		return $body;
93
	}
94
95
	/**
96
	 * Get a response's body's content length.
97
	 *
98
	 * @codeCoverageIgnore
99
	 * @param  ResponseInterface $response
100
	 * @return integer
101
	 */
102
	protected function getBodyContentLength( ResponseInterface $response ) {
103
		$content_length = $response->getHeaderLine( 'Content-Length' );
104
105
		if ( ! $content_length ) {
106
			$body = $this->getBody( $response );
107
			$content_length = $body->getSize();
108
		}
109
110
		if ( ! is_numeric( $content_length ) ) {
111
			$content_length = 0;
112
		}
113
114
		return (integer) $content_length;
115
	}
116
117
	/**
118
	 * Send a request's body to the client.
119
	 *
120
	 * @codeCoverageIgnore
121
	 * @param  ResponseInterface $response
122
	 * @param  integer           $chunk_size
123
	 * @return void
124
	 */
125
	public function sendBody( ResponseInterface $response, $chunk_size = 4096 ) {
126
		$body = $this->getBody( $response );
127
		$content_length = $this->getBodyContentLength( $response );
128
129
		if ( $content_length > 0 ) {
130
			$this->sendBodyWithLength( $body, $content_length, $chunk_size );
131
		} else {
132
			$this->sendBodyWithoutLength( $body, $chunk_size );
133
		}
134
	}
135
136
	/**
137
	 * Send a body with an unknown length to the client.
138
	 *
139
	 * @codeCoverageIgnore
140
	 * @param  StreamInterface $body
141
	 * @param  integer         $chunk_size
142
	 * @return void
143
	 */
144
	protected function sendBodyWithoutLength( StreamInterface $body, $chunk_size ) {
145
		while ( ! $body->eof() ) {
146
			echo $body->read( $chunk_size );
147
148
			if ( connection_status() !== CONNECTION_NORMAL ) {
149
				break;
150
			}
151
		}
152
	}
153
154
	/**
155
	 * Send a body with a known length to the client.
156
	 *
157
	 * @codeCoverageIgnore
158
	 * @param  StreamInterface $body
159
	 * @param  integer         $length
160
	 * @param  integer         $chunk_size
161
	 * @return void
162
	 */
163
	protected function sendBodyWithLength( StreamInterface $body, $length, $chunk_size ) {
164
		$content_left = $length;
165
166
		while ( $content_left > 0 ) {
167
			$read = min( $chunk_size, $content_left );
168
169
			if ( $read <= 0 ) {
170
				break;
171
			}
172
173
			echo $body->read( $read );
174
175
			$content_left -= $read;
176
177
			if ( connection_status() !== CONNECTION_NORMAL ) {
178
				break;
179
			}
180
		}
181
	}
182
183
	/**
184
	 * Create a new response object.
185
	 *
186
	 * @return ResponseInterface
187
	 */
188 1
	public function response() {
189 1
		return new Psr7Response();
190
	}
191
192
	/**
193
	 * Get a cloned response with the passed string as the body.
194
	 *
195
	 * @param  string            $output
196
	 * @return ResponseInterface
197
	 */
198 1
	public function output( $output ) {
199 1
		$response = $this->response();
200 1
		$response = $response->withBody( Psr7\stream_for( $output ) );
1 ignored issue
show
The function stream_for was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

200
		$response = $response->withBody( /** @scrutinizer ignore-call */ Psr7\stream_for( $output ) );
Loading history...
201 1
		return $response;
202
	}
203
204
	/**
205
	 * Get a cloned response, json encoding the passed data as the body.
206
	 *
207
	 * @param  mixed             $data
208
	 * @return ResponseInterface
209
	 */
210 1
	public function json( $data ) {
211 1
		$response = $this->response();
212 1
		$response = $response->withHeader( 'Content-Type', 'application/json' );
213 1
		$response = $response->withBody( Psr7\stream_for( wp_json_encode( $data ) ) );
1 ignored issue
show
The function stream_for was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

213
		$response = $response->withBody( /** @scrutinizer ignore-call */ Psr7\stream_for( wp_json_encode( $data ) ) );
Loading history...
214 1
		return $response;
215
	}
216
217
	/**
218
	 * Get a cloned response, with location and status headers.
219
	 *
220
	 * @return RedirectResponse
221
	 */
222 1
	public function redirect() {
223 1
		return new RedirectResponse( $this->request );
224
	}
225
226
	/**
227
	 * Get a view file representation.
228
	 *
229
	 * @param  string|array<string> $views
230
	 * @return ViewInterface
231
	 */
232 1
	public function view( $views ) {
233 1
		return View::make( $views );
234
	}
235
236
	/**
237
	 * Get an error response, with status headers and rendering a suitable view as the body.
238
	 *
239
	 * @param  integer           $status
240
	 * @return ResponseInterface
241
	 */
242 1
	public function error( $status ) {
243 1
		return $this->view( [$status, 'error', 'index'] )
244 1
			->toResponse()
245 1
			->withStatus( $status );
246
	}
247
}
248