Skip to content

Commit

Permalink
Merge pull request #8 from bakame-php/feature/add-display-string-type
Browse files Browse the repository at this point in the history
Adding support for the DisplayString type
  • Loading branch information
nyamsprod authored Dec 23, 2023
2 parents 5db7d59 + 074afd2 commit df70dde
Show file tree
Hide file tree
Showing 31 changed files with 667 additions and 136 deletions.
2 changes: 1 addition & 1 deletion .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
'import_constants' => true,
'import_functions' => true,
],
'new_with_braces' => true,
'new_with_parentheses' => true,
'no_blank_lines_after_phpdoc' => true,
'no_empty_phpdoc' => true,
'no_empty_comment' => true,
Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this

### Added

- None
- Support for the `DisplayString` type
- `ByteSequence::tryFromEncoded`
- `Token::tryFromString`
- `OuterList::fromPairs`
- Adding functional API via `http_parse_sf` and `http_build_sf`

### Fixed

- Tests file moved under the `/tests` directory
- Fix `Type::tryFromValue` to correctly detect string type derivative.

### Deprecated

Expand Down
81 changes: 49 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,32 @@ build and update HTTP Structured Fields in PHP according to the [RFC8941](https:
Once installed you will be able to do the following:

```php
use Bakame\Http\StructuredFields\{InnerList, Item, OuterList, Token};
use Bakame\Http\StructuredFields\InnerList;
use Bakame\Http\StructuredFields\OuterList;
use Bakame\Http\StructuredFields\Token;

//1 - parsing an Accept Header
$headerValue = 'text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8';
$field = OuterList::fromHttpValue($headerValue);
$field = http_parse_structured_field('list', $headerValue);
$field[2]->value()->toString(); // returns 'application/xml'
$field[2]->parameter('q'); // returns (float) 0.9
$field[0]->value()->toString(); // returns 'text/html'
$field[0]->parameter('q'); // returns null

//2 - building a Retrofit Cookie Header
echo OuterList::new(
InnerList::fromAssociative(['foo', 'bar'], [
echo http_build_structured_field('list', [
[
['foo', 'bar'],
[
'expire' => $expire,
'path' => '/',
'max-age' => 2500,
'secure' => true,
'httponly' => true,
'samesite' => Token::fromString('lax'),
])
)
->toHttpValue();
]
]
]);
// returns ("foo" "bar");expire=@1681504328;path="/";max-age=2500;secure;httponly=?0;samesite=lax
```

Expand Down Expand Up @@ -72,13 +76,11 @@ declare(strict_types=1);

require 'vendor/autoload.php';

use Bakame\Http\StructuredFields\Item;

// the raw HTTP field value is given by your application
// via any given framework, package or super global.

$headerLine = 'bar;baz=42'; //the raw header line is a structured field item
$field = Item::fromHttpValue($headerLine);
$field = parse($headerLine, 'item');
$field->value(); // returns Token::fromString('bar); the found token value
$field->parameter('baz'); // returns 42; the value of the parameter or null if the parameter is not defined.
```
Expand All @@ -89,9 +91,10 @@ compliant HTTP field string value. To ease integration, the `__toString` method
implemented as an alias to the `toHttpValue` method.

````php
use Bakame\Http\StructuredFields\Item;
use function Bakame\Http\StructuredFields\http_sf_parse;
use function Bakame\Http\StructuredFields\http_sf_build;

$field = Item::fromHttpValue('bar; baz=42; secure=?1');
$field = http_sf_parse('bar; baz=42; secure=?1', 'item');
echo $field->toHttpValue(); // return 'bar;baz=42;secure'
// on serialization the field has been normalized

Expand All @@ -101,6 +104,8 @@ echo $field->toHttpValue(); // return 'bar;baz=42;secure'
header('foo: '. $field->toHttpValue());
//or
header('foo: '. $field);
//or
header('foo: '. http_sf_build($field));
````

All five (5) structured data type as defined in the RFC are provided inside the
Expand Down Expand Up @@ -176,15 +181,16 @@ Per the RFC, items can have different types that are translated to PHP using:

The table below summarizes the item value type.

| RFC Type | PHP Type | Package Enum Type |
|---------------|---------------------------|----------------------|
| Integer | `int` | `Type::Integer` |
| Decimal | `float` | `Type::Decimal` |
| String | `string` | `Type::String` |
| Boolean | `bool` | `Type::Boolean` |
| Token | class `Token` | `Type::Token` |
| Byte Sequence | class `ByteSequence` | `Type::ByteSequence` |
| Date | class `DateTimeImmutable` | `Type::Date` |
| RFC Type | PHP Type | Package Enum Type |
|---------------|---------------------------|-----------------------|
| Integer | `int` | `Type::Integer` |
| Decimal | `float` | `Type::Decimal` |
| String | `string` | `Type::String` |
| Boolean | `bool` | `Type::Boolean` |
| Token | class `Token` | `Type::Token` |
| Byte Sequence | class `ByteSequence` | `Type::ByteSequence` |
| Date | class `DateTimeImmutable` | `Type::Date` |
| DisplayString | class `DisplayString` | `Type::DisplayString` |

The Enum `Type` which list all available types can be use to determine the RFC type
corresponding to a PHP structure using the `Type::fromValue` static method.
Expand Down Expand Up @@ -213,9 +219,9 @@ Type::String->equals($field); // returns true;
Type::Boolean->equals(Type::String); // returns false
```

The RFC defines two (2) specific data types that can not be represented by
PHP default type system, for them, we have defined two classes `Token`
and `ByteSequence` to help with their representation.
The RFC defines three (3) specific data types that can not be represented by
PHP default type system, for them, we have defined three classes `Token`,
`ByteSequence` and `DisplayString` to help with their representation.

```php
use Bakame\Http\StructuredFields\Token;
Expand All @@ -224,31 +230,40 @@ use Bakame\Http\StructuredFields\ByteSequence;
Token::fromString(string|Stringable $value): Token
ByteSequence::fromDecoded(string|Stringable $value): ByteSequence;
ByteSequence::fromEncoded(string|Stringable $value): ByteSequence;
DisplayString::fromDecoded(string|Stringable $value): DisplayString;
DisplayString::fromEncoded(string|Stringable $value): DisplayString;
```

Both classes are final and immutable; their value can not be modified once
All classes are final and immutable; their value can not be modified once
instantiated. To access their value, they expose the following API:

```php
use Bakame\Http\StructuredFields\Token;
use Bakame\Http\StructuredFields\ByteSequence;
use Bakame\Http\StructuredFields\DisplayString;

$token = Token::fromString('application/text+xml');
echo $token->toString(); // returns 'application/text+xml'

$byte = ByteSequence::fromDecoded('Hello world!');
$byte = DisplayString::fromDecoded('füü');
$byte->decoded(); // returns 'füü'
$byte->encoded(); // returns 'f%c3%bc%c3%bc'

$displayString = ByteSequence::fromDecoded('Hello world!');
$byte->decoded(); // returns 'Hello world!'
$byte->encoded(); // returns 'SGVsbG8gd29ybGQh'

$token->equals($byte); // will return false;
$displayString->equals($byte); // will return false;
$byte->equals(ByteSequence::fromEncoded('SGVsbG8gd29ybGQh')); // will return true

$token->type(); // returns Type::Token enum
$byte->type(); // returns Type::ByteSequence
$displayString->type(); // returns Type::DisplayString
```

> [!WARNING]
> Both classes DO NOT expose the `Stringable` interface to distinguish them
> The classes DO NOT expose the `Stringable` interface to distinguish them
from a string or a string like object

#### Item
Expand Down Expand Up @@ -316,11 +331,11 @@ use Bakame\Http\StructuredFields\InnerList;
use Bakame\Http\StructuredFields\Item;
use Bakame\Http\StructuredFields\Parameters;

$field->parameter(string $key): ByteSequence|Token|DateTimeImmutable|Stringable|string|int|float|bool|null;
$field->parameter(string $key): ByteSequence|Token|DisplayString|DateTimeImmutable|Stringable|string|int|float|bool|null;
$field->parameters(): Parameters;
$field->parameterByIndex(int $index): array{0:string, 1:ByteSequence|Token|DateTimeImmutable|Stringable|string|int|float|boo}|array{}
$field->parameterByIndex(int $index): array{0:string, 1:ByteSequence|Token|DisplayString|DateTimeImmutable|Stringable|string|int|float|boo}|array{}
InnerList::toPair(): array{0:list<Item>, 1:Parameters}>};
Item::toPair(): array{0:ByteSequence|Token|DateTimeImmutable|Stringable|string|int|float|bool, 1:Parameters}>};
Item::toPair(): array{0:ByteSequence|Token|DisplayString|DateTimeImmutable|Stringable|string|int|float|bool, 1:Parameters}>};
```

> [!NOTE]
Expand All @@ -343,8 +358,10 @@ use Bakame\Http\StructuredFields\ByteSequence;
use Bakame\Http\StructuredFields\Item;
use Bakame\Http\StructuredFields\Token;

Item:new(DateTimeInterface|ByteSequence|Token|string|int|float|bool $value): self
Item:new(DateTimeInterface|ByteSequence|Token|DisplayString|string|int|float|bool $value): self
Item::fromDecodedByteSequence(Stringable|string $value): self;
Item::fromEncodedDisplayString(Stringable|string $value): self;
Item::fromDecodedDisplayString(Stringable|string $value): self;
Item::fromEncodedByteSequence(Stringable|string $value): self;
Item::fromToken(Stringable|string $value): self;
Item::fromString(Stringable|string $value): self;
Expand All @@ -363,7 +380,7 @@ To update the `Item` instance value, use the `withValue` method:
```php
use Bakame\Http\StructuredFields\Item;

