Skip to content

Commit

Permalink
Refactoring Negotiator + Testing
Browse files Browse the repository at this point in the history
  • Loading branch information
arcanedev-maroc committed Sep 24, 2015
1 parent 86539f7 commit 7566e63
Show file tree
Hide file tree
Showing 2 changed files with 272 additions and 24 deletions.
130 changes: 107 additions & 23 deletions src/Utilities/Negotiator.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class Negotiator implements NegotiatorInterface
*/
private $supportedLocales;

/**
* @var Request
*/
private $request;

/* ------------------------------------------------------------------------------------------------
| Main Functions
| ------------------------------------------------------------------------------------------------
Expand All @@ -56,6 +61,24 @@ public function __construct($defaultLocale, $supportedLanguages)
$this->supportedLocales = $supportedLanguages;
}

/* ------------------------------------------------------------------------------------------------
| Getters & Setters
| ------------------------------------------------------------------------------------------------
*/
/**
* Set request instance.
*
* @param Request $request
*
* @return self
*/
private function setRequest(Request $request)
{
$this->request = $request;

return $this;
}

/* ------------------------------------------------------------------------------------------------
| Main Functions
| ------------------------------------------------------------------------------------------------
Expand All @@ -69,43 +92,104 @@ public function __construct($defaultLocale, $supportedLanguages)
*/
public function negotiate(Request $request)
{
$matches = $this->getMatchesFromAcceptedLanguages($request);
$this->setRequest($request);

foreach (array_keys($matches) as $locale) {
if ($this->isSupported($locale))
return $locale;
$locale = $this->getFromAcceptedLanguagesHeader();

if ( ! is_null($locale)) return $locale;

$locale = $this->getFromHttpAcceptedLanguagesServer();

if ( ! is_null($locale)) return $locale;

$locale = $this->getFromRemoteHostServer();

if ( ! is_null($locale)) return $locale;

// TODO: Adding negotiate form IP Address ??

return $this->defaultLocale;
}

/**
* Get locale from accepted languages header.
*
* @return null|string
*/
private function getFromAcceptedLanguagesHeader()
{
$matches = $this->getMatchesFromAcceptedLanguages();

if ($locale = $this->inSupportedLocales($matches)) {
return $locale;
}

// If any (i.e. "*") is acceptable, return the first supported format
if (isset($matches[ '*' ])) {
/** @var \Arcanedev\Localization\Entities\Locale $locale */
$locale = $this->supportedLocales->first();
// If any (i.e. "*") is acceptable, return the first supported locale
if (isset($matches['*'])) {
return $this->supportedLocales->first()->key();
}

return null;
}

/**
* Get locale from http accepted languages server.
*
* @return null|string
*/
private function getFromHttpAcceptedLanguagesServer()
{
$httpAcceptLanguage = $this->request->server('HTTP_ACCEPT_LANGUAGE');

return $locale->key();
// @codeCoverageIgnoreStart
if ( ! class_exists('Locale') || empty($httpAcceptLanguage)) {
return null;
}
// @codeCoverageIgnoreEnd

if (class_exists('Locale') && ! empty($request->server('HTTP_ACCEPT_LANGUAGE'))) {
$httpAcceptLanguage = Locale::acceptFromHttp($request->server('HTTP_ACCEPT_LANGUAGE'));
$locale = Locale::acceptFromHttp($httpAcceptLanguage);

if ($this->isSupported($httpAcceptLanguage))
return $httpAcceptLanguage;
if ($this->isSupported($locale)) {
return $locale;
}

if ($request->server('REMOTE_HOST')) {
$remote_host = explode('.', $request->server('REMOTE_HOST'));
$locale = strtolower(end($remote_host));
return null;
}

if ($this->isSupported($locale))
return $locale;
private function getFromRemoteHostServer()
{
if (empty($remoteHost = $this->request->server('REMOTE_HOST'))) {
return null;
}

return $this->defaultLocale;
$remoteHost = explode('.', $remoteHost);
$locale = strtolower(end($remoteHost));

if ($this->isSupported($locale)) return $locale;

return null;
}

/* ------------------------------------------------------------------------------------------------
| Check Functions
| ------------------------------------------------------------------------------------------------
*/
/**
* Check if matches a supported locale.
*
* @param array $matches
*
* @return null|string
*/
private function inSupportedLocales(array $matches)
{
foreach (array_keys($matches) as $locale) {
if ($this->isSupported($locale)) return $locale;
}

return null;
}

/**
* Check if the locale is supported.
*
Expand All @@ -125,15 +209,15 @@ private function isSupported($locale)
/**
* Return all the accepted languages from the browser
*
* @param Request $request
*
* @return array - Matches from the header field Accept-Languages
*/
private function getMatchesFromAcceptedLanguages(Request $request)
private function getMatchesFromAcceptedLanguages()
{
$matches = [];

if ($acceptLanguages = $request->header('Accept-Language')) {
$acceptLanguages = $this->request->header('Accept-Language');

if ( ! empty($acceptLanguages)) {
$acceptLanguages = explode(',', $acceptLanguages);

$genericMatches = $this->retrieveGenericMatches($acceptLanguages, $matches);
Expand Down
166 changes: 165 additions & 1 deletion tests/Utilities/NegotiatorTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?php namespace Arcanedev\Localization\Tests\Utilities;

use Arcanedev\Localization\Tests\TestCase;
use Arcanedev\Localization\Utilities\Negotiator;
use Illuminate\Http\Request;
use Prophecy\Prophecy\ObjectProphecy;

/**
* Class NegotiatorTest
Expand All @@ -10,18 +13,29 @@
*/
class NegotiatorTest extends TestCase
{
/* ------------------------------------------------------------------------------------------------
| Properties
| ------------------------------------------------------------------------------------------------
*/
/** @var Negotiator */
private $negotiator;

/* ------------------------------------------------------------------------------------------------
| Main Functions
| ------------------------------------------------------------------------------------------------
*/
public function setUp()
{
parent::setUp();

$this->negotiator = app('arcanedev.localization.negotiator');
}

public function tearDown()
{
parent::tearDown();

unset($this->negotiator);
}

/* ------------------------------------------------------------------------------------------------
Expand All @@ -31,6 +45,156 @@ public function tearDown()
/** @test */
public function it_can_be_instantiated()
{
$this->assertTrue(true); // TODO: Adding real tests
$this->assertInstanceOf(Negotiator::class, $this->negotiator);
}

/** @test */
public function it_can_negotiate_supported_accepted_languages_header()
{
$languages = [
['en', 'en-us, en; q=0.5'],
['fr', 'en; q=0.5, fr; q=1.0'],
['es', 'es; q=0.6, en; q=0.5, fr; q=0.5'],
];

foreach ($languages as $language) {
/** @var Request $request */
$request = $this->mockRequestWithAcceptLanguage($language[1])->reveal();

$this->assertEquals($language[0], $this->negotiator->negotiate($request));
}
}

/** @test */
public function it_can_negotiate_any_accepted_languages_header()
{
/** @var Request $request */
$request = $this->mockRequestWithAcceptLanguage('*')->reveal();

$this->assertEquals('en', $this->negotiator->negotiate($request));
}

/** @test */
public function it_can_negotiate_supported_http_accepted_languages_server()
{
/** @var Request $request */
$request = $this->mockRequestWithHttpAcceptLanguage('fr;q=0.8,en;q=0.4', 'jp; q=1.0')->reveal();

$this->assertEquals('fr', $this->negotiator->negotiate($request));

$request = $this->mockRequestWithHttpAcceptLanguage('fr;q=0.8,en;q=0.4', '*/*')->reveal();

$this->assertEquals('fr', $this->negotiator->negotiate($request));
}

/** @test */
public function it_can_negotiate_supported_remote_host_server()
{
/** @var Request $request */
$request = $this->mockRequestWithRemoteHostServer(
'http://www.omelette-au-fromage.fr',
'ar;q=0.8,sv;q=0.4',
'jp; q=1.0'
)->reveal();

$this->assertEquals('fr', $this->negotiator->negotiate($request));
}

/** @test */
public function it_can_negotiate_unsupported_remote_host_server()
{
/** @var Request $request */
$request = $this->mockRequestWithRemoteHostServer(
'http://www.sushi.jp',
'ar;q=0.8,sv;q=0.4',
'jp; q=1.0'
)->reveal();

$this->assertEquals('en', $this->negotiator->negotiate($request));
}

/** @test */
public function it_can_negotiate_undefined_remote_host_server()
{
/** @var Request $request */
$request = $this->mockRequestWithRemoteHostServer(
null,
'ar;q=0.8,sv;q=0.4',
'jp; q=1.0'
)->reveal();

$this->assertEquals('en', $this->negotiator->negotiate($request));
}

/* ------------------------------------------------------------------------------------------------
| Other Functions
| ------------------------------------------------------------------------------------------------
*/
/**
* Mock request.
*
* @return ObjectProphecy
*/
private function mockRequest()
{
$request = $this->prophesize(Request::class);

return $request;
}

/**
* Mock request with accept language header.
*
* @param string $acceptLanguages
*
* @return ObjectProphecy
*/
private function mockRequestWithAcceptLanguage($acceptLanguages)
{
$request = $this->mockRequest();

$request->header('Accept-Language')
->willReturn($acceptLanguages)
->shouldBeCalled();

return $request;
}

/**
* Mock request with HTTP Accept Language server.
*
* @param string $acceptLanguages
*
* @return ObjectProphecy
*/
private function mockRequestWithHttpAcceptLanguage($httpAcceptLanguages, $acceptLanguages)
{
$request = $this->mockRequestWithAcceptLanguage($acceptLanguages);

$request->server('HTTP_ACCEPT_LANGUAGE')
->willReturn($httpAcceptLanguages)
->shouldBeCalled();

return $request;
}

/**
* Mock request with REMOTE_HOST server.
*
* @param string $remoteHost
* @param string $httpAcceptLanguages
* @param string $acceptLanguages
*
* @return ObjectProphecy
*/
private function mockRequestWithRemoteHostServer($remoteHost, $httpAcceptLanguages, $acceptLanguages)
{
$request = $this->mockRequestWithHttpAcceptLanguage($httpAcceptLanguages, $acceptLanguages);

$request->server('REMOTE_HOST')
->willReturn($remoteHost)
->shouldBeCalled();

return $request;
}
}

0 comments on commit 7566e63

Please sign in to comment.