Skip to content

Commit 15870ae

Browse files
authored
Merge pull request #1 from gpolverini/master
- Adding ClientInterface - Fixing closing resource handler on destructing - Completing unit tests
2 parents 4ab0339 + 631eb7a commit 15870ae

18 files changed

+4416
-521
lines changed

phpunit.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
<logging>
2828
<log type="coverage-html" target="./build/coverage" charset="UTF-8"
29-
title="Recordings Service"
29+
title="PSR-7 HTTP Client (cURL)"
3030
yui="true" highlight="true"
3131
lowUpperBound="35" highLowerBound="70" />
3232
<log type="coverage-clover" target="./build/logs/clover.xml"/>

src/AbstractMessage.php

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
<?php
2+
/*
3+
Copyright (c) 2017 Jorge Matricali <jorgematricali@gmail.com>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
22+
*/
23+
24+
namespace Matricali\Http;
25+
26+
use Psr\Http\Message\MessageInterface;
27+
use Psr\Http\Message\StreamInterface;
28+
29+
abstract class AbstractMessage implements MessageInterface
30+
{
31+
protected $protocolVersion;
32+
protected $headers;
33+
protected $body;
34+
35+
/**
36+
* AbstractMessage constructor.
37+
*
38+
* @param string $protocolVersion.
39+
* @param array $headers Header value(s).
40+
* @param string $body.
41+
* @throws \InvalidArgumentException for invalid HTTP methods.
42+
*/
43+
public function __construct($protocolVersion = '1.1', array $headers = array(), $body = null)
44+
{
45+
if ($body !== null && !is_string($body)) {
46+
throw new \InvalidArgumentException('The body parameter must be of the string type');
47+
}
48+
$this->protocolVersion = $protocolVersion;
49+
$this->headers = [];
50+
if ($headers !== null) {
51+
foreach ($headers as $k => $v) {
52+
if (!is_array($v)) {
53+
$this->headers[$k] = array_map('trim', explode(',', $v));
54+
continue;
55+
}
56+
$this->headers[$k] = $v;
57+
}
58+
}
59+
$this->body = $body;
60+
}
61+
62+
/**
63+
* Retrieves the index if a header exists by the given case-insensitive name.
64+
*
65+
* @param string $name Case-insensitive header field name.
66+
* @return mixed Returns integer if any header names match the given header
67+
* name using a case-insensitive string comparison. Returns false if
68+
* no matching header name is found in the message.
69+
*/
70+
protected function searchHeader($name)
71+
{
72+
$keysHeader = array_keys($this->headers);
73+
$index = array_search(strtolower($name), array_map('strtolower', $keysHeader));
74+
return $index !== false ? $keysHeader[$index] : $index;
75+
}
76+
77+
/**
78+
* Retrieves the HTTP protocol version as a string.
79+
*
80+
* The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
81+
*
82+
* @return string HTTP protocol version.
83+
*/
84+
public function getProtocolVersion()
85+
{
86+
return $this->protocolVersion;
87+
}
88+
89+
/**
90+
* Return an instance with the specified HTTP protocol version.
91+
*
92+
* The version string MUST contain only the HTTP version number (e.g.,
93+
* "1.1", "1.0").
94+
*
95+
* This method MUST be implemented in such a way as to retain the
96+
* immutability of the message, and MUST return an instance that has the
97+
* new protocol version.
98+
*
99+
* @param string $version HTTP protocol version
100+
* @return static
101+
*/
102+
public function withProtocolVersion($version)
103+
{
104+
$clone = clone $this;
105+
$clone->protocolVersion = $version;
106+
return $clone;
107+
}
108+
109+
/**
110+
* Retrieves all message header values.
111+
*
112+
* The keys represent the header name as it will be sent over the wire, and
113+
* each value is an array of strings associated with the header.
114+
*
115+
* // Represent the headers as a string
116+
* foreach ($message->getHeaders() as $name => $values) {
117+
* echo $name . ": " . implode(", ", $values);
118+
* }
119+
*
120+
* // Emit headers iteratively:
121+
* foreach ($message->getHeaders() as $name => $values) {
122+
* foreach ($values as $value) {
123+
* header(sprintf('%s: %s', $name, $value), false);
124+
* }
125+
* }
126+
*
127+
* While header names are not case-sensitive, getHeaders() will preserve the
128+
* exact case in which headers were originally specified.
129+
*
130+
* @return string[][] Returns an associative array of the message's headers. Each
131+
* key MUST be a header name, and each value MUST be an array of strings
132+
* for that header.
133+
*/
134+
public function getHeaders()
135+
{
136+
return $this->headers;
137+
}
138+
139+
/**
140+
* Checks if a header exists by the given case-insensitive name.
141+
*
142+
* @param string $name Case-insensitive header field name.
143+
* @return bool Returns true if any header names match the given header
144+
* name using a case-insensitive string comparison. Returns false if
145+
* no matching header name is found in the message.
146+
*/
147+
public function hasHeader($name)
148+
{
149+
return $this->searchHeader($name) !== false;
150+
}
151+
152+
/**
153+
* Retrieves a message header value by the given case-insensitive name.
154+
*
155+
* This method returns an array of all the header values of the given
156+
* case-insensitive header name.
157+
*
158+
* If the header does not appear in the message, this method MUST return an
159+
* empty array.
160+
*
161+
* @param string $name Case-insensitive header field name.
162+
* @return string[] An array of string values as provided for the given
163+
* header. If the header does not appear in the message, this method MUST
164+
* return an empty array.
165+
*/
166+
public function getHeader($name)
167+
{
168+
$key = $this->searchHeader($name);
169+
if ($key !== false) {
170+
return $this->headers[$key];
171+
}
172+
return [];
173+
}
174+
175+
/**
176+
* Retrieves a comma-separated string of the values for a single header.
177+
*
178+
* This method returns all of the header values of the given
179+
* case-insensitive header name as a string concatenated together using
180+
* a comma.
181+
*
182+
* NOTE: Not all header values may be appropriately represented using
183+
* comma concatenation. For such headers, use getHeader() instead
184+
* and supply your own delimiter when concatenating.
185+
*
186+
* If the header does not appear in the message, this method MUST return
187+
* an empty string.
188+
*
189+
* @param string $name Case-insensitive header field name.
190+
* @return string A string of values as provided for the given header
191+
* concatenated together using a comma. If the header does not appear in
192+
* the message, this method MUST return an empty string.
193+
*/
194+
public function getHeaderLine($name)
195+
{
196+
$key = $this->searchHeader($name);
197+
if ($key !== false) {
198+
return implode(', ', $this->headers[$key]);
199+
}
200+
return '';
201+
}
202+
203+
/**
204+
* Return an instance with the provided value replacing the specified header.
205+
*
206+
* While header names are case-insensitive, the casing of the header will
207+
* be preserved by this function, and returned from getHeaders().
208+
*
209+
* This method MUST be implemented in such a way as to retain the
210+
* immutability of the message, and MUST return an instance that has the
211+
* new and/or updated header and value.
212+
*
213+
* @param string $name Case-insensitive header field name.
214+
* @param string|string[] $value Header value(s).
215+
* @return static
216+
* @throws \InvalidArgumentException for invalid header names or values.
217+
*/
218+
public function withHeader($name, $value)
219+
{
220+
$key = $this->searchHeader($name);
221+
$value = is_array($value) ? $value : [$value];
222+
if ($key !== false) {
223+
$name = $key;
224+
}
225+
$clone = clone $this;
226+
$clone->headers[$name] = $value;
227+
return $clone;
228+
}
229+
230+
/**
231+
* Return an instance with the specified header appended with the given value.
232+
*
233+
* Existing values for the specified header will be maintained. The new
234+
* value(s) will be appended to the existing list. If the header did not
235+
* exist previously, it will be added.
236+
*
237+
* This method MUST be implemented in such a way as to retain the
238+
* immutability of the message, and MUST return an instance that has the
239+
* new header and/or value.
240+
*
241+
* @param string $name Case-insensitive header field name to add.
242+
* @param string|string[] $value Header value(s).
243+
* @return static
244+
* @throws \InvalidArgumentException for invalid header names or values.
245+
*/
246+
public function withAddedHeader($name, $value)
247+
{
248+
$key = $this->searchHeader($name);
249+
$value = is_array($value) ? $value : [$value];
250+
if ($key !== false) {
251+
$name = $key;
252+
$value = array_merge($this->headers[$key], $value);
253+
}
254+
$clone = clone $this;
255+
$clone->headers[$name] = $value;
256+
return $clone;
257+
}
258+
259+
/**
260+
* Return an instance without the specified header.
261+
*
262+
* Header resolution MUST be done without case-sensitivity.
263+
*
264+
* This method MUST be implemented in such a way as to retain the
265+
* immutability of the message, and MUST return an instance that removes
266+
* the named header.
267+
*
268+
* @param string $name Case-insensitive header field name to remove.
269+
* @return static
270+
*/
271+
public function withoutHeader($name)
272+
{
273+
$clone = clone $this;
274+
$key = $this->searchHeader($name);
275+
if ($key !== false) {
276+
unset($clone->headers[$key]);
277+
}
278+
return $clone;
279+
}
280+
281+
/**
282+
* Gets the body of the message.
283+
*
284+
* @return StreamInterface Returns the body as a stream.
285+
*/
286+
public function getBody()
287+
{
288+
return $this->body;
289+
}
290+
291+
/**
292+
* Return an instance with the specified message body.
293+
*
294+
* The body MUST be a StreamInterface object.
295+
*
296+
* This method MUST be implemented in such a way as to retain the
297+
* immutability of the message, and MUST return a new instance that has the
298+
* new body stream.
299+
*
300+
* @param StreamInterface $body Body.
301+
* @return static
302+
* @throws \InvalidArgumentException When the body is not valid.
303+
*/
304+
public function withBody(StreamInterface $body)
305+
{
306+
$clone = clone $this;
307+
$clone->body = $body->__toString();
308+
return $clone;
309+
}
310+
}

0 commit comments

Comments
 (0)