Passed
Push — master ( bf1276...3d9c09 )
by Atanas
01:51
created

Response::sendBody()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
224 1
		if ( $status === 404 ) {
225 1
			$wp_query->set_404();
226 1
		}
227
228 1
		return static::view( [$status, 'error', 'index'] )
229 1
			->toResponse()
230 1
			->withStatus( $status );
231
	}
232
}
233