Completed
Push — master ( a76ab0...72ddbe )
by Fosco
7s
created

FacebookUrlDetectionHandler::getHostName()   D

Complexity

Conditions 9
Paths 8

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 28
rs 4.909
cc 9
eloc 14
nc 8
nop 0
1
<?php
2
/**
3
 * Copyright 2016 Facebook, Inc.
4
 *
5
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
6
 * use, copy, modify, and distribute this software in source code or binary
7
 * form for use in connection with the web services and APIs provided by
8
 * Facebook.
9
 *
10
 * As with any software that integrates with the Facebook platform, your use
11
 * of this software is subject to the Facebook Developer Principles and
12
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
13
 * shall be included in all copies or substantial portions of the software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
 * DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
namespace Facebook\Url;
25
26
/**
27
 * Class FacebookUrlDetectionHandler
28
 *
29
 * @package Facebook
30
 */
31
class FacebookUrlDetectionHandler implements UrlDetectionInterface
32
{
33
    /**
34
     * @inheritdoc
35
     */
36
    public function getCurrentUrl()
37
    {
38
        return $this->getHttpScheme() . '://' . $this->getHostName() . $this->getServerVar('REQUEST_URI');
39
    }
40
41
    /**
42
     * Get the currently active URL scheme.
43
     *
44
     * @return string
45
     */
46
    protected function getHttpScheme()
47
    {
48
        return $this->isBehindSsl() ? 'https' : 'http';
49
    }
50
51
    /**
52
     * Tries to detect if the server is running behind an SSL.
53
     *
54
     * @return boolean
55
     */
56
    protected function isBehindSsl()
57
    {
58
        // Check for proxy first
59
        $protocol = $this->getHeader('X_FORWARDED_PROTO');
60
        if ($protocol) {
61
            return $this->protocolWithActiveSsl($protocol);
62
        }
63
64
        $protocol = $this->getServerVar('HTTPS');
65
        if ($protocol) {
66
            return $this->protocolWithActiveSsl($protocol);
67
        }
68
69
        return (string)$this->getServerVar('SERVER_PORT') === '443';
70
    }
71
72
    /**
73
     * Detects an active SSL protocol value.
74
     *
75
     * @param string $protocol
76
     *
77
     * @return boolean
78
     */
79
    protected function protocolWithActiveSsl($protocol)
80
    {
81
        $protocol = strtolower((string)$protocol);
82
83
        return in_array($protocol, ['on', '1', 'https', 'ssl'], true);
84
    }
85
86
    /**
87
     * Tries to detect the host name of the server.
88
     *
89
     * Some elements adapted from
90
     *
91
     * @see https://github.com/symfony/HttpFoundation/blob/master/Request.php
92
     *
93
     * @return string
94
     */
95
    protected function getHostName()
96
    {
97
        // Check for proxy first
98
        if ($header = $this->getHeader('X_FORWARDED_HOST') && $this->isValidForwardedHost($header)) {
0 ignored issues
show
Bug introduced by
The variable $header seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: $header = ($this->getHea...ForwardedHost($header)), Probably Intended Meaning: ($header = $this->getHea...dForwardedHost($header)
Loading history...
99
            $elements = explode(',', $header);
100
            $host = $elements[count($elements) - 1];
101
        } elseif (!$host = $this->getHeader('HOST')) {
102
            if (!$host = $this->getServerVar('SERVER_NAME')) {
103
                $host = $this->getServerVar('SERVER_ADDR');
104
            }
105
        }
106
107
        // trim and remove port number from host
108
        // host is lowercase as per RFC 952/2181
109
        $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
110
111
        // Port number
112
        $scheme = $this->getHttpScheme();
113
        $port = $this->getCurrentPort();
114
        $appendPort = ':' . $port;
115
116
        // Don't append port number if a normal port.
117
        if (($scheme == 'http' && $port == '80') || ($scheme == 'https' && $port == '443')) {
118
            $appendPort = '';
119
        }
120
121
        return $host . $appendPort;
122
    }
123
124
    protected function getCurrentPort()
125
    {
126
        // Check for proxy first
127
        $port = $this->getHeader('X_FORWARDED_PORT');
128
        if ($port) {
129
            return (string)$port;
130
        }
131
132
        $protocol = (string)$this->getHeader('X_FORWARDED_PROTO');
133
        if ($protocol === 'https') {
134
            return '443';
135
        }
136
137
        return (string)$this->getServerVar('SERVER_PORT');
138
    }
139
140
    /**
141
     * Returns the a value from the $_SERVER super global.
142
     *
143
     * @param string $key
144
     *
145
     * @return string
146
     */
147
    protected function getServerVar($key)
0 ignored issues
show
Coding Style introduced by
getServerVar uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
148
    {
149
        return isset($_SERVER[$key]) ? $_SERVER[$key] : '';
150
    }
151
152
    /**
153
     * Gets a value from the HTTP request headers.
154
     *
155
     * @param string $key
156
     *
157
     * @return string
158
     */
159
    protected function getHeader($key)
160
    {
161
        return $this->getServerVar('HTTP_' . $key);
162
    }
163
164
    /**
165
     * Checks if the value in X_FORWARDED_HOST is a valid hostname
166
     * Could prevent unintended redirections
167
     *
168
     * @param string $header
169
     *
170
     * @return boolean
171
     */
172
    protected function isValidForwardedHost($header)
173
    {
174
        $elements = explode(',', $header);
175
        $host = $elements[count($elements) - 1];
176
        
177
        return preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $host) //valid chars check
178
            && 0 < strlen($host) && strlen($host) < 254 //overall length check
179
            && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $host); //length of each label
180
    }
181
}
182