Item::withValue(DateTimeInterface|ByteSequence|Token|string|int|float|bool $value): static
Item::withValue(DateTimeInterface|ByteSequence|Token|DisplayString|string|int|float|bool $value): static
```

#### Ordered Maps
Expand Down
17 changes: 10 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@
"require-dev": {
"friendsofphp/php-cs-fixer": "^v3.15.1",
"httpwg/structured-field-tests": "*@dev",
"phpstan/phpstan": "^1.10.34",
"phpstan/phpstan-strict-rules": "^1.5.1",
"phpstan/phpstan-phpunit": "^1.3.14",
"phpstan/phpstan": "^1.10.50",
"phpstan/phpstan-strict-rules": "^1.5.2",
"phpstan/phpstan-phpunit": "^1.3.15",
"phpstan/phpstan-deprecation-rules": "^1.1.4",
"phpunit/phpunit": "^10.3.4",
"phpbench/phpbench": "^1.2.14",
"symfony/var-dumper": "^6.3.4"
"phpunit/phpunit": "^10.5.3",
"phpbench/phpbench": "^1.2.15",
"symfony/var-dumper": "^6.4.0"
},
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"Bakame\\Http\\StructuredFields\\": "src/"
}
Expand Down Expand Up @@ -77,7 +80,7 @@
"source": {
"url": "https://github.com/httpwg/structured-field-tests.git",
"type": "git",
"reference": "main"
"reference": "mnot/display-string"
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ parameters:
ignoreErrors:
- message: '#it_fails_to_create_an_item_from_an_array_of_pairs\(\)#'
path: tests/ItemTest.php
- message: '#Function http_build_structured_field\(\) has parameter \$data with no value type specified in iterable type iterable.#'
path: src/functions.php
reportUnmatchedIgnoredErrors: true
11 changes: 11 additions & 0 deletions src/ByteSequence.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Stringable;

use Throwable;

use function base64_decode;
use function base64_encode;
use function preg_match;
Expand Down Expand Up @@ -38,6 +40,15 @@ public static function fromEncoded(Stringable|string $encodedValue): self
return new self($decoded);
}

public static function tryFromEncoded(Stringable|string $encodedValue): ?self
{
try {
return self::fromEncoded($encodedValue);
} catch (Throwable) {
return null;
}
}

/**
* Returns a new instance from a raw decoded string.
*/
Expand Down
14 changes: 9 additions & 5 deletions src/Dictionary.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ public function pair(int $index): array
/**
* @param SfMember|SfMemberInput $member
*/
public function add(string $key, iterable|StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
public function add(string $key, iterable|StructuredField|Token|ByteSequence|DisplayString|DateTimeInterface|string|int|float|bool $member): static
{
$members = $this->members;
$members[MapKey::from($key)->value] = self::filterMember($member);
Expand Down Expand Up @@ -304,8 +304,10 @@ public function removeByKeys(string ...$keys): static
/**
* @param SfMember|SfMemberInput $member
*/
public function append(string $key, iterable|StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
{
public function append(
string $key,
iterable|StructuredField|Token|ByteSequence|DisplayString|DateTimeInterface|string|int|float|bool $member
): static {
$members = $this->members;
unset($members[$key]);

Expand All @@ -315,8 +317,10 @@ public function append(string $key, iterable|StructuredField|Token|ByteSequence|
/**
* @param SfMember|SfMemberInput $member
*/
public function prepend(string $key, iterable|StructuredField|Token|ByteSequence|DateTimeInterface|string|int|float|bool $member): static
{
public function prepend(
string $key,
iterable|StructuredField|Token|ByteSequence|DisplayString|DateTimeInterface|string|int|float|bool $member
): static {
$members = $this->members;
unset($members[$key]);

Expand Down
Loading

0 comments on commit df70dde

Please sign in to comment.