Parsing and Serializing HTTP Fields
Parsing fields
Processing HTTP Fields has never been easy as it was never standardized before. With structured fields this aspect
of processing HTTP fields is made predicable you will not have to re-invent the wheel for each new field.
To do so the RFC defines 3 main structures an HTTP fields can have. It can be a List, a Dictionary or
an Item. By specifying that your field is one of those structures you already gave the author the complete
process to how it should be parsed.
Structured Fields Data Types
If we go back to our first example, the permission policy field, it is defined as a Dictionary as such the
package will use the Dictionary parsing process to split the field accordingly.
use Bakame\Http\StructuredFields\Dictionary;
use Bakame\Http\StructuredFields\DataType;
$headerLine = 'picture-in-picture=(), geolocation=(self "https://example.com/"), camera=*';
$permission = DataType::Dictionary->parse($headerLine);
// can also be written as follows
$permission = Dictionary::fromHttpValue($headerLine);
//both call will return a Dictionary instance on success
The Dictionary class is an implementation of the structured field Dictionary data type. The package
provides a PHP implementation for each data type supported by the RFC. The following table summarizes
the data type system.
| RFC Type | PHP Type | Package Enum Name | Package Enum Value |
|---|---|---|---|
| List | class OuterList |
DataType::List |
list |
| Dictionary | class Dictionary |
DataType::Dictionary |
dictionary |
| Item | class Item |
DataType::Item |
item |
| InnerList | class InnerList |
DataType::InnerList |
innerlist |
| Parameters | class Parameters |
DataType::Parameters |
parameters |
As example the following existing headers can be classified within the Structured Fields:
- The
Keep-Aliveheader is aDictionary - The
Acceptheaders areList - The
Content-Typeheader is anItem
These examples are taken from a list of already supported fields
All the classes share the same methods for parsing the HTTP text representation of the field. They all use
the fromHttpValue named constructor. This method will parse the field string representation and
return an instantiated PHP class containing the parsing result. Because there are 2 RFC related
to structured fields, the method accepts an optional enum Ietf that indicates which RFC
should be used for conformance. If no enum value is provided, the method will fall back
to using the latest accepted RFC at the moment of its release: RFC9651.
use Bakame\Http\StructuredFields\OuterList;
use Bakame\Http\StructuredFields\Ietf;
$headerLine = '(), (self "https://example.com/");start=@123456789';
$field = OuterList::fromHttpValue($headerLine, Ietf::Rfc9651);
// will work
$field = OuterList::fromHttpValue($headerLine, Ietf::Rfc8941);
// will trigger a SyntaxError because the field syntax is invalid for RFC8941
Each construct also provides two syntactic sugar methods, the fromRfc9651 and fromRfc8941
named constructors, if you are more comfortable not using the Ietf enum.
use Bakame\Http\StructuredFields\Item;
use Bakame\Http\StructuredFields\Ietf;
$headerLine = '"https://example.com/";start=@123456789';
$field = Item::fromRfc9651($headerLine);
// will work
$field = Item::fromRfc8941($headerLine);
// will trigger a SyntaxError because the field syntax is invalid for RFC8941
Data Types relationships
Once parsed, each data type will expose its content depending on its specification. What’s
important to remember is that apart from the Item all the other data types are containers.
As such their data can be accessible via their indices (for all containers) and also via
their member name if the container represents an ordered map. The relation between each
construct can be summarized as follows:
DictionaryandOuterListinstances can only containItemandInnerList;InnerListandParametersinstances can only containItem;OuterListandInnerListmembers can only be accessed by their indices (ie: they are list);DictionaryandParametersmembers can also be accessed by their name (ie: they are ordered map);ItemandInnerListinstances can have aParameterscontainer attached to them.Itemcontain in aParameterscontainer can not have parameters attached to them to avoid recursion. They are named Bare Item.Itemcontain in aInnerListcontainer can have parameters attached to them.
Let’s use simple examples to illustrate those rules:
use Bakame\Http\StructuredFields\DataType;
$headerLine = 'picture-in-picture=(), geolocation=(self "https://example.com/"), camera=*';
$permissions = DataType::Dictionary->parse($headerLine); // parse the dictionary structured field
$permissions['picture-in-picture']->isEmpty(); // picture-in-picture returned value is an empty InnerList
count($permissions['geolocation']); // geolocation returned value is an InnerList of 3 Items
$permissions['geolocation'][1]->value(); // returns "https://example.com/"
$permissions['camera']->value(); // camera only value is a single Item
$permissions->getByIndex(2); // returns an array ['camera', Item::fromPair(['*', []])]
In the example above:
- We have no
Parametersclass used, - We see that a
Dictionaryfield only containsItemandInnerListcontainers - the
InnerListcontains solelyItemsthat can be accessible via their indices - the
Dictionaryvalues can be accessible via their names or thir indices.
The following field is an example from the Accept header which is already structured fields compliant.
$fieldValue = 'text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8';
$field = DataType::List->parse($fieldValue);
$field[2]->value()->toString(); // returns 'application/xml'
$field[2]->parameterByKey('q'); // returns (float) 0.9
$field[0]->value()->toString(); // returns 'text/html'
$field[0]->parameterByKey('q'); // returns null
- The Accept header is an
Listfield made ofItemonly. - each
Itemcan have an attachedParameterscontainer - Member of the
Parameterscontainer can be accessed by named from theItemthey are attached to.
Serializing fields
Each Data type implementation is able to convert itself into a proper HTTP text representation
using its toHttpValue method. The method is complementary to the fromHttpValue named constructor
in that it does the opposite of that method. Just like the fromHttpValue, it can take an optional
Ietf enum to specify which RFC version it should adhere to for serialization. And two syntactic
methods toRfc9651 and toRfc8941 are also present to ease usage. Last but not least, all classes
implements the Stringable interface using the latest accepted RFC.
$fieldValue = 'text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8';
$field = DataType::List->parse($fieldValue);
$field->toHttpValue();
(string) $field;
// both calls will return
// 'text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8';
The toHttpValue method applies by default all the normalizations recommended by the RFC.