From 1fda22c9654f022bf1f2f545e67828e2c339113d Mon Sep 17 00:00:00 2001 From: David Maus <maus@hab.de> Date: Thu, 19 Apr 2018 13:08:04 +0200 Subject: [PATCH] Verwende willdurand/negotiation --- .gitmodules | 3 + composer.json | 3 +- composer.lock | 54 ++++- vendor/composer/autoload_psr4.php | 1 + vendor/composer/autoload_static.php | 8 + vendor/composer/installed.json | 54 +++++ vendor/willdurand/negotiation/.gitignore | 2 + vendor/willdurand/negotiation/.travis.yml | 19 ++ vendor/willdurand/negotiation/CONTRIBUTING.md | 33 +++ vendor/willdurand/negotiation/LICENSE | 19 ++ vendor/willdurand/negotiation/README.md | 162 +++++++++++++++ vendor/willdurand/negotiation/appveyor.yml | 30 +++ vendor/willdurand/negotiation/composer.json | 27 +++ .../willdurand/negotiation/phpunit.xml.dist | 23 +++ .../src/Negotiation/AbstractNegotiator.php | 118 +++++++++++ .../negotiation/src/Negotiation/Accept.php | 46 +++++ .../src/Negotiation/AcceptCharset.php | 7 + .../src/Negotiation/AcceptEncoding.php | 7 + .../src/Negotiation/AcceptHeader.php | 7 + .../src/Negotiation/AcceptLanguage.php | 49 +++++ .../src/Negotiation/BaseAccept.php | 154 ++++++++++++++ .../src/Negotiation/CharsetNegotiator.php | 14 ++ .../src/Negotiation/EncodingNegotiator.php | 14 ++ .../src/Negotiation/Exception/Exception.php | 7 + .../Negotiation/Exception/InvalidArgument.php | 7 + .../Negotiation/Exception/InvalidHeader.php | 7 + .../Negotiation/Exception/InvalidLanguage.php | 7 + .../Exception/InvalidMediaType.php | 7 + .../src/Negotiation/LanguageNegotiator.php | 41 ++++ .../negotiation/src/Negotiation/Match.php | 62 ++++++ .../src/Negotiation/Negotiator.php | 89 ++++++++ .../Negotiation/Tests/AcceptLanguageTest.php | 52 +++++ .../tests/Negotiation/Tests/AcceptTest.php | 86 ++++++++ .../Negotiation/Tests/BaseAcceptTest.php | 84 ++++++++ .../Tests/CharsetNegotiatorTest.php | 121 +++++++++++ .../Tests/EncodingNegotiatorTest.php | 76 +++++++ .../Tests/LanguageNegotiatorTest.php | 81 ++++++++ .../tests/Negotiation/Tests/MatchTest.php | 56 +++++ .../Negotiation/Tests/NegotiatorTest.php | 195 ++++++++++++++++++ .../tests/Negotiation/Tests/TestCase.php | 15 ++ .../negotiation/tests/bootstrap.php | 15 ++ 41 files changed, 1860 insertions(+), 2 deletions(-) create mode 100644 .gitmodules create mode 100644 vendor/willdurand/negotiation/.gitignore create mode 100644 vendor/willdurand/negotiation/.travis.yml create mode 100644 vendor/willdurand/negotiation/CONTRIBUTING.md create mode 100644 vendor/willdurand/negotiation/LICENSE create mode 100644 vendor/willdurand/negotiation/README.md create mode 100644 vendor/willdurand/negotiation/appveyor.yml create mode 100644 vendor/willdurand/negotiation/composer.json create mode 100644 vendor/willdurand/negotiation/phpunit.xml.dist create mode 100644 vendor/willdurand/negotiation/src/Negotiation/AbstractNegotiator.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/Accept.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/AcceptCharset.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/AcceptEncoding.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/AcceptHeader.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/AcceptLanguage.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/BaseAccept.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/CharsetNegotiator.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/EncodingNegotiator.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/Exception/Exception.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidArgument.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidHeader.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidLanguage.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidMediaType.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/LanguageNegotiator.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/Match.php create mode 100644 vendor/willdurand/negotiation/src/Negotiation/Negotiator.php create mode 100644 vendor/willdurand/negotiation/tests/Negotiation/Tests/AcceptLanguageTest.php create mode 100644 vendor/willdurand/negotiation/tests/Negotiation/Tests/AcceptTest.php create mode 100644 vendor/willdurand/negotiation/tests/Negotiation/Tests/BaseAcceptTest.php create mode 100644 vendor/willdurand/negotiation/tests/Negotiation/Tests/CharsetNegotiatorTest.php create mode 100644 vendor/willdurand/negotiation/tests/Negotiation/Tests/EncodingNegotiatorTest.php create mode 100644 vendor/willdurand/negotiation/tests/Negotiation/Tests/LanguageNegotiatorTest.php create mode 100644 vendor/willdurand/negotiation/tests/Negotiation/Tests/MatchTest.php create mode 100644 vendor/willdurand/negotiation/tests/Negotiation/Tests/NegotiatorTest.php create mode 100644 vendor/willdurand/negotiation/tests/Negotiation/Tests/TestCase.php create mode 100644 vendor/willdurand/negotiation/tests/bootstrap.php diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..909f8d85f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor\\pica2mods"] + path = vendor\\pica2mods + url = https://github.com/dmj/pica2mods.git diff --git a/composer.json b/composer.json index 86ac1c844..6234bd579 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,7 @@ "symfony/http-foundation": "^3.4", "hab/picarecord": "^1.1", "hab/picareader": "^1.2", - "hab/picawriter": "^1.0" + "hab/picawriter": "^1.0", + "willdurand/negotiation": "^2.3" } } diff --git a/composer.lock b/composer.lock index 06f673931..30ec0a181 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "7b4e0ab48493485011f66d6e7fc1e6b2", + "content-hash": "d60aaa0891d9bd50b170b054a1e81a63", "packages": [ { "name": "hab/picareader", @@ -333,6 +333,58 @@ "shim" ], "time": "2018-01-30T19:27:44+00:00" + }, + { + "name": "willdurand/negotiation", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/willdurand/Negotiation.git", + "reference": "03436ededa67c6e83b9b12defac15384cb399dc9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/03436ededa67c6e83b9b12defac15384cb399dc9", + "reference": "03436ededa67c6e83b9b12defac15384cb399dc9", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Negotiation\\": "src/Negotiation" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "William Durand", + "email": "will+git@drnd.me" + } + ], + "description": "Content Negotiation tools for PHP provided as a standalone library.", + "homepage": "http://williamdurand.fr/Negotiation/", + "keywords": [ + "accept", + "content", + "format", + "header", + "negotiation" + ], + "time": "2017-05-14T17:21:12+00:00" } ], "packages-dev": [], diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index f9ff74ebe..0348a818e 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -9,4 +9,5 @@ return array( 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), + 'Negotiation\\' => array($vendorDir . '/willdurand/negotiation/src/Negotiation'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 207e20e69..545298306 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -19,6 +19,10 @@ class ComposerStaticInit78daec5e1761b48dba06a78068db3ccf 'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Component\\HttpFoundation\\' => 33, ), + 'N' => + array ( + 'Negotiation\\' => 12, + ), ); public static $prefixDirsPsr4 = array ( @@ -34,6 +38,10 @@ class ComposerStaticInit78daec5e1761b48dba06a78068db3ccf array ( 0 => __DIR__ . '/..' . '/symfony/http-foundation', ), + 'Negotiation\\' => + array ( + 0 => __DIR__ . '/..' . '/willdurand/negotiation/src/Negotiation', + ), ); public static $prefixesPsr0 = array ( diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index ddc76dc70..343b63b24 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -340,5 +340,59 @@ "portable", "shim" ] + }, + { + "name": "willdurand/negotiation", + "version": "v2.3.1", + "version_normalized": "2.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/willdurand/Negotiation.git", + "reference": "03436ededa67c6e83b9b12defac15384cb399dc9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/03436ededa67c6e83b9b12defac15384cb399dc9", + "reference": "03436ededa67c6e83b9b12defac15384cb399dc9", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5" + }, + "time": "2017-05-14T17:21:12+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Negotiation\\": "src/Negotiation" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "William Durand", + "email": "will+git@drnd.me" + } + ], + "description": "Content Negotiation tools for PHP provided as a standalone library.", + "homepage": "http://williamdurand.fr/Negotiation/", + "keywords": [ + "accept", + "content", + "format", + "header", + "negotiation" + ] } ] diff --git a/vendor/willdurand/negotiation/.gitignore b/vendor/willdurand/negotiation/.gitignore new file mode 100644 index 000000000..3a9875b46 --- /dev/null +++ b/vendor/willdurand/negotiation/.gitignore @@ -0,0 +1,2 @@ +/vendor/ +composer.lock diff --git a/vendor/willdurand/negotiation/.travis.yml b/vendor/willdurand/negotiation/.travis.yml new file mode 100644 index 000000000..d4eacb7e3 --- /dev/null +++ b/vendor/willdurand/negotiation/.travis.yml @@ -0,0 +1,19 @@ +sudo: false +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +matrix: + allow_failures: + - php: hhvm + +before_script: + - composer self-update + - composer install --prefer-dist --no-interaction + +script: phpunit --coverage-text diff --git a/vendor/willdurand/negotiation/CONTRIBUTING.md b/vendor/willdurand/negotiation/CONTRIBUTING.md new file mode 100644 index 000000000..3677a2cf0 --- /dev/null +++ b/vendor/willdurand/negotiation/CONTRIBUTING.md @@ -0,0 +1,33 @@ +Contributing +============ + +First of all, **thank you** for contributing, **you are awesome**! + +Here are a few rules to follow in order to ease code reviews, and discussions before +maintainers accept and merge your work. + +You MUST follow the [PSR-1](http://www.php-fig.org/psr/1/) and +[PSR-2](http://www.php-fig.org/psr/2/). If you don't know about any of them, you +should really read the recommendations. Can't wait? Use the [PHP-CS-Fixer +tool](http://cs.sensiolabs.org/). + +You MUST run the test suite. + +You MUST write (or update) unit tests. + +You SHOULD write documentation. + +Please, write [commit messages that make +sense](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), +and [rebase your branch](http://git-scm.com/book/en/Git-Branching-Rebasing) +before submitting your Pull Request. + +One may ask you to [squash your +commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) +too. This is used to "clean" your Pull Request before merging it (we don't want +commits such as `fix tests`, `fix 2`, `fix 3`, etc.). + +Also, while creating your Pull Request on GitHub, you MUST write a description +which gives the context and/or explains why you are creating it. + +Thank you! diff --git a/vendor/willdurand/negotiation/LICENSE b/vendor/willdurand/negotiation/LICENSE new file mode 100644 index 000000000..3546ac1b5 --- /dev/null +++ b/vendor/willdurand/negotiation/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) William Durand <will+git@drnd.me> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/willdurand/negotiation/README.md b/vendor/willdurand/negotiation/README.md new file mode 100644 index 000000000..1e7032fbb --- /dev/null +++ b/vendor/willdurand/negotiation/README.md @@ -0,0 +1,162 @@ +Negotiation +=========== + +[](http://travis-ci.org/willdurand/Negotiation) +[](https://ci.appveyor.com/project/willdurand/negotiation) +[](https://packagist.org/packages/willdurand/Negotiation) +[](https://packagist.org/packages/willdurand/Negotiation) + + +**Negotiation** is a standalone library without any dependencies that allows you +to implement [content +negotiation](https://tools.ietf.org/html/rfc7231#section-5.3) in your +application, whatever framework you use. This library is based on [RFC +7231](https://tools.ietf.org/html/rfc7231). Negotiation is easy to use, and +extensively unit tested! + +> **Important:** You are browsing the documentation of Negotiation **2.x**. +Documentation for version **1.x** is available here: [Negotiation 1.x +documentation](https://github.com/willdurand/Negotiation/blob/1.x/README.md#usage). +You might also be interested in this: [**What's new in Negotiation 2?**](https://github.com/willdurand/Negotiation/releases/tag/v2.0.0-alpha1) + + +Installation +------------ + +The recommended way to install Negotiation is through +[Composer](http://getcomposer.org/): + +```bash +$ composer require willdurand/negotiation +``` + + +Usage Examples +-------------- + +### Media Type Negotiation + +``` php +$negotiator = new \Negotiation\Negotiator(); + +$acceptHeader = 'text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8'; +$priorities = array('text/html; charset=UTF-8', 'application/json', 'application/xml;q=0.5'); + +$mediaType = $negotiator->getBest($acceptHeader, $priorities); + +$value = $mediaType->getValue(); +// $value == 'text/html; charset=UTF-8' +``` + +The `Negotiator` returns an instance of `Accept`, or `null` if negotiating the +best media type has failed. + +### Language Negotiation + +``` php +<?php + +$negotiator = new \Negotiation\LanguageNegotiator(); + +$acceptLangageHeader = 'en; q=0.1, fr; q=0.4, fu; q=0.9, de; q=0.2'; +$priorities = array('de', 'fu', 'en'); + +$bestLanguage = $negotiator->getBest($acceptLangageHeader, $priorities); + +$type = $bestLanguage->getType(); +// $type == 'fu'; + +$quality = $bestLanguage->getQuality(); +// $quality == 0.9 +``` + +The `LanguageNegotiator` returns an instance of `AcceptLanguage`. + +### Encoding Negotiation + +``` php +<?php + +$negotiator = new \Negotiation\EncodingNegotiator(); +$encoding = $negotiator->getBest($acceptHeader, $priorities); +``` + +The `EncodingNegotiator` returns an instance of `AcceptEncoding`. + +### Charset Negotiation + +``` php +<?php + +$negotiator = new \Negotiation\CharsetNegotiator(); + +$acceptCharsetHeader = 'ISO-8859-1, UTF-8; q=0.9'; +$priorities = array('iso-8859-1;q=0.3', 'utf-8;q=0.9', 'utf-16;q=1.0'); + +$bestCharset = $negotiator->getBest($acceptCharsetHeader, $priorities); + +$type = $bestCharset->getType(); +// $type == 'utf-8'; + +$quality = $bestCharset->getQuality(); +// $quality == 0.81 +``` + +The `CharsetNegotiator` returns an instance of `AcceptCharset`. + +### `Accept*` Classes + +`Accept` and `Accept*` classes share common methods such as: + +* `getValue()` returns the accept value (e.g. `text/html; z=y; a=b; c=d`) +* `getNormalizedValue()` returns the value with parameters sorted (e.g. + `text/html; a=b; c=d; z=y`) +* `getQuality()` returns the quality if available (`q` parameter) +* `getType()` returns the accept type (e.g. `text/html`) +* `getParameters()` returns the set of parameters (excluding the `q` parameter + if provided) +* `getParameter()` allows to retrieve a given parameter by its name. Fallback to + a `$default` (nullable) value otherwise. +* `hasParameter()` indicates whether a parameter exists. + + +Unit Tests +---------- + +Setup the test suite using Composer: + + $ composer install --dev + +Run it using PHPUnit: + + $ phpunit + + +Contributing +------------ + +See [CONTRIBUTING](CONTRIBUTING.md) file. + + +Credits +------- + +* Some parts of this library are inspired by: + + * [Symfony](http://github.com/symfony/symfony) framework; + * [FOSRest](http://github.com/FriendsOfSymfony/FOSRest); + * [PEAR HTTP2](https://github.com/pear/HTTP2). + +* William Durand <will+git@drnd.me> +* [@neural-wetware](https://github.com/neural-wetware) + + +License +------- + +Negotiation is released under the MIT License. See the bundled LICENSE file for +details. diff --git a/vendor/willdurand/negotiation/appveyor.yml b/vendor/willdurand/negotiation/appveyor.yml new file mode 100644 index 000000000..7280bcce5 --- /dev/null +++ b/vendor/willdurand/negotiation/appveyor.yml @@ -0,0 +1,30 @@ +build: false +shallow_clone: true +platform: x86 +clone_folder: c:\projects\willdurand\negotiation + +cache: + - '%LOCALAPPDATA%\Composer\files' + +init: + - SET PATH=C:\Program Files\OpenSSL;c:\tools\php;c:\tools\php71;%PATH% + +install: + - ps: Set-Service wuauserv -StartupType Manual + - cinst -y OpenSSL.Light + - cinst -y php + - cd c:\tools\php71 + - copy php.ini-production php.ini /Y + - echo date.timezone="UTC" >> php.ini + - echo extension_dir=ext >> php.ini + - echo extension=php_openssl.dll >> php.ini + - echo extension=php_mbstring.dll >> php.ini + - echo extension=php_fileinfo.dll >> php.ini + - echo memory_limit=1G >> php.ini + - cd c:\projects\willdurand\negotiation + - php -r "readfile('http://getcomposer.org/installer');" | php + - php composer.phar update --no-interaction --no-progress + +test_script: + - cd c:\projects\willdurand\negotiation + - vendor\bin\phpunit.bat --verbose diff --git a/vendor/willdurand/negotiation/composer.json b/vendor/willdurand/negotiation/composer.json new file mode 100644 index 000000000..6418b4181 --- /dev/null +++ b/vendor/willdurand/negotiation/composer.json @@ -0,0 +1,27 @@ +{ + "name": "willdurand/negotiation", + "description": "Content Negotiation tools for PHP provided as a standalone library.", + "keywords": [ "content", "negotiation", "format", "accept", "header" ], + "license": "MIT", + "homepage": "http://williamdurand.fr/Negotiation/", + "authors": [ + { + "name": "William Durand", + "email": "will+git@drnd.me" + } + ], + "require": { + "php": ">=5.4.0" + }, + "autoload": { + "psr-4": { "Negotiation\\": "src/Negotiation" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "require-dev": { + "phpunit/phpunit": "~4.5" + } +} diff --git a/vendor/willdurand/negotiation/phpunit.xml.dist b/vendor/willdurand/negotiation/phpunit.xml.dist new file mode 100644 index 000000000..ac6f0f92e --- /dev/null +++ b/vendor/willdurand/negotiation/phpunit.xml.dist @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit backupGlobals="false" + backupStaticAttributes="false" + colors="true" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + processIsolation="false" + stopOnFailure="false" + syntaxCheck="false" + bootstrap="tests/bootstrap.php" + > + <testsuites> + <testsuite name="Negotiation Test Suite"> + <directory>./tests/</directory> + </testsuite> + </testsuites> + <filter> + <whitelist> + <directory>./src/Negotiation/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/vendor/willdurand/negotiation/src/Negotiation/AbstractNegotiator.php b/vendor/willdurand/negotiation/src/Negotiation/AbstractNegotiator.php new file mode 100644 index 000000000..2cd839e53 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/AbstractNegotiator.php @@ -0,0 +1,118 @@ +<?php + +namespace Negotiation; + +use Negotiation\Exception\InvalidArgument; +use Negotiation\Exception\InvalidHeader; + +abstract class AbstractNegotiator +{ + /** + * @param string $header A string containing an `Accept|Accept-*` header. + * @param array $priorities A set of server priorities. + * + * @return AcceptHeader|null best matching type + */ + public function getBest($header, array $priorities) + { + if (empty($priorities)) { + throw new InvalidArgument('A set of server priorities should be given.'); + } + + if (!$header) { + throw new InvalidArgument('The header string should not be empty.'); + } + + // Once upon a time, two `array_map` calls were sitting there, but for + // some reasons, they triggered `E_WARNING` time to time (because of + // PHP bug [55416](https://bugs.php.net/bug.php?id=55416). Now, they + // are gone. + // See: https://github.com/willdurand/Negotiation/issues/81 + $acceptedHeaders = array(); + foreach ($this->parseHeader($header) as $h) { + try { + $acceptedHeaders[] = $this->acceptFactory($h); + } catch (Exception\Exception $e) { + // silently skip in case of invalid headers coming in from a client + } + } + $acceptedPriorities = array(); + foreach ($priorities as $p) { + $acceptedPriorities[] = $this->acceptFactory($p); + } + $matches = $this->findMatches($acceptedHeaders, $acceptedPriorities); + $specificMatches = array_reduce($matches, 'Negotiation\Match::reduce', []); + + usort($specificMatches, 'Negotiation\Match::compare'); + + $match = array_shift($specificMatches); + + return null === $match ? null : $acceptedPriorities[$match->index]; + } + + /** + * @param string $header accept header part or server priority + * + * @return AcceptHeader Parsed header object + */ + abstract protected function acceptFactory($header); + + /** + * @param AcceptHeader $header + * @param AcceptHeader $priority + * @param integer $index + * + * @return Match|null Headers matched + */ + protected function match(AcceptHeader $header, AcceptHeader $priority, $index) + { + $ac = $header->getType(); + $pc = $priority->getType(); + + $equal = !strcasecmp($ac, $pc); + + if ($equal || $ac === '*') { + $score = 1 * $equal; + + return new Match($header->getQuality() * $priority->getQuality(), $score, $index); + } + + return null; + } + + /** + * @param string $header A string that contains an `Accept*` header. + * + * @return AcceptHeader[] + */ + private function parseHeader($header) + { + $res = preg_match_all('/(?:[^,"]*+(?:"[^"]*+")?)+[^,"]*+/', $header, $matches); + + if (!$res) { + throw new InvalidHeader(sprintf('Failed to parse accept header: "%s"', $header)); + } + + return array_values(array_filter(array_map('trim', $matches[0]))); + } + + /** + * @param AcceptHeader[] $headerParts + * @param Priority[] $priorities Configured priorities + * + * @return Match[] Headers matched + */ + private function findMatches(array $headerParts, array $priorities) + { + $matches = []; + foreach ($priorities as $index => $p) { + foreach ($headerParts as $h) { + if (null !== $match = $this->match($h, $p, $index)) { + $matches[] = $match; + } + } + } + + return $matches; + } +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/Accept.php b/vendor/willdurand/negotiation/src/Negotiation/Accept.php new file mode 100644 index 000000000..281ae27d7 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/Accept.php @@ -0,0 +1,46 @@ +<?php + +namespace Negotiation; + +use Negotiation\Exception\InvalidMediaType; + +final class Accept extends BaseAccept implements AcceptHeader +{ + private $basePart; + + private $subPart; + + public function __construct($value) + { + parent::__construct($value); + + if ($this->type === '*') { + $this->type = '*/*'; + } + + $parts = explode('/', $this->type); + + if (count($parts) !== 2 || !$parts[0] || !$parts[1]) { + throw new InvalidMediaType(); + } + + $this->basePart = $parts[0]; + $this->subPart = $parts[1]; + } + + /** + * @return string + */ + public function getSubPart() + { + return $this->subPart; + } + + /** + * @return string + */ + public function getBasePart() + { + return $this->basePart; + } +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/AcceptCharset.php b/vendor/willdurand/negotiation/src/Negotiation/AcceptCharset.php new file mode 100644 index 000000000..7ce34908e --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/AcceptCharset.php @@ -0,0 +1,7 @@ +<?php + +namespace Negotiation; + +final class AcceptCharset extends BaseAccept implements AcceptHeader +{ +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/AcceptEncoding.php b/vendor/willdurand/negotiation/src/Negotiation/AcceptEncoding.php new file mode 100644 index 000000000..8165a7f85 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/AcceptEncoding.php @@ -0,0 +1,7 @@ +<?php + +namespace Negotiation; + +final class AcceptEncoding extends BaseAccept implements AcceptHeader +{ +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/AcceptHeader.php b/vendor/willdurand/negotiation/src/Negotiation/AcceptHeader.php new file mode 100644 index 000000000..3f5f8c579 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/AcceptHeader.php @@ -0,0 +1,7 @@ +<?php + +namespace Negotiation; + +interface AcceptHeader +{ +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/AcceptLanguage.php b/vendor/willdurand/negotiation/src/Negotiation/AcceptLanguage.php new file mode 100644 index 000000000..ad1c03236 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/AcceptLanguage.php @@ -0,0 +1,49 @@ +<?php + +namespace Negotiation; + +use Negotiation\Exception\InvalidLanguage; + +final class AcceptLanguage extends BaseAccept implements AcceptHeader +{ + private $language; + private $script; + private $region; + + public function __construct($value) + { + parent::__construct($value); + + $parts = explode('-', $this->type); + + if (2 === count($parts)) { + $this->language = $parts[0]; + $this->region = $parts[1]; + } elseif (1 === count($parts)) { + $this->language = $parts[0]; + } elseif (3 === count($parts)) { + $this->language = $parts[0]; + $this->script = $parts[1]; + $this->region = $parts[2]; + } else { + // TODO: this part is never reached... + throw new InvalidLanguage(); + } + } + + /** + * @return string + */ + public function getSubPart() + { + return $this->region; + } + + /** + * @return string + */ + public function getBasePart() + { + return $this->language; + } +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/BaseAccept.php b/vendor/willdurand/negotiation/src/Negotiation/BaseAccept.php new file mode 100644 index 000000000..3c584479c --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/BaseAccept.php @@ -0,0 +1,154 @@ +<?php + +namespace Negotiation; + +abstract class BaseAccept +{ + /** + * @var float + */ + private $quality = 1.0; + + /** + * @var string + */ + private $normalized; + + /** + * @var string + */ + private $value; + + /** + * @var array + */ + private $parameters; + + /** + * @var string + */ + protected $type; + + /** + * @param string $value + */ + public function __construct($value) + { + list($type, $parameters) = $this->parseParameters($value); + + if (isset($parameters['q'])) { + $this->quality = (float) $parameters['q']; + unset($parameters['q']); + } + + $type = trim(strtolower($type)); + + $this->value = $value; + $this->normalized = $type . ($parameters ? "; " . $this->buildParametersString($parameters) : ''); + $this->type = $type; + $this->parameters = $parameters; + } + + /** + * @return string + */ + public function getNormalizedValue() + { + return $this->normalized; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @return float + */ + public function getQuality() + { + return $this->quality; + } + + /** + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * @param string $key + * @param mixed $default + * + *Â @return string|null + */ + public function getParameter($key, $default = null) + { + return isset($this->parameters[$key]) ? $this->parameters[$key] : $default; + } + + /** + * @param string $key + * + * @return boolean + */ + public function hasParameter($key) + { + return isset($this->parameters[$key]); + } + + /** + * + * @param string $acceptPart + * @return array + */ + private function parseParameters($acceptPart) + { + $parts = explode(';', $acceptPart); + $type = array_shift($parts); + + $parameters = []; + foreach ($parts as $part) { + $part = explode('=', $part); + + if (2 !== count($part)) { + continue; // TODO: throw exception here? + } + + $key = strtolower(trim($part[0])); // TODO: technically not allowed space around "=". throw exception? + $parameters[$key] = trim($part[1], ' "'); + } + + return [ $type, $parameters ]; + } + + /** + * @param string $parameters + * + * @return string + */ + private function buildParametersString($parameters) + { + $parts = []; + + ksort($parameters); + foreach ($parameters as $key => $val) { + $parts[] = sprintf('%s=%s', $key, $val); + } + + return implode('; ', $parts); + } +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/CharsetNegotiator.php b/vendor/willdurand/negotiation/src/Negotiation/CharsetNegotiator.php new file mode 100644 index 000000000..c1f9a4b66 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/CharsetNegotiator.php @@ -0,0 +1,14 @@ +<?php + +namespace Negotiation; + +class CharsetNegotiator extends AbstractNegotiator +{ + /** + * {@inheritdoc} + */ + protected function acceptFactory($accept) + { + return new AcceptCharset($accept); + } +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/EncodingNegotiator.php b/vendor/willdurand/negotiation/src/Negotiation/EncodingNegotiator.php new file mode 100644 index 000000000..6b97fb738 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/EncodingNegotiator.php @@ -0,0 +1,14 @@ +<?php + +namespace Negotiation; + +class EncodingNegotiator extends AbstractNegotiator +{ + /** + * {@inheritdoc} + */ + protected function acceptFactory($accept) + { + return new AcceptEncoding($accept); + } +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/Exception/Exception.php b/vendor/willdurand/negotiation/src/Negotiation/Exception/Exception.php new file mode 100644 index 000000000..aca5d784a --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/Exception/Exception.php @@ -0,0 +1,7 @@ +<?php + +namespace Negotiation\Exception; + +interface Exception +{ +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidArgument.php b/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidArgument.php new file mode 100644 index 000000000..c84e7e486 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidArgument.php @@ -0,0 +1,7 @@ +<?php + +namespace Negotiation\Exception; + +class InvalidArgument extends \InvalidArgumentException implements Exception +{ +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidHeader.php b/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidHeader.php new file mode 100644 index 000000000..eed3efe82 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidHeader.php @@ -0,0 +1,7 @@ +<?php + +namespace Negotiation\Exception; + +class InvalidHeader extends \RuntimeException implements Exception +{ +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidLanguage.php b/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidLanguage.php new file mode 100644 index 000000000..2285a7144 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidLanguage.php @@ -0,0 +1,7 @@ +<?php + +namespace Negotiation\Exception; + +class InvalidLanguage extends \RuntimeException implements Exception +{ +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidMediaType.php b/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidMediaType.php new file mode 100644 index 000000000..6c6a47997 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/Exception/InvalidMediaType.php @@ -0,0 +1,7 @@ +<?php + +namespace Negotiation\Exception; + +class InvalidMediaType extends \RuntimeException implements Exception +{ +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/LanguageNegotiator.php b/vendor/willdurand/negotiation/src/Negotiation/LanguageNegotiator.php new file mode 100644 index 000000000..ac9381403 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/LanguageNegotiator.php @@ -0,0 +1,41 @@ +<?php + +namespace Negotiation; + +class LanguageNegotiator extends AbstractNegotiator +{ + /** + * {@inheritdoc} + */ + protected function acceptFactory($accept) + { + return new AcceptLanguage($accept); + } + + /** + * {@inheritdoc} + */ + protected function match(AcceptHeader $acceptLanguage, AcceptHeader $priority, $index) + { + if (!$acceptLanguage instanceof AcceptLanguage || !$priority instanceof AcceptLanguage) { + return null; + } + + $ab = $acceptLanguage->getBasePart(); + $pb = $priority->getBasePart(); + + $as = $acceptLanguage->getSubPart(); + $ps = $priority->getSubPart(); + + $baseEqual = !strcasecmp($ab, $pb); + $subEqual = !strcasecmp($as, $ps); + + if (($ab == '*' || $baseEqual) && ($as === null || $subEqual)) { + $score = 10 * $baseEqual + $subEqual; + + return new Match($acceptLanguage->getQuality() * $priority->getQuality(), $score, $index); + } + + return null; + } +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/Match.php b/vendor/willdurand/negotiation/src/Negotiation/Match.php new file mode 100644 index 000000000..5b0e014e2 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/Match.php @@ -0,0 +1,62 @@ +<?php + +namespace Negotiation; + +final class Match +{ + /** + * @var float + */ + public $quality; + + /** + * @var int + */ + public $score; + + /** + * @var int + */ + public $index; + + public function __construct($quality, $score, $index) + { + $this->quality = $quality; + $this->score = $score; + $this->index = $index; + } + + /** + * @param Match $a + * @param Match $b + * + * @return int + */ + public static function compare(Match $a, Match $b) + { + if ($a->quality !== $b->quality) { + return $a->quality > $b->quality ? -1 : 1; + } + + if ($a->index !== $b->index) { + return $a->index > $b->index ? 1 : -1; + } + + return 0; + } + + /** + * @param array $carry reduced array + * @param Match $match match to be reduced + * + * @return Match[] + */ + public static function reduce(array $carry, Match $match) + { + if (!isset($carry[$match->index]) || $carry[$match->index]->score < $match->score) { + $carry[$match->index] = $match; + } + + return $carry; + } +} diff --git a/vendor/willdurand/negotiation/src/Negotiation/Negotiator.php b/vendor/willdurand/negotiation/src/Negotiation/Negotiator.php new file mode 100644 index 000000000..d083fa1e0 --- /dev/null +++ b/vendor/willdurand/negotiation/src/Negotiation/Negotiator.php @@ -0,0 +1,89 @@ +<?php + +namespace Negotiation; + +class Negotiator extends AbstractNegotiator +{ + /** + * {@inheritdoc} + */ + protected function acceptFactory($accept) + { + return new Accept($accept); + } + + /** + * {@inheritdoc} + */ + protected function match(AcceptHeader $accept, AcceptHeader $priority, $index) + { + if (!$accept instanceof Accept || !$priority instanceof Accept) { + return null; + } + + $acceptBase = $accept->getBasePart(); + $priorityBase = $priority->getBasePart(); + + $acceptSub = $accept->getSubPart(); + $prioritySub = $priority->getSubPart(); + + $intersection = array_intersect_assoc($accept->getParameters(), $priority->getParameters()); + + $baseEqual = !strcasecmp($acceptBase, $priorityBase); + $subEqual = !strcasecmp($acceptSub, $prioritySub); + + if (($acceptBase === '*' || $baseEqual) + && ($acceptSub === '*' || $subEqual) + && count($intersection) === count($accept->getParameters()) + ) { + $score = 100 * $baseEqual + 10 * $subEqual + count($intersection); + + return new Match($accept->getQuality() * $priority->getQuality(), $score, $index); + } + + if (!strstr($acceptSub, '+') || !strstr($prioritySub, '+')) { + return null; + } + + // Handle "+" segment wildcards + list($acceptSub, $acceptPlus) = $this->splitSubPart($acceptSub); + list($prioritySub, $priorityPlus) = $this->splitSubPart($prioritySub); + + // If no wildcards in either the subtype or + segment, do nothing. + if (!($acceptBase === '*' || $baseEqual) + || !($acceptSub === '*' || $prioritySub === '*' || $acceptPlus === '*' || $priorityPlus === '*') + ) { + return null; + } + + $subEqual = !strcasecmp($acceptSub, $prioritySub); + $plusEqual = !strcasecmp($acceptPlus, $priorityPlus); + + if (($acceptSub === '*' || $prioritySub === '*' || $subEqual) + && ($acceptPlus === '*' || $priorityPlus === '*' || $plusEqual) + && count($intersection) === count($accept->getParameters()) + ) { + $score = 100 * $baseEqual + 10 * $subEqual + $plusEqual + count($intersection); + + return new Match($accept->getQuality() * $priority->getQuality(), $score, $index); + } + + return null; + } + + /** + * Split a subpart into the subpart and "plus" part. + * + * For media-types of the form "application/vnd.example+json", matching + * should allow wildcards for either the portion before the "+" or + * after. This method splits the subpart to allow such matching. + */ + protected function splitSubPart($subPart) + { + if (!strstr($subPart, '+')) { + return [$subPart, '']; + } + + return explode('+', $subPart, 2); + } +} diff --git a/vendor/willdurand/negotiation/tests/Negotiation/Tests/AcceptLanguageTest.php b/vendor/willdurand/negotiation/tests/Negotiation/Tests/AcceptLanguageTest.php new file mode 100644 index 000000000..bc8ab2489 --- /dev/null +++ b/vendor/willdurand/negotiation/tests/Negotiation/Tests/AcceptLanguageTest.php @@ -0,0 +1,52 @@ +<?php + +namespace Negotiation\Tests; + +use Negotiation\AcceptLanguage; + +class AcceptLanguageTest extends TestCase +{ + + /** + * @dataProvider dataProviderForGetType + */ + public function testGetType($header, $expected) + { + $accept = new AcceptLanguage($header); + $actual = $accept->getType(); + $this->assertEquals($expected, $actual); + } + + public static function dataProviderForGetType() + { + return array( + array('en;q=0.7', 'en'), + array('en-GB;q=0.8', 'en-gb'), + array('da', 'da'), + array('en-gb;q=0.8', 'en-gb'), + array('es;q=0.7', 'es'), + array('fr ; q= 0.1', 'fr'), + array('', null), + array(null, null), + ); + } + + /** + * @dataProvider dataProviderForGetValue + */ + public function testGetValue($header, $expected) + { + $accept = new AcceptLanguage($header); + $actual = $accept->getValue(); + $this->assertEquals($expected, $actual); + + } + + public static function dataProviderForGetValue() + { + return array( + array('en;q=0.7', 'en;q=0.7'), + array('en-GB;q=0.8', 'en-GB;q=0.8'), + ); + } +} diff --git a/vendor/willdurand/negotiation/tests/Negotiation/Tests/AcceptTest.php b/vendor/willdurand/negotiation/tests/Negotiation/Tests/AcceptTest.php new file mode 100644 index 000000000..f76219e85 --- /dev/null +++ b/vendor/willdurand/negotiation/tests/Negotiation/Tests/AcceptTest.php @@ -0,0 +1,86 @@ +<?php + +namespace Negotiation\Tests; + +use Negotiation\Accept; + +class AcceptTest extends TestCase +{ + public function testGetParameter() + { + $accept = new Accept('foo/bar; q=1; hello=world'); + + $this->assertTrue($accept->hasParameter('hello')); + $this->assertEquals('world', $accept->getParameter('hello')); + $this->assertFalse($accept->hasParameter('unknown')); + $this->assertNull($accept->getParameter('unknown')); + $this->assertFalse($accept->getParameter('unknown', false)); + $this->assertSame('world', $accept->getParameter('hello', 'goodbye')); + } + + /** + * @dataProvider dataProviderForTestGetNormalizedValue + */ + public function testGetNormalizedValue($header, $expected) + { + $accept = new Accept($header); + $actual = $accept->getNormalizedValue(); + $this->assertEquals($expected, $actual); + } + + public static function dataProviderForTestGetNormalizedValue() + { + return array( + array('text/html; z=y; a=b; c=d', 'text/html; a=b; c=d; z=y'), + array('application/pdf; q=1; param=p', 'application/pdf; param=p') + ); + } + + /** + * @dataProvider dataProviderForGetType + */ + public function testGetType($header, $expected) + { + $accept = new Accept($header); + $actual = $accept->getType(); + $this->assertEquals($expected, $actual); + } + + public static function dataProviderForGetType() + { + return array( + array('text/html;hello=world', 'text/html'), + array('application/pdf', 'application/pdf'), + array('application/xhtml+xml;q=0.9', 'application/xhtml+xml'), + array('text/plain; q=0.5', 'text/plain'), + array('text/html;level=2;q=0.4', 'text/html'), + array('text/html ; level = 2 ; q = 0.4', 'text/html'), + array('text/*', 'text/*'), + array('text/* ;q=1 ;level=2', 'text/*'), + array('*/*', '*/*'), + array('*', '*/*'), + array('*/* ; param=555', '*/*'), + array('* ; param=555', '*/*'), + array('TEXT/hTmL;leVel=2; Q=0.4', 'text/html'), + ); + } + + /** + * @dataProvider dataProviderForGetValue + */ + public function testGetValue($header, $expected) + { + $accept = new Accept($header); + $actual = $accept->getValue(); + $this->assertEquals($expected, $actual); + + } + + public static function dataProviderForGetValue() + { + return array( + array('text/html;hello=world ;q=0.5', 'text/html;hello=world ;q=0.5'), + array('application/pdf', 'application/pdf'), + ); + } +} diff --git a/vendor/willdurand/negotiation/tests/Negotiation/Tests/BaseAcceptTest.php b/vendor/willdurand/negotiation/tests/Negotiation/Tests/BaseAcceptTest.php new file mode 100644 index 000000000..3fcf1f986 --- /dev/null +++ b/vendor/willdurand/negotiation/tests/Negotiation/Tests/BaseAcceptTest.php @@ -0,0 +1,84 @@ +<?php + +namespace Negotiation\Tests; + +use Negotiation\BaseAccept; + +class BaseAcceptTest extends TestCase +{ + /** + * @dataProvider dataProviderForParseParameters + */ + public function testParseParameters($value, $expected) + { + $accept = new DummyAccept($value); + $parameters = $accept->getParameters(); + + // TODO: hack-ish... this is needed because logic in BaseAccept + //constructor drops the quality from the parameter set. + if (false !== strpos($value, 'q')) { + $parameters['q'] = $accept->getQuality(); + } + + $this->assertCount(count($expected), $parameters); + + foreach ($expected as $key => $value) { + $this->assertArrayHasKey($key, $parameters); + $this->assertEquals($value, $parameters[$key]); + } + } + + public static function dataProviderForParseParameters() + { + return array( + array( + 'application/json ;q=1.0; level=2;foo= bar', + array( + 'q' => 1.0, + 'level' => 2, + 'foo' => 'bar', + ), + ), + array( + 'application/json ;q = 1.0; level = 2; FOO = bAr', + array( + 'q' => 1.0, + 'level' => 2, + 'foo' => 'bAr', + ), + ), + array( + 'application/json;q=1.0', + array( + 'q' => 1.0, + ), + ), + array( + 'application/json;foo', + array(), + ), + ); + } + + /** + * @dataProvider dataProviderBuildParametersString + */ + + public function testBuildParametersString($value, $expected) + { + $accept = new DummyAccept($value); + + $this->assertEquals($expected, $accept->getNormalizedValue()); + } + + public static function dataProviderBuildParametersString() + { + return array( + array('media/type; xxx = 1.0;level=2;foo=bar', 'media/type; foo=bar; level=2; xxx=1.0'), + ); + } +} + +class DummyAccept extends BaseAccept +{ +} diff --git a/vendor/willdurand/negotiation/tests/Negotiation/Tests/CharsetNegotiatorTest.php b/vendor/willdurand/negotiation/tests/Negotiation/Tests/CharsetNegotiatorTest.php new file mode 100644 index 000000000..832a067c0 --- /dev/null +++ b/vendor/willdurand/negotiation/tests/Negotiation/Tests/CharsetNegotiatorTest.php @@ -0,0 +1,121 @@ +<?php + +namespace Negotiation\Tests; + +use Negotiation\CharsetNegotiator; + +class CharsetNegotiatorTest extends TestCase +{ + + /** + * @var CharsetNegotiator + */ + private $negotiator; + + protected function setUp() + { + $this->negotiator = new CharsetNegotiator(); + } + + public function testGetBestReturnsNullWithUnmatchedHeader() + { + $this->assertNull($this->negotiator->getBest('foo, bar, yo', array('baz'))); + } + + /** + * 'bu' has the highest quality rating, but is non-existent, + * so we expect the next highest rated 'fr' content to be returned. + * + * See: http://svn.apache.org/repos/asf/httpd/test/framework/trunk/t/modules/negotiation.t + */ + public function testGetBestIgnoresNonExistentContent() + { + $acceptCharset = 'en; q=0.1, fr; q=0.4, bu; q=1.0'; + $accept = $this->negotiator->getBest($acceptCharset, array('en', 'fr')); + + $this->assertInstanceOf('Negotiation\AcceptCharset', $accept); + $this->assertEquals('fr', $accept->getValue()); + } + + /** + * @dataProvider dataProviderForTestGetBest + */ + public function testGetBest($accept, $priorities, $expected) + { + if (is_null($expected)) + $this->setExpectedException('Negotiation\Exception\InvalidArgument'); + + $accept = $this->negotiator->getBest($accept, $priorities); + if (null === $accept) { + $this->assertNull($expected); + } else { + $this->assertInstanceOf('Negotiation\AcceptCharset', $accept); + $this->assertSame($expected, $accept->getValue()); + } + } + + public static function dataProviderForTestGetBest() + { + $pearCharset = 'ISO-8859-1, Big5;q=0.6,utf-8;q=0.7, *;q=0.5'; + $pearCharset2 = 'ISO-8859-1, Big5;q=0.6,utf-8;q=0.7'; + + return array( + array($pearCharset, array( 'utf-8', 'big5', 'iso-8859-1', 'shift-jis',), 'iso-8859-1'), + array($pearCharset, array( 'utf-8', 'big5', 'shift-jis',), 'utf-8'), + array($pearCharset, array( 'Big5', 'shift-jis',), 'Big5'), + array($pearCharset, array( 'shift-jis',), 'shift-jis'), + array($pearCharset2, array( 'utf-8', 'big5', 'iso-8859-1', 'shift-jis',), 'iso-8859-1'), + array($pearCharset2, array( 'utf-8', 'big5', 'shift-jis',), 'utf-8'), + array($pearCharset2, array( 'Big5', 'shift-jis',), 'Big5'), + array('utf-8;q=0.6,iso-8859-5;q=0.9', array( 'iso-8859-5', 'utf-8',), 'iso-8859-5'), + array('', array( 'iso-8859-5', 'utf-8',), null), + array('en, *;q=0.9', array('fr'), 'fr'), + # Quality of source factors + array($pearCharset, array('iso-8859-1;q=0.5', 'utf-8', 'utf-16;q=1.0'), 'utf-8'), + array($pearCharset, array('iso-8859-1;q=0.8', 'utf-8', 'utf-16;q=1.0'), 'iso-8859-1;q=0.8'), + ); + } + + public function testGetBestRespectsPriorities() + { + $accept = $this->negotiator->getBest('foo, bar, yo', array('yo')); + + $this->assertInstanceOf('Negotiation\AcceptCharset', $accept); + $this->assertEquals('yo', $accept->getValue()); + } + + public function testGetBestDoesNotMatchPriorities() + { + $acceptCharset = 'en, de'; + $priorities = array('fr'); + + $this->assertNull($this->negotiator->getBest($acceptCharset, $priorities)); + } + + public function testGetBestRespectsQualityOfSource() + { + $accept = $this->negotiator->getBest('utf-8;q=0.5,iso-8859-1', array('iso-8859-1;q=0.3', 'utf-8;q=0.9', 'utf-16;q=1.0')); + $this->assertInstanceOf('Negotiation\AcceptCharset', $accept); + $this->assertEquals('utf-8', $accept->getType()); + } + + /** + * @dataProvider dataProviderForTestParseHeader + */ + public function testParseHeader($header, $expected) + { + $accepts = $this->call_private_method('Negotiation\CharsetNegotiator', 'parseHeader', $this->negotiator, array($header)); + + $this->assertSame($expected, $accepts); + } + + public static function dataProviderForTestParseHeader() + { + return array( + array('*;q=0.3,ISO-8859-1,utf-8;q=0.7', array('*;q=0.3', 'ISO-8859-1', 'utf-8;q=0.7')), + array('*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', array('*;q=0.3', 'ISO-8859-1;q=0.7', 'utf-8;q=0.7')), + array('*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', array('*;q=0.3', 'utf-8;q=0.7', 'ISO-8859-1;q=0.7')), + array('iso-8859-5, unicode-1-1;q=0.8', array('iso-8859-5', 'unicode-1-1;q=0.8')), + ); + } +} diff --git a/vendor/willdurand/negotiation/tests/Negotiation/Tests/EncodingNegotiatorTest.php b/vendor/willdurand/negotiation/tests/Negotiation/Tests/EncodingNegotiatorTest.php new file mode 100644 index 000000000..3a2a2c994 --- /dev/null +++ b/vendor/willdurand/negotiation/tests/Negotiation/Tests/EncodingNegotiatorTest.php @@ -0,0 +1,76 @@ +<?php + +namespace Negotiation\Tests; + +use Negotiation\EncodingNegotiator; + +class EncodingNegotiatorTest extends TestCase +{ + + /** + * @var EncodingNegotiator + */ + private $negotiator; + + protected function setUp() + { + $this->negotiator = new EncodingNegotiator(); + } + + public function testGetBestReturnsNullWithUnmatchedHeader() + { + $this->assertNull($this->negotiator->getBest('foo, bar, yo', array('baz'))); + } + + /** + * @dataProvider dataProviderForTestGetBest + */ + public function testGetBest($accept, $priorities, $expected) + { + $accept = $this->negotiator->getBest($accept, $priorities); + + if (null === $accept) { + $this->assertNull($expected); + } else { + $this->assertInstanceOf('Negotiation\AcceptEncoding', $accept); + $this->assertEquals($expected, $accept->getValue()); + } + } + + public static function dataProviderForTestGetBest() + { + return array( + array('gzip;q=1.0, identity; q=0.5, *;q=0', array('identity'), 'identity'), + array('gzip;q=0.5, identity; q=0.5, *;q=0.7', array('bzip', 'foo'), 'bzip'), + array('gzip;q=0.7, identity; q=0.5, *;q=0.7', array('gzip', 'foo'), 'gzip'), + # Quality of source factors + array('gzip;q=0.7,identity', array('identity;q=0.5', 'gzip;q=0.9'), 'gzip;q=0.9'), + ); + } + + public function testGetBestRespectsQualityOfSource() + { + $accept = $this->negotiator->getBest('gzip;q=0.7,identity', array('identity;q=0.5', 'gzip;q=0.9')); + $this->assertInstanceOf('Negotiation\AcceptEncoding', $accept); + $this->assertEquals('gzip', $accept->getType()); + } + + /** + * @dataProvider dataProviderForTestParseAcceptHeader + */ + public function testParseAcceptHeader($header, $expected) + { + $accepts = $this->call_private_method('Negotiation\Negotiator', 'parseHeader', $this->negotiator, array($header)); + + $this->assertSame($expected, $accepts); + } + + public static function dataProviderForTestParseAcceptHeader() + { + return array( + array('gzip,deflate,sdch', array('gzip', 'deflate', 'sdch')), + array("gzip, deflate\t,sdch", array('gzip', 'deflate', 'sdch')), + array('gzip;q=1.0, identity; q=0.5, *;q=0', array('gzip;q=1.0', 'identity; q=0.5', '*;q=0')), + ); + } +} diff --git a/vendor/willdurand/negotiation/tests/Negotiation/Tests/LanguageNegotiatorTest.php b/vendor/willdurand/negotiation/tests/Negotiation/Tests/LanguageNegotiatorTest.php new file mode 100644 index 000000000..d11f3dcbe --- /dev/null +++ b/vendor/willdurand/negotiation/tests/Negotiation/Tests/LanguageNegotiatorTest.php @@ -0,0 +1,81 @@ +<?php + +namespace Negotiation\Tests; + +use Negotiation\Exception\InvalidArgument; +use Negotiation\LanguageNegotiator; + +class LanguageNegotiatorTest extends TestCase +{ + + /** + * @var LanguageNegotiator + */ + private $negotiator; + + protected function setUp() + { + $this->negotiator = new LanguageNegotiator(); + } + + /** + * @dataProvider dataProviderForTestGetBest + */ + public function testGetBest($accept, $priorities, $expected) + { + try { + $accept = $this->negotiator->getBest($accept, $priorities); + + if (null === $accept) { + $this->assertNull($expected); + } else { + $this->assertInstanceOf('Negotiation\AcceptLanguage', $accept); + $this->assertEquals($expected, $accept->getValue()); + } + } catch (\Exception $e) { + $this->assertEquals($expected, $e); + } + } + + public static function dataProviderForTestGetBest() + { + return array( + array('en, de', array('fr'), null), + array('foo, bar, yo', array('baz', 'biz'), null), + array('fr-FR, en;q=0.8', array('en-US', 'de-DE'), 'en-US'), + array('en, *;q=0.9', array('fr'), 'fr'), + array('foo, bar, yo', array('yo'), 'yo'), + array('en; q=0.1, fr; q=0.4, bu; q=1.0', array('en', 'fr'), 'fr'), + array('en; q=0.1, fr; q=0.4, fu; q=0.9, de; q=0.2', array('en', 'fu'), 'fu'), + array('', array('en', 'fu'), new InvalidArgument('The header string should not be empty.')), + array('fr, zh-Hans-CN;q=0.3', array('fr'), 'fr'), + # Quality of source factors + array('en;q=0.5,de', array('de;q=0.3', 'en;q=0.9'), 'en;q=0.9'), + ); + } + + public function testGetBestRespectsQualityOfSource() + { + $accept = $this->negotiator->getBest('en;q=0.5,de', array('de;q=0.3', 'en;q=0.9')); + $this->assertInstanceOf('Negotiation\AcceptLanguage', $accept); + $this->assertEquals('en', $accept->getType()); + } + + /** + * @dataProvider dataProviderForTestParseHeader + */ + public function testParseHeader($header, $expected) + { + $accepts = $this->call_private_method('Negotiation\Negotiator', 'parseHeader', $this->negotiator, array($header)); + + $this->assertSame($expected, $accepts); + } + + public static function dataProviderForTestParseHeader() + { + return array( + array('en; q=0.1, fr; q=0.4, bu; q=1.0', array('en; q=0.1', 'fr; q=0.4', 'bu; q=1.0')), + array('en; q=0.1, fr; q=0.4, fu; q=0.9, de; q=0.2', array('en; q=0.1', 'fr; q=0.4', 'fu; q=0.9', 'de; q=0.2')), + ); + } +} diff --git a/vendor/willdurand/negotiation/tests/Negotiation/Tests/MatchTest.php b/vendor/willdurand/negotiation/tests/Negotiation/Tests/MatchTest.php new file mode 100644 index 000000000..c570a0a58 --- /dev/null +++ b/vendor/willdurand/negotiation/tests/Negotiation/Tests/MatchTest.php @@ -0,0 +1,56 @@ +<?php + +namespace Negotiation\Tests; + +use Negotiation\Match; + +class MatchTest extends TestCase +{ + /** + * @dataProvider dataProviderForTestCompare + */ + public function testCompare($match1, $match2, $expected) + { + $this->assertEquals($expected, Match::compare($match1, $match2)); + } + + public static function dataProviderForTestCompare() + { + return array( + array(new Match(1.0, 110, 1), new Match(1.0, 111, 1), 0), + array(new Match(0.1, 10, 1), new Match(0.1, 10, 2), -1), + array(new Match(0.5, 110, 5), new Match(0.5, 11, 4), 1), + array(new Match(0.4, 110, 1), new Match(0.6, 111, 3), 1), + array(new Match(0.6, 110, 1), new Match(0.4, 111, 3), -1), + ); + } + + /** + * @dataProvider dataProviderForTestReduce + */ + public function testReduce($carry, $match, $expected) + { + $this->assertEquals($expected, Match::reduce($carry, $match)); + } + + public static function dataProviderForTestReduce() + { + return array( + array( + array(1 => new Match(1.0, 10, 1)), + new Match(0.5, 111, 1), + array(1 => new Match(0.5, 111, 1)), + ), + array( + array(1 => new Match(1.0, 110, 1)), + new Match(0.5, 11, 1), + array(1 => new Match(1.0, 110, 1)), + ), + array( + array(0 => new Match(1.0, 10, 1)), + new Match(0.5, 111, 1), + array(0 => new Match(1.0, 10, 1), 1 => new Match(0.5, 111, 1)), + ), + ); + } +} diff --git a/vendor/willdurand/negotiation/tests/Negotiation/Tests/NegotiatorTest.php b/vendor/willdurand/negotiation/tests/Negotiation/Tests/NegotiatorTest.php new file mode 100644 index 000000000..fe3ff767a --- /dev/null +++ b/vendor/willdurand/negotiation/tests/Negotiation/Tests/NegotiatorTest.php @@ -0,0 +1,195 @@ +<?php + +namespace Negotiation\Tests; + +use Negotiation\Exception\InvalidArgument; +use Negotiation\Exception\InvalidMediaType; +use Negotiation\Negotiator; +use Negotiation\Accept; +use Negotiation\Match; + +class NegotiatorTest extends TestCase +{ + + /** + * @var Negotiator + */ + private $negotiator; + + protected function setUp() + { + $this->negotiator = new Negotiator(); + } + + /** + * @dataProvider dataProviderForTestGetBest + */ + public function testGetBest($header, $priorities, $expected) + { + try { + $acceptHeader = $this->negotiator->getBest($header, $priorities); + } catch (\Exception $e) { + $this->assertEquals($expected, $e); + + return; + } + + if ($acceptHeader === null) { + $this->assertNull($expected); + + return; + } + + $this->assertInstanceOf('Negotiation\Accept', $acceptHeader); + + $this->assertSame($expected[0], $acceptHeader->getType()); + $this->assertSame($expected[1], $acceptHeader->getParameters()); + } + + public static function dataProviderForTestGetBest() + { + $pearAcceptHeader = 'text/html,application/xhtml+xml,application/xml;q=0.9,text/*;q=0.7,*/*,image/gif; q=0.8, image/jpeg; q=0.6, image/*'; + $rfcHeader = 'text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5'; + + return array( + # exceptions + array('/qwer', array('f/g'), null), + array('/qwer,f/g', array('f/g'), array('f/g', array())), + array('foo/bar', array('/qwer'), new InvalidMediaType()), + array('', array('foo/bar'), new InvalidArgument('The header string should not be empty.')), + array('*/*', array(), new InvalidArgument('A set of server priorities should be given.')), + + # See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + array($rfcHeader, array('text/html;level=1'), array('text/html', array('level' => '1'))), + array($rfcHeader, array('text/html'), array('text/html', array())), + array($rfcHeader, array('text/plain'), array('text/plain', array())), + array($rfcHeader, array('image/jpeg',), array('image/jpeg', array())), + array($rfcHeader, array('text/html;level=2'), array('text/html', array('level' => '2'))), + array($rfcHeader, array('text/html;level=3'), array('text/html', array( 'level' => '3'))), + + array('text/*;q=0.7, text/html;q=0.3, */*;q=0.5, image/png;q=0.4', array('text/html', 'image/png'), array('image/png', array())), + array('image/png;q=0.1, text/plain, audio/ogg;q=0.9', array('image/png', 'text/plain', 'audio/ogg'), array('text/plain', array())), + array('image/png, text/plain, audio/ogg', array('baz/asdf'), null), + array('image/png, text/plain, audio/ogg', array('audio/ogg'), array('audio/ogg', array())), + array('image/png, text/plain, audio/ogg', array('YO/SuP'), null), + array('text/html; charset=UTF-8, application/pdf', array('text/html; charset=UTF-8'), array('text/html', array('charset' => 'UTF-8'))), + array('text/html; charset=UTF-8, application/pdf', array('text/html'), null), + array('text/html, application/pdf', array('text/html; charset=UTF-8'), array('text/html', array('charset' => 'UTF-8'))), + # PEAR HTTP2 tests - have been altered from original! + array($pearAcceptHeader, array('image/gif', 'image/png', 'application/xhtml+xml', 'application/xml', 'text/html', 'image/jpeg', 'text/plain',), array('image/png', array())), + array($pearAcceptHeader, array('image/gif', 'application/xhtml+xml', 'application/xml', 'image/jpeg', 'text/plain',), array('application/xhtml+xml', array())), + array($pearAcceptHeader, array('image/gif', 'application/xml', 'image/jpeg', 'text/plain',), array('application/xml', array())), + array($pearAcceptHeader, array('image/gif', 'image/jpeg', 'text/plain'), array('image/gif', array())), + array($pearAcceptHeader, array('text/plain', 'image/png', 'image/jpeg'), array('image/png', array())), + array($pearAcceptHeader, array('image/jpeg', 'image/gif',), array('image/gif', array())), + array($pearAcceptHeader, array('image/png',), array('image/png', array())), + array($pearAcceptHeader, array('audio/midi',), array('audio/midi', array())), + array('text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', array( 'application/rss+xml'), array('application/rss+xml', array())), + # LWS / case sensitivity + array('text/* ; q=0.3, TEXT/html ;Q=0.7, text/html ; level=1, texT/Html ;leVel = 2 ;q=0.4, */* ; q=0.5', array( 'text/html; level=2'), array('text/html', array( 'level' => '2'))), + array('text/* ; q=0.3, text/html;Q=0.7, text/html ;level=1, text/html; level=2;q=0.4, */*;q=0.5', array( 'text/HTML; level=3'), array('text/html', array( 'level' => '3'))), + # Incompatible + array('text/html', array( 'application/rss'), null), + # IE8 Accept header + array('image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, */*', array( 'text/html', 'application/xhtml+xml'), array('text/html', array())), + # Quality of source factors + array($rfcHeader, array('text/html;q=0.4', 'text/plain'), array('text/plain', array())), + # Wildcard "plus" parts (e.g., application/vnd.api+json) + array('application/vnd.api+json', array('application/json', 'application/*+json'), array('application/*+json', array())), + array('application/json;q=0.7, application/*+json;q=0.7', array('application/hal+json', 'application/problem+json'), array('application/hal+json', array())), + array('application/json;q=0.7, application/problem+*;q=0.7', array('application/hal+xml', 'application/problem+xml'), array('application/problem+xml', array())), + array($pearAcceptHeader, array('application/*+xml'), array('application/*+xml', array())), + # @see https://github.com/willdurand/Negotiation/issues/93 + array('application/hal+json', array('application/ld+json', 'application/hal+json', 'application/xml', 'text/xml', 'application/json', 'text/html'), array('application/hal+json', array())), + ); + } + + public function testGetBestRespectsQualityOfSource() + { + $accept = $this->negotiator->getBest('text/html,text/*;q=0.7', array('text/html;q=0.5', 'text/plain;q=0.9')); + $this->assertInstanceOf('Negotiation\Accept', $accept); + $this->assertEquals('text/plain', $accept->getType()); + } + + /** + * @dataProvider dataProviderForTestParseHeader + */ + public function testParseHeader($header, $expected) + { + $accepts = $this->call_private_method('Negotiation\Negotiator', 'parseHeader', $this->negotiator, array($header)); + + $this->assertSame($expected, $accepts); + } + + public static function dataProviderForTestParseHeader() + { + return array( + array('text/html ; q=0.9', array('text/html ; q=0.9')), + array('text/html,application/xhtml+xml', array('text/html', 'application/xhtml+xml')), + array(',,text/html;q=0.8 , , ', array('text/html;q=0.8')), + array('text/html;charset=utf-8; q=0.8', array('text/html;charset=utf-8; q=0.8')), + array('text/html; foo="bar"; q=0.8 ', array('text/html; foo="bar"; q=0.8')), + array('text/html; foo="bar"; qwer="asdf", image/png', array('text/html; foo="bar"; qwer="asdf"', "image/png")), + array('text/html ; quoted_comma="a,b ,c,",application/xml;q=0.9,*/*;charset=utf-8; q=0.8', array('text/html ; quoted_comma="a,b ,c,"', 'application/xml;q=0.9', '*/*;charset=utf-8; q=0.8')), + array('text/html, application/json;q=0.8, text/csv;q=0.7', array('text/html', 'application/json;q=0.8', 'text/csv;q=0.7')) + ); + } + + /** + * @dataProvider dataProviderForTestFindMatches + */ + public function testFindMatches($headerParts, $priorities, $expected) + { + $neg = new Negotiator(); + + $matches = $this->call_private_method('Negotiation\Negotiator', 'findMatches', $neg, array($headerParts, $priorities)); + + $this->assertEquals($expected, $matches); + } + + public static function dataProviderForTestFindMatches() + { + return array( + array( + array(new Accept('text/html; charset=UTF-8'), new Accept('image/png; foo=bar; q=0.7'), new Accept('*/*; foo=bar; q=0.4')), + array(new Accept('text/html; charset=UTF-8'), new Accept('image/png; foo=bar'), new Accept('application/pdf')), + array( + new Match(1.0, 111, 0), + new Match(0.7, 111, 1), + new Match(0.4, 1, 1), + ) + ), + array( + array(new Accept('text/html'), new Accept('image/*; q=0.7')), + array(new Accept('text/html; asfd=qwer'), new Accept('image/png'), new Accept('application/pdf')), + array( + new Match(1.0, 110, 0), + new Match(0.7, 100, 1), + ) + ), + array( # https://tools.ietf.org/html/rfc7231#section-5.3.2 + array(new Accept('text/*; q=0.3'), new Accept('text/html; q=0.7'), new Accept('text/html; level=1'), new Accept('text/html; level=2; q=0.4'), new Accept('*/*; q=0.5')), + array(new Accept('text/html; level=1'), new Accept('text/html'), new Accept('text/plain'), new Accept('image/jpeg'), new Accept('text/html; level=2'), new Accept('text/html; level=3')), + array( + new Match(0.3, 100, 0), + new Match(0.7, 110, 0), + new Match(1.0, 111, 0), + new Match(0.5, 0, 0), + new Match(0.3, 100, 1), + new Match(0.7, 110, 1), + new Match(0.5, 0, 1), + new Match(0.3, 100, 2), + new Match(0.5, 0, 2), + new Match(0.5, 0, 3), + new Match(0.3, 100, 4), + new Match(0.7, 110, 4), + new Match(0.4, 111, 4), + new Match(0.5, 0, 4), + new Match(0.3, 100, 5), + new Match(0.7, 110, 5), + new Match(0.5, 0, 5), + ) + ) + ); + } +} diff --git a/vendor/willdurand/negotiation/tests/Negotiation/Tests/TestCase.php b/vendor/willdurand/negotiation/tests/Negotiation/Tests/TestCase.php new file mode 100644 index 000000000..37f936404 --- /dev/null +++ b/vendor/willdurand/negotiation/tests/Negotiation/Tests/TestCase.php @@ -0,0 +1,15 @@ +<?php + +namespace Negotiation\Tests; + +abstract class TestCase extends \PHPUnit_Framework_TestCase +{ + protected function call_private_method($class, $method, $object, $params) + { + $method = new \ReflectionMethod($class, $method); + + $method->setAccessible(true); + + return $method->invokeArgs($object, $params); + } +} diff --git a/vendor/willdurand/negotiation/tests/bootstrap.php b/vendor/willdurand/negotiation/tests/bootstrap.php new file mode 100644 index 000000000..c63e17a68 --- /dev/null +++ b/vendor/willdurand/negotiation/tests/bootstrap.php @@ -0,0 +1,15 @@ +<?php + +if (! ($loader = @include __DIR__ . '/../vendor/autoload.php')) { + die(<<<EOT +You need to install the project dependencies using Composer: +$ wget http://getcomposer.org/composer.phar +OR +$ curl -s https://getcomposer.org/installer | php +$ php composer.phar install --dev +$ phpunit +EOT + ); +} + +$loader->add('Negotiation\Tests', __DIR__); -- GitLab