Aura.Accept

Provides content-negotiation tools using Accept* headers.

Foreword

Installation

This library requires PHP 5.3 or later; we recommend using the latest available version of PHP as a matter of principle. It has no userland dependencies.

It is installable and autoloadable via Composer as aura/accept.

Alternatively, download a release or clone this repository, then require or include its autoload.php file.

Quality

Scrutinizer Code Quality Code Coverage Build Status

To run the PHPUnit unit tests at the command line, issue composer install and then vendor/bin/phpunit at the package root. This requires Composer to be available as composer.

This library attempts to comply with PSR-1, PSR-2, and PSR-4. If you notice compliance oversights, please send a patch via pull request.

Community

To ask questions, provide feedback, or otherwise communicate with the Aura community, please join our Google Group, follow @auraphp on Twitter, or chat with us on #auraphp on Freenode.

Getting Started

Instantiation

First, instantiate a AcceptFactory object, then use it to create an Accept object.

<?php
use Aura\Accept\AcceptFactory;

$accept_factory = new AcceptFactory($_SERVER);
$accept = $accept_factory->newInstance();
?>

The Accept object provides convenienence methods to negotiate between acceptable (to the client) and available (from the application) charset, encoding, language, and media-type values. The methods are:

  • negotiateCharset()
  • negotiateEncoding()
  • negotiateLanguage()
  • negotiateMedia()

Your application code, knowing what values it has available, should pass an array of the available values to the negotiate*() method. (The values that are acceptable to the client are already indicated by $_SERVER).

The result returned from the method will be a *Value object describing the highest-quality match that was negotiated between the available and acceptable values. If there is no negotiable match, the result will be false.

N.b. Accept headers can be kind of complicated. See the HTTP Header Field Definitions for more detailed information regarding quality factors, matching rules, and parameters extensions.

Negotiating Media Types

To negotiate a media type (aka a content type), call the negotiateMedia() method with a list of available media types. The available types should be in the order you prefer for delivery, from "most preferred" to "least preferred".

<?php
// assume the request indicates these Accept values (XML is best, then CSV,
// then anything else)
$_SERVER['HTTP_ACCEPT'] = 'application/xml;q=1.0,text/csv;q=0.5,*;q=0.1';

// create the accept factory
$accept_factory = new AcceptFactory($_SERVER);

// create the accept object
$accept = $accept_factory->newInstance();

// assume our application has `application/json` and `text/csv` available
// as media types, in order of highest-to-lowest preference for delivery
$available = array(
    'application/json',
    'text/csv',
);

// get the best match between what the request finds acceptable and what we
// have available; the result in this case is 'text/csv'
$media = $accept->negotiateMedia($available);
echo $media->getValue(); // text/csv
?>

If the requested URL ends in a recognized file extension for a media type, the MediaNegotiator object used by Accept will use that file extension instead of the explicit Accept header value to determine the acceptable media type:

<?php
// assume the request indicates these Accept values (XML is best, then CSV,
// then anything else)
$_SERVER['HTTP_ACCEPT'] = 'application/xml;q=1.0,text/csv;q=0.5,*;q=0.1';

// assume also that the request URI explicitly notes a .json file extension
$_SERVER['REQUEST_URI'] = '/path/to/entity.json';

// create the accept factory
$accept_factory = new AcceptFactory($_SERVER);

// factory the accept object
$accept = $accept_factory->newInstance();

// assume our application has `application/json` and `text/csv` available
// as media types, in order of highest-to-lowest preference for delivery
$available = array(
    'application/json',
    'text/csv',
);

// get the best match between what the request finds acceptable and what we
// have available; the result in this case is 'application/json' because of
// the file extenstion overriding the Accept header values
$media = $accept->negotiateMedia($available);
echo $media->getValue(); // application/json
?>

(See the MediaNegotiator class file for the list of what file extensions map to what media types.)

To create your own mappings, set them into the AcceptFactory object at construction time:

<?php
$accept_factory = new AcceptFactory($_SERVER, array(
    '.foo' => 'application/x-foo-content-type',
));

$accept = $accept_factory->newInstance();
?>

If the acceptable values indicate additional parameters, you can match on those as well:

<?php
// assume the request indicates these Accept values (XML is best, then CSV,
// then anything else)
$_SERVER['HTTP_ACCEPT'] = 'text/html;level=1;q=0.5,text/html;level=3';

// create the accept factory
$accept_factory = new AcceptFactory($_SERVER);

// factory the accept object
$accept = $accept_factory->newInstance();

// assume our application has these available as media types,
// in order of highest-to-lowest preference for delivery
$available = array(
    'text/html;level=1',
    'text/html;level=2',
);

// get the best match between what the request finds acceptable and what we
// have available; the result in this case is 'text/html;level=1'
$media = $accept->negotiateMedia($available);
echo $media->getValue(); // text/html
var_dump($media->getParameters()); // array('level' => '1')
?>

N.b. Parameters in the acceptable values that are not present in the available values will not be used for matching.

Negotiating Other Values

The other negotiation methods work much the same way, although they are less complex than media-type negotiation.

<?php
// assume the request indicates these Accept-* values
$_SERVER['HTTP_ACCEPT_CHARSET'] = 'iso-8859-5, unicode-1-1;q=0.8';
$_SERVER['HTTP_ACCEPT_ENCODING'] = 'compress;q=0.5, gzip;q=1.0';
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en-US, en-GB, en, *';

// create the accept factory
$accept_factory = new AcceptFactory($_SERVER);

// factory the accept object
$accept = $accept_factory->newInstance();

// charset negotiation
$available_charsets = array('iso-1234', 'unicode-1-1');
$charset = $accept->negotiateCharset($available_charsets);
echo $charset->getValue('unicode-1-1');

// encoding negotiation
$available_encodings = array();
$encoding = $accept->negotiateEncoding($available_encodings);
var_dump($encoding); // false

// language negotiation
$available_languages = array('pt-BR', 'fr-FR');
$language = $accept->negotiateLanguage($available_languages);
echo $language->getValue(); // pt-BR
?>