GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 27a152...e09222 )
by Jan-Petter
03:18
created

UserAgentParser::validateVersion()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
rs 8.8571
cc 5
eloc 7
nc 2
nop 0
1
<?php
2
namespace vipnytt;
3
4
use vipnytt\UserAgentParser\Exceptions\ProductException;
5
use vipnytt\UserAgentParser\Exceptions\VersionException;
6
7
/**
8
 * Class UserAgentParser
9
 *
10
 * @link https://tools.ietf.org/html/rfc7231#section-5.5.3
11
 * @link https://tools.ietf.org/html/rfc7230
12
 *
13
 * @package vipnytt
14
 */
15
class UserAgentParser
16
{
17
    const RFC_README = 'https://tools.ietf.org/html/rfc7231#section-5.5.3';
18
19
    /**
20
     * Product
21
     * @var string
22
     */
23
    private $product;
24
25
    /**
26
     * Version
27
     * @var string
28
     */
29
    private $version;
30
31
    /**
32
     * Constructor
33
     *
34
     * @param string $product
35
     * @param int|string|null $version
36
     */
37
    public function __construct($product, $version = null)
38
    {
39
        $this->product = $product;
40
        $this->version = $version;
0 ignored issues
show
Documentation Bug introduced by
It seems like $version can also be of type integer. However, the property $version is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
41
        if (strpos($this->product, '/') !== false) {
42
            $this->split();
43
        }
44
        $this->validateProduct();
45
        $this->validateVersion();
46
    }
47
48
    private function split()
49
    {
50
        if (count($parts = explode('/', trim($this->product . '/' . $this->version, '/'), 2)) === 2) {
51
            $this->product = $parts[0];
52
            $this->version = $parts[1];
53
        }
54
    }
55
56
    /**
57
     * Validate the Product format
58
     * @link https://tools.ietf.org/html/rfc7230#section-3.2.4
59
     *
60
     * @throws ProductException
61
     */
62
    private function validateProduct()
63
    {
64
        foreach (
65
            [
66
                'mozilla',
67
                'compatible',
68
                '(',
69
                ')',
70
                ' ',
71
            ] as $blacklisted
72
        ) {
73
            if (
74
                stripos($this->product, $blacklisted) !== false ||
75
                empty($this->product)
76
            ) {
77
                throw new ProductException('Invalid product format (`' . $this->product . '`). Examples of valid User-agents: `MyCustomBot`, `MyFetcher-news`, `MyCrawler/2.1` and `MyBot-images/1.2`. See also ' . self::RFC_README);
78
            }
79
        }
80
        if ($this->product !== ($new = preg_replace('/[^\x21-\x7E]/', '', $this->product))) {
81
            trigger_error("Product name contains invalid characters. Truncated to `$new`.", E_USER_WARNING);
82
        }
83
    }
84
85
    /**
86
     * Validate the Version and it's format
87
     *
88
     * @throws VersionException
89
     */
90
    private function validateVersion()
91
    {
92
        if (
93
            $this->version !== null &&
94
            (
95
                empty($this->version) ||
96
                preg_match('/[^0-9.]/', $this->version) ||
97
                version_compare($this->version, '0.0.1', '>=') === false
98
            )
99
        ) {
100
            throw new VersionException('Invalid version format (`' . $this->version . '`). See http://semver.org/ for guidelines. In addition, dev/alpha/beta/rc tags is disallowed. See also ' . self::RFC_README);
101
        }
102
    }
103
104
    /**
105
     * Find the best matching User-agent
106
     *
107
     * @param string[] $userAgents
108
     * @return string|false
109
     */
110
    public function getMostSpecific(array $userAgents)
111
    {
112
        $array = [];
113
        foreach ($userAgents as $string) {
114
            // Strip non-US-ASCII characters
115
            $array[$string] = strtolower(preg_replace('/[^\x21-\x7E]/', '', $string));
116
        }
117
        foreach (array_map('strtolower', $this->getUserAgents()) as $generated) {
118
            if (($result = array_search($generated, $array)) !== false) {
119
                // Match found
120
                return $result;
121
            }
122
        }
123
        return false;
124
    }
125
126
    /**
127
     * Get an array of all possible User-agent combinations
128
     *
129
     * @return array
130
     */
131
    public function getUserAgents()
132
    {
133
        return array_merge(
134
            preg_filter('/^/', $this->product . '/', $this->getVersions()),
135
            $this->getProducts()
136
        );
137
    }
138
139
    /**
140
     * Get versions
141
     *
142
     * @return array
143
     */
144
    public function getVersions()
145
    {
146
        while (substr_count($this->version, '.') < 2) {
147
            $this->version .= '.0';
148
        }
149
        // Remove part by part of the version.
150
        $result = array_merge(
151
            [$this->version],
152
            $this->explode($this->version, '.')
153
        );
154
        asort($result);
155
        usort($result, function ($a, $b) {
156
            return strlen($b) - strlen($a);
157
        });
158
        return $this->filter($result);
159
    }
160
161
    /**
162
     * Explode
163
     *
164
     * @param string $string
165
     * @param string $delimiter
166
     * @return array
167
     */
168
    private function explode($string, $delimiter)
169
    {
170
        $result = [];
171
        while (($pos = strrpos($string, $delimiter)) !== false) {
172
            $result[] = ($string = substr($string, 0, $pos));
173
        }
174
        return $result;
175
    }
176
177
    /**
178
     * Filter duplicates from an array
179
     *
180
     * @param string[] $array
181
     * @return array
182
     */
183
    private function filter($array)
184
    {
185
        $result = [];
186
        foreach ($array as $value) {
187
            if (!in_array($array, $result)) {
188
                $result[] = $value;
189
            }
190
        }
191
        return array_filter($result);
192
    }
193
194
    /**
195
     * Get products
196
     *
197
     * @return array
198
     */
199
    public function getProducts()
200
    {
201
        $result = array_merge(
202
            [
203
                $this->product,
204
                preg_replace('/[^A-Za-z0-9]/', '', $this->product), // in case of special characters
205
            ],
206
            $this->explode($this->product, '-')
207
        );
208
        return $this->filter($result);
209
    }
210
211
    /**
212
     * Get User-agent
213
     *
214
     * @return string
215
     */
216
    public function getUserAgent()
217
    {
218
        return $this->getVersion() === null ? $this->getProduct() : $this->getProduct() . '/' . $this->getVersion();
219
    }
220
221
    /**
222
     * Get version
223
     *
224
     * @return string
225
     */
226
    public function getVersion()
227
    {
228
        if (empty($this->version)) {
229
            return null;
230
        }
231
        return trim($this->version, '.0');
232
    }
233
234
    /**
235
     * Get product
236
     *
237
     * @return string
238
     */
239
    public function getProduct()
240
    {
241
        return $this->product;
242
    }
243
}
244