Completed
Push — master ( 12bc5d...f69768 )
by Oscar
58:41
created

LanguageNegotiator   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 3
Bugs 1 Features 1
Metric Value
wmc 12
c 3
b 1
f 1
lcom 1
cbo 8
dl 0
loc 104
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getLanguage() 0 4 1
A __construct() 0 4 1
A usePath() 0 6 1
D __invoke() 0 40 9
1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use Psr7Middlewares\Middleware;
6
use Psr7Middlewares\Utils;
7
use Negotiation\LanguageNegotiator as Negotiator;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
11
/**
12
 * Middleware returns the client preferred language.
13
 */
14
class LanguageNegotiator
15
{
16
    use Utils\NegotiateTrait;
17
    use Utils\BasePathTrait;
18
    use Utils\RedirectTrait;
19
20
    const KEY = 'LANGUAGE';
21
22
    /**
23
     * @var array Allowed languages
24
     */
25
    private $languages = [];
26
27
    /**
28
     * @var bool Use the path to detect the language
29
     */
30
    private $usePath = false;
31
32
    /**
33
     * Returns the language.
34
     *
35
     * @param ServerRequestInterface $request
36
     *
37
     * @return string|null
38
     */
39
    public static function getLanguage(ServerRequestInterface $request)
40
    {
41
        return Middleware::getAttribute($request, self::KEY);
42
    }
43
44
    /**
45
     * Define de available languages.
46
     *
47
     * @param array $languages
48
     */
49
    public function __construct(array $languages)
50
    {
51
        $this->languages = $languages;
52
    }
53
54
    /**
55
     * Use the base path to detect the current language.
56
     *
57
     * @param bool $usePath
58
     *
59
     * @return self
60
     */
61
    public function usePath($usePath = true)
62
    {
63
        $this->usePath = $usePath;
64
65
        return $this;
66
    }
67
68
    /**
69
     * Execute the middleware.
70
     *
71
     * @param ServerRequestInterface $request
72
     * @param ResponseInterface      $response
73
     * @param callable               $next
74
     *
75
     * @return ResponseInterface
76
     */
77
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
78
    {
79
        $language = null;
80
81
        //Use path
82
        if ($this->usePath) {
83
            $uri = $request->getUri();
84
            $path = ltrim($this->getPath($uri->getPath()), '/');
85
86
            $dirs = explode('/', $path, 2);
87
            $first = array_shift($dirs);
88
89
            if (!empty($first) && in_array($first, $this->languages, true)) {
90
                $language = $first;
91
92
                //remove the language in the path
93
                $request = $request->withUri($uri->withPath('/'.array_shift($dirs)));
94
            }
95
        }
96
97
        //Use http headers
98
        if ($language === null) {
99
            $language = $this->negotiateHeader($request->getHeaderLine('Accept-Language'), new Negotiator(), $this->languages);
100
101
            if (empty($language)) {
102
                $language = isset($this->languages[0]) ? $this->languages[0] : null;
103
            }
104
105
            //Redirect to a path with the language
106
            if ($this->redirectStatus && $this->usePath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->redirectStatus of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
107
                $path = Utils\Helpers::joinPath($this->basePath, $language, $this->getPath($uri->getPath()));
0 ignored issues
show
Bug introduced by
The variable $uri does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
108
109
                return self::getRedirectResponse($this->redirectStatus, $uri->withPath($path), $response);
110
            }
111
        }
112
113
        $request = Middleware::setAttribute($request, self::KEY, $language);
114
115
        return $next($request, $response);
116
    }
117
}
118