Skip to content

Commit

Permalink
Re-introducing the DataType enum
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Dec 23, 2023
1 parent 074afd2 commit d07543b
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 108 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ All Notable changes to `bakame/http-strucured-fields` will be documented in this
- `ByteSequence::tryFromEncoded`
- `Token::tryFromString`
- `OuterList::fromPairs`
- Adding functional API via `http_parse_sf` and `http_build_sf`
- `DataType` is now part of the public API

### Fixed

Expand Down
122 changes: 97 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,31 @@ 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;
use Bakame\Http\StructuredFields\OuterList;
use Bakame\Http\StructuredFields\DataType;
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 = http_parse_structured_field('list', $headerValue);
$field = DataType::List->parse($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 http_build_structured_field('list', [
echo DataType::List->build[
[
['foo', 'bar'],
[
'expire' => $expire,
'path' => '/',
'max-age' => 2500,
'secure' => true,
'httponly' => true,
'samesite' => Token::fromString('lax'),
['expire', $expire],
['path', '/'],
[ 'max-age', 2500],
['secure', true],
['httponly', true],
['samesite', Token::fromString('lax')],
]
]
]);
],
]),
// returns ("foo" "bar");expire=@1681504328;path="/";max-age=2500;secure;httponly=?0;samesite=lax
```

Expand All @@ -65,22 +64,68 @@ header. Content validation is out of scope for this library.

### Parsing and Serializing Structured Fields

#### Basic usage
#### Basic Usage

> [!NOTE]
> New in version 1.2.0
Parsing the header value is done via the `fromHttpValue` named constructor.
The method is attached to each library's structured fields representation
as shown below:
The `DataType` enum serves as a factory class to quickly parse and build a structured field.
The enum list all available data type according to the RFC. To parse a header you need to
give the `parse` method a string or a stringable object. On success, it will return a
`Bakame\Http\StruncturedFields\StruncturedField` implementing object otherwise an
exception will be thrown.

```php
$headerLine = 'bar;baz=42'; //the raw header line is a structured field item
$field = DataType::Item->parse($headerLine);
$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.
```

On the other hand, `build` method expects an iterable structure composed
of pair values that matches each data type and returns the structured field text representation
of the header.

```php
use Bakame\Http\StructuredFields\Item;
use Bakame\Http\StructuredFields\DataType;

$expire = Item::fromDateString('+30 minutes');
$data = [
[
'dumela lefatshe',
[['a', false]]
],
[
['a', 'b', $expire],
[['a', true]]
],
];
echo DataType::List->build($data);
// display "dumela lefatshe";a=?0, ("a" "b" @1703319068);a
```

The data type can be given as a string or using the `DataType` enum.

#### Using specific named constructor

The package provides specific classes for each data type. if you do not wish to
use the `DataType` factoring, parsing the header value is done via the
`fromHttpValue` named constructor. The method is attached to each library's
structured fields representation as shown below:

```php
declare(strict_types=1);

use Bakame\Http\StructuredFields\DataType;

require 'vendor/autoload.php';

// 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 = parse($headerLine, 'item');
$field = Item::fromHttpValue($headerLine);
$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 @@ -91,10 +136,7 @@ compliant HTTP field string value. To ease integration, the `__toString` method
implemented as an alias to the `toHttpValue` method.

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

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

Expand All @@ -104,8 +146,6 @@ 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 @@ -211,9 +251,10 @@ To ease validation a `Type::equals` method is exposed to check if the `Item` has
the expected type. It can also be used to compare types.

```php
use Bakame\Http\StructuredFields\DataType;
use Bakame\Http\StructuredFields\Type;

$field = Item::fromHttpValue('"foo"');
$field = DataType::Item->parse('"foo"');
Type::Date->equals($field); // returns false
Type::String->equals($field); // returns true;
Type::Boolean->equals(Type::String); // returns false
Expand Down Expand Up @@ -302,7 +343,7 @@ if you try to use them on any container object:
```php
use Bakame\Http\StructuredFields\Parameters;

