New in Symfony 4.1: HTTP header improvements

Introduced a HeaderUtils class

Contributed by
Christian Schmidt
in #24699.

Parsing HTTP headers is not as trivial as some may think. It requires parsing quoted strings with backslash escaping and ignoring white-space in certain places. We did that in some methods of the HttpFoundation component but the repeated logic was starting to make the code hard to maintain.

That's why in Symfony 4.1 we've introduced a new HeaderUtils class that provides the most common utilities needed when parsing HTTP headers. This is not an internal class, so you can use it in your own code too:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
use Symfony\Component\HttpFoundation\HeaderUtils;

// Splits an HTTP header by one or more separators
HeaderUtils::split('da, en-gb;q=0.8', ',;')
// => array(array('da'), array('en-gb'), array('q', '0.8'))

// Combines an array of arrays into one associative array
HeaderUtils::combine(array(array('foo', 'abc'), array('bar')))
// => array('foo' => 'abc', 'bar' => true)

// Joins an associative array into a string for use in an HTTP header
HeaderUtils::toString(array('foo' => 'abc', 'bar' => true, 'baz' => 'a b c'), ',')
// => 'foo=abc, bar, baz="a b c"'

// Encodes a string as a quoted string, if necessary
HeaderUtils::quote('foo "bar"')
// => 'foo \"bar\"'

// Decodes a quoted string
HeaderUtils::unquote('foo \"bar\"')
// => 'foo "bar"'

Allow to bypass headers when submitting forms in tests

Contributed by
cfjulien
in #26791.

An issue reported by the Mink browser testing project made us realize that you cannot bypass HTTP header information when submitting forms in tests which use the BrowserKit component.

That's why in Symfony 4.1 the submit() method now accepts a third optional argument called $serverParameters which allows you to do things like this:

1
2
3
4
$crawler = $client->request('GET', 'http://www.example.com/foo');
$form = $crawler->filter('input')->form();
$client->submit($form, [], ['HTTP_ACCEPT_LANGUAGE' => 'de']);
// => $client->getRequest()->getServer()['HTTP_ACCEPT_LANGUAGE'] = 'de'

Added support for default values in Accept headers

Contributed by
Javier Eguiluz
in #26036.

When using the Accept HTTP header it's common to use expressions like .../*, */* and even * to define the default values:

1
Accept: text/plain;q=0.5, text/html, text/*;q=0.8, */*

However, in Symfony versions previous to 4.1 these default values weren't supported:

1
2
3
4
5
6
use Symfony\Component\HttpFoundation\AcceptHeader;

$acceptHeader = AcceptHeader::fromString('text/plain;q=0.5, text/html, text/*;q=0.8, */*');
$quality = $acceptHeader->get('text/xml')->getQuality();
// instead of returning '0.8', this code displays the following error message:
//   Call to a member function getQuality() on null

In Symfony 4.1 all these default values are now properly supported:

1
2
3
4
$acceptHeader = AcceptHeader::fromString('text/plain;q=0.5, text/html, text/*;q=0.8, */*');
$acceptHeader->get('text/xml')->getQuality();        // => 0.8 (because of text/*)
$acceptHeader->get('text/html')->getQuality();       // => 1.0
$acceptHeader->get('application/xml')->getQuality(); // => 1.0 (because of */*)

Comments

Great ! Thankx you for this tool :)
This looks really useful, thanks!
Nice,
Why '$acceptHeader->get('text/x-dvi')->getQuality();' is 1.0? I think he should match to text/*
@Eugeniy you are right! I've fixed the last example. Thanks!
These are some really nice improvements! 👏
HeaderUtils::joinAssoc(array('foo' => 'abc', 'bar' => true, 'baz' => 'a b c'), ',')
// => 'foo=bar, baz, baz="a b c"'

Shouldn't the result be : foo=abc, bar, baz="a b c" ?
@Thierry yes! We've fixed that and other minor errors in the post. Thank you!
Thanks to all three contributors there are really nice feature!

Comments are closed.

To ensure that comments stay relevant, they are closed for old posts.