Skip to content

Commit b1bf520

Browse files
committed
Merge branch 'cleanup'
2 parents b5875cd + 011ce87 commit b1bf520

File tree

5 files changed

+103
-26
lines changed

5 files changed

+103
-26
lines changed

src/Http/Auth/AccessToken.php

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Lkrms\Contract\IImmutable;
77
use Lkrms\Contract\IReadable;
88
use Lkrms\Exception\InvalidArgumentException;
9+
use Lkrms\Http\Contract\IAccessToken;
910
use Lkrms\Utility\Date;
1011
use DateTimeImmutable;
1112
use DateTimeInterface;
@@ -19,34 +20,25 @@
1920
* @property-read string[] $Scopes
2021
* @property-read array<string,mixed> $Claims
2122
*/
22-
final class AccessToken implements IReadable, IImmutable
23+
final class AccessToken implements IAccessToken, IImmutable, IReadable
2324
{
2425
use TFullyReadable;
2526

26-
/**
27-
* @var string
28-
*/
29-
protected $Token;
27+
protected string $Token;
3028

31-
/**
32-
* @var string
33-
*/
34-
protected $Type;
29+
protected string $Type;
3530

36-
/**
37-
* @var DateTimeImmutable|null
38-
*/
39-
protected $Expires;
31+
protected ?DateTimeImmutable $Expires;
4032

4133
/**
4234
* @var string[]
4335
*/
44-
protected $Scopes;
36+
protected array $Scopes;
4537

4638
/**
4739
* @var array<string,mixed>
4840
*/
49-
protected $Claims;
41+
protected array $Claims;
5042

5143
/**
5244
* Creates a new AccessToken object
@@ -57,8 +49,13 @@ final class AccessToken implements IReadable, IImmutable
5749
* @param string[]|null $scopes
5850
* @param array<string,mixed>|null $claims
5951
*/
60-
public function __construct(string $token, string $type, $expires, ?array $scopes = null, ?array $claims = null)
61-
{
52+
public function __construct(
53+
string $token,
54+
string $type,
55+
$expires,
56+
?array $scopes = null,
57+
?array $claims = null
58+
) {
6259
if (is_int($expires) && $expires < 0) {
6360
throw new InvalidArgumentException(sprintf(
6461
'Invalid $expires: %d',
@@ -76,4 +73,20 @@ public function __construct(string $token, string $type, $expires, ?array $scope
7673
$this->Scopes = $scopes ?: [];
7774
$this->Claims = $claims ?: [];
7875
}
76+
77+
/**
78+
* @inheritDoc
79+
*/
80+
public function getToken(): string
81+
{
82+
return $this->Token;
83+
}
84+
85+
/**
86+
* @inheritDoc
87+
*/
88+
public function getType(): string
89+
{
90+
return $this->Type;
91+
}
7992
}

src/Http/Contract/IAccessToken.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Lkrms\Http\Contract;
4+
5+
interface IAccessToken
6+
{
7+
/**
8+
* Get the object's token string
9+
*/
10+
public function getToken(): string;
11+
12+
/**
13+
* Get the token's type, e.g. "Bearer"
14+
*/
15+
public function getType(): string;
16+
}

src/Http/Contract/IHttpHeaders.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Lkrms\Contract\Arrayable;
66
use Lkrms\Contract\ICollection;
77
use Lkrms\Contract\IImmutable;
8+
use Lkrms\Http\Catalog\HttpHeader;
89

910
/**
1011
* A collection of HTTP headers
@@ -51,13 +52,23 @@ public function set($key, $value);
5152
public function unset($key);
5253

5354
/**
54-
* Apply values to headers from an array or Traversable, optionally
55-
* preserving existing values
55+
* Merge the collection with the given headers, optionally preserving
56+
* existing values
5657
*
5758
* @param Arrayable<string,string[]|string>|iterable<string,string[]|string> $items
5859
*/
5960
public function merge($items, bool $preserveExisting = false);
6061

62+
/**
63+
* Apply an access token to the collection
64+
*
65+
* @return static
66+
*/
67+
public function authorize(
68+
IAccessToken $token,
69+
string $headerName = HttpHeader::AUTHORIZATION
70+
);
71+
6172
/**
6273
* Reduce the collection to headers received after the HTTP message body
6374
*

src/Http/HttpHeaders.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
use Lkrms\Concern\Immutable;
66
use Lkrms\Concern\ImmutableArrayAccess;
77
use Lkrms\Concern\TReadableCollection;
8-
use Lkrms\Contract\Arrayable;
98
use Lkrms\Contract\ICollection;
109
use Lkrms\Exception\InvalidArgumentException;
10+
use Lkrms\Http\Catalog\HttpHeader;
11+
use Lkrms\Http\Contract\IAccessToken;
1112
use Lkrms\Http\Contract\IHttpHeaders;
1213
use Lkrms\Support\Catalog\RegularExpression as Regex;
1314
use Lkrms\Utility\Arr;
@@ -69,7 +70,7 @@ public function addLine(string $line, bool $strict = false)
6970

7071
if ($line === "\r\n" || (!$strict && trim($line) === '')) {
7172
if ($strict && $this->Closed) {
72-
throw new InvalidArgumentException('HTTP message cannot have empty line after body');
73+
throw new InvalidArgumentException('HTTP message cannot have empty header after body');
7374
}
7475
return $this->with('Closed', true)->with('Carry', null);
7576
}
@@ -250,6 +251,19 @@ public function merge($items, bool $preserveExisting = false)
250251
return $this->replaceHeaders($headers, $index);
251252
}
252253

254+
/**
255+
* @inheritDoc
256+
*/
257+
public function authorize(
258+
IAccessToken $token,
259+
string $headerName = HttpHeader::AUTHORIZATION
260+
) {
261+
return $this->set(
262+
$headerName,
263+
sprintf('%s %s', $token->getType(), $token->getToken())
264+
);
265+
}
266+
253267
/**
254268
* @inheritDoc
255269
*/
@@ -285,21 +299,19 @@ public function filter(callable $callback, int $mode = ICollection::CALLBACK_USE
285299
$changed = false;
286300

287301
foreach ($index as $nextLower => $headerIndex) {
288-
$nextKey = null;
289302
$nextValue = null;
290303
foreach ($headerIndex as $i) {
291304
$header = $this->Headers[$i];
292305
$value = reset($header);
293-
$nextKey ??= key($header);
294306
if ($mode === ICollection::CALLBACK_USE_KEY) {
295307
break;
296308
}
297309
$nextValue[] = $value;
298310
}
299311
$next = $mode === ICollection::CALLBACK_USE_KEY
300-
? $nextKey
312+
? $nextLower
301313
: ($mode === ICollection::CALLBACK_USE_BOTH
302-
? [$nextKey => $nextValue]
314+
? [$nextLower => $nextValue]
303315
: $nextValue);
304316
if ($count++) {
305317
if (!$callback($item, $next, $prev)) {

tests/unit/Support/Http/HttpHeadersTest.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
namespace Lkrms\Tests\Support\Http;
44

5+
use Lkrms\Contract\ICollection;
56
use Lkrms\Exception\InvalidArgumentException;
7+
use Lkrms\Http\Auth\AccessToken;
68
use Lkrms\Http\Catalog\HttpHeader;
9+
use Lkrms\Http\Catalog\HttpHeaderGroup;
710
use Lkrms\Http\HttpHeaders;
811
use Lkrms\Support\Catalog\MimeType;
12+
use Lkrms\Utility\Arr;
913
use LogicException;
1014

1115
final class HttpHeadersTest extends \Lkrms\Tests\TestCase
@@ -160,7 +164,7 @@ public static function addLineProvider(): array
160164
true,
161165
],
162166
'[strict] trailing header #4' => [
163-
InvalidArgumentException::class . ',HTTP message cannot have empty line after body',
167+
InvalidArgumentException::class . ',HTTP message cannot have empty header after body',
164168
["foo: bar\r\n", "\r\n", "baz: qux\r\n", "\r\n"],
165169
true,
166170
true,
@@ -258,6 +262,27 @@ public function testEmptyValue(): void
258262
);
259263
}
260264

265+
public function testFilter(): void
266+
{
267+
$index = Arr::toIndex(Arr::lower(HttpHeaderGroup::SENSITIVE));
268+
$token = new AccessToken('foo.bar.baz', 'Bearer', time() + 3600);
269+
$headers = (new HttpHeaders())
270+
->authorize($token)
271+
->set(HttpHeader::ACCEPT, '*/*');
272+
$this->assertSame([
273+
'Authorization' => ['Bearer foo.bar.baz'],
274+
'Accept' => ['*/*'],
275+
], $headers->getHeaders());
276+
$headers = $headers
277+
->filter(
278+
fn(string $key) => !($index[$key] ?? false),
279+
ICollection::CALLBACK_USE_KEY
280+
);
281+
$this->assertSame([
282+
'Accept' => ['*/*'],
283+
], $headers->getHeaders());
284+
}
285+
261286
public function testOffsetSet(): void
262287
{
263288
$headers = new HttpHeaders();

0 commit comments

Comments
 (0)