$value = Parameters::fromHttpValue(';a=foobar']);
$value = Parameters::fromHttpValue(';a=foobar');
$value->has('b'); // return false
$value['a']->value(); // return 'foobar'
$value['b']; // triggers a InvalidOffset exception, the index does not exist
Expand Down Expand Up @@ -613,6 +654,37 @@ echo $list->toHttpValue(); //'(:SGVsbG8gV29ybGQ=: 42.0 42)'
echo $list; //'(:SGVsbG8gV29ybGQ=: 42.0 42)'
```

> [!NOTE]
> New in version 1.2.0
It is also possible to create an `OuterList` based on an iterable structure
of pairs.

```php
use Bakame\Http\StructuredFields\OuterList;

$list = OuterList::fromPairs([
[
['foo', 'bar'],
[
['expire', $expire],
['path', '/'],
[ 'max-age', 2500],
['secure', true],
['httponly', true],
['samesite', Token::fromString('lax')],
]
],
[
'coucoulesamis',
[['a', false]],
]
]);
```

The pairs definitions are the same as for creating either a `InnerList` or an `Item` using
their respective `fromPair` method.

#### Adding and updating parameters

To ease working with instances that have a `Parameters` object attached to, the following
Expand Down
3 changes: 0 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@
"symfony/var-dumper": "^6.4.0"
},
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"Bakame\\Http\\StructuredFields\\": "src/"
}
Expand Down
4 changes: 2 additions & 2 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +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
- message: '#Method Bakame\\Http\\StructuredFields\\DataType::build\(\) has parameter \$data with no value type specified in iterable type iterable.#'
path: src/DataType.php
reportUnmatchedIgnoredErrors: true
44 changes: 44 additions & 0 deletions src/DataType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Bakame\Http\StructuredFields;

use Stringable;

enum DataType: string
{
case Item = 'item';
case Parameters = 'parameters';
case InnerList = 'innerlist';
case List = 'list';
case Dictionary = 'dictionary';

/**
* @throws StructuredFieldError
*/
public function parse(Stringable|string $httpValue): StructuredField
{
return match ($this) {
self::Dictionary => Dictionary::fromHttpValue($httpValue),
self::Parameters => Parameters::fromHttpValue($httpValue),
self::List => OuterList::fromHttpValue($httpValue),
self::InnerList => InnerList::fromHttpValue($httpValue),
self::Item => Item::fromHttpValue($httpValue),
};
}

/**
* @throws StructuredFieldError
*/
public function build(iterable $data): string
{
return match ($this) {
self::Dictionary => Dictionary::fromPairs($data)->toHttpValue(),
self::Parameters => Parameters::fromPairs($data)->toHttpValue(),
self::List => OuterList::fromPairs($data)->toHttpValue(),
self::InnerList => InnerList::fromPair([...$data])->toHttpValue(), /* @phpstan-ignore-line */
self::Item => Item::fromPair([...$data])->toHttpValue(), /* @phpstan-ignore-line */
};
}
}
22 changes: 11 additions & 11 deletions src/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
/**
* @see https://www.rfc-editor.org/rfc/rfc8941.html#section-3.3
*/
enum Type
enum Type: string
{
case Integer;
case Decimal;
case String;
case Token;
case ByteSequence;
case DisplayString;
case Boolean;
case Date;
case Integer = 'integer';
case Decimal = 'decimal';
case String = 'string';
case Token = 'token';
case ByteSequence = 'bytesequence';
case DisplayString = 'displaystring';
case Boolean = 'boolean';
case Date = 'date';

public function equals(mixed $other): bool
{
Expand Down Expand Up @@ -48,9 +48,9 @@ public static function tryFromValue(mixed $value): self|null
is_float($value) => Type::Decimal,
is_bool($value) => Type::Boolean,
is_string($value) => match (true) {
1 === preg_match('/[^\x20-\x7f]/', $value) => Type::DisplayString,
1 === preg_match("/^([a-z*][a-z\d:\/!#\$%&'*+\-.^_`|~]*)$/i", $value) => Type::Token,
null !== Token::tryFromString($value) => Type::Token,
null !== ByteSequence::tryFromEncoded($value) => Type::ByteSequence,
1 === preg_match('/[^\x20-\x7f]/', $value) => Type::DisplayString,
default => Type::String,
},
default => null,
Expand Down
59 changes: 0 additions & 59 deletions src/functions.php

This file was deleted.

Loading

0 comments on commit d07543b

Please sign in to comment.