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 ( 9ee818...699fee )
by Jan-Petter
01:57
created

UserAgentParser::validateProduct()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 3
eloc 7
nc 4
nop 0
1
<?php
2
namespace vipnytt;
3
4
use vipnytt\UserAgentParser\Exceptions\FormatException;
5
use vipnytt\UserAgentParser\Exceptions\ProductException;
6
use vipnytt\UserAgentParser\Exceptions\VersionException;
7
8
/**
9
 * Class UserAgentParser
10
 *
11
 * @link https://tools.ietf.org/html/rfc7231#section-5.5.3
12
 * @link https://tools.ietf.org/html/rfc7230
13
 *
14
 * @package vipnytt
15
 */
16
class UserAgentParser
17
{
18
    /**
19
     * RFC 7231 - Section 5.5.3 - User-agent
20
     */
21
    const RFC_README = 'https://tools.ietf.org/html/rfc7231#section-5.5.3';
22
23
    /**
24
     * PREG pattern for valid characters
25
     */
26
    const PREG_PATTERN = '/[^\x21-\x7E]/';
27
28
    /**
29
     * Product
30
     * @var string
31
     */
32
    private $product;
33
34
    /**
35
     * Version
36
     * @var int|string|null
37
     */
38
    private $version;
39
40
    /**
41
     * Constructor
42
     *
43
     * @param string $product
44
     * @param int|string|null $version
45
     */
46
    public function __construct($product, $version = null)
47
    {
48
        $this->product = $product;
49
        $this->version = $version;
50
        if (strpos($this->product, '/') !== false) {
51
            $this->split();
52
        }
53
        $this->blacklistCheck();
54
        $this->validateProduct();
55
        $this->validateVersion();
56
    }
57
58
    /**
59
     * Split Product and Version
60
     *
61
     * @return bool
62
     */
63
    private function split()
64
    {
65
        if (count($parts = explode('/', trim($this->product . '/' . $this->version, '/'), 2)) === 2) {
66
            $this->product = $parts[0];
67
            $this->version = $parts[1];
68
        }
69
        return true;
70
    }
71
72
    /**
73
     * @throws ProductException
74
     */
75
    private function blacklistCheck()
76
    {
77
        foreach (
78
            [
79
                'mozilla',
80
                'compatible',
81
                '(',
82
                ')',
83
                ' ',
84
            ] as $string
85
        ) {
86
            if (stripos($this->product, $string) !== false) {
87
                throw new FormatException('Invalid User-agent format (`' . trim($this->product . '/' . $this->version, '/') . '`). Examples of valid User-agents: `MyCustomBot`, `MyFetcher-news`, `MyCrawler/2.1` and `MyBot-images/1.2`. See also ' . self::RFC_README);
88
            }
89
        }
90
    }
91
92
    /**
93
     * Validate the Product format
94
     * @link https://tools.ietf.org/html/rfc7230#section-3.2.4
95
     *
96
     * @return bool
97
     * @throws ProductException
98
     */
99
    private function validateProduct()
100
    {
101
        $old = $this->product;
102
        if ($old !== ($this->product = preg_replace(self::PREG_PATTERN, '', $this->product))) {
103
            trigger_error("Product name contains invalid characters. Truncated to `$this->product`.", E_USER_WARNING);
104
        }
105
        if (empty($this->product)) {
106
            throw new ProductException('Product string cannot be empty.');
107
        }
108
        return true;
109
    }
110
111
    /**
112
     * Validate the Version and it's format
113
     *
114
     * @return bool
115
     * @throws VersionException
116
     */
117
    private function validateVersion()
118
    {
119
        if (
120
            $this->version !== null &&
121
            (
122
                empty($this->version) ||
123
                preg_match('/[^0-9.]/', $this->version) ||
124
                version_compare($this->version, '0.0.1', '>=') === false
125
            )
126
        ) {
127
            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);
128
        }
129
        $this->version = trim($this->version, '.0');
130
        return true;
131
    }
132
133
    /**
134
     * Get User-agent
135
     *
136
     * @return string
137
     */
138
    public function getUserAgent()
139
    {
140
        return trim($this->getProduct() . '/' . $this->getVersion(), '/');
141
    }
142
143
    /**
144
     * Get product
145
     *
146
     * @return string
147
     */
148
    public function getProduct()
149
    {
150
        return $this->product;
151
    }
152
153
    /**
154
     * Get version
155
     *
156
     * @return string|null
157
     */
158
    public function getVersion()
159
    {
160
        return empty($this->version) ? null : $this->version;
161
    }
162
163
    /**
164
     * Find the best matching User-agent
165
     *
166
     * @param string[] $userAgents
167
     * @return string|false
168
     */
169
    public function getMostSpecific(array $userAgents)
170
    {
171
        $array = [];
172
        foreach ($userAgents as $string) {
173
            // Strip non-US-ASCII characters
174
            $array[$string] = strtolower(preg_replace(self::PREG_PATTERN, '', $string));
175
        }
176
        foreach (array_map('strtolower', $this->getUserAgents()) as $generated) {
177
            if (($result = array_search($generated, $array)) !== false) {
178
                // Match found
179
                return $result;
180
            }
181
        }
182
        return false;
183
    }
184
185
    /**
186
     * Get an array of all possible User-agent combinations
187
     *
188
     * @return array
189
     */
190
    public function getUserAgents()
191
    {
192
        return array_merge(
193
            preg_filter('/^/', $this->product . '/', $this->getVersions()),
194
            $this->getProducts()
195
        );
196
    }
197
198
    /**
199
     * Get versions
200
     *
201
     * @return array
202
     */
203
    public function getVersions()
204
    {
205
        while (
206
            substr_count($this->version, '.'
207
            ) < 2) {
208
            $this->version .= '.0';
209
        }
210
        // Remove part by part of the version.
211
        $result = array_merge(
212
            [$this->version],
213
            $this->explode($this->version, '.')
214
        );
215
        asort($result);
216
        usort($result, function ($a, $b) {
217
            return strlen($b) - strlen($a);
218
        });
219
        return $this->filterDuplicates($result);
220
    }
221
222
    /**
223
     * Explode
224
     *
225
     * @param string $string
226
     * @param string $delimiter
227
     * @return array
228
     */
229
    private function explode($string, $delimiter)
230
    {
231
        $result = [];
232
        while (($pos = strrpos($string, $delimiter)) !== false) {
233
            $result[] = ($string = substr($string, 0, $pos));
234
        }
235
        return $result;
236
    }
237
238
    /**
239
     * Filter duplicates from an array
240
     *
241
     * @param string[] $array
242
     * @return array
243
     */
244
    private function filterDuplicates($array)
245
    {
246
        $result = [];
247
        foreach ($array as $value) {
248
            if (!in_array($array, $result)) {
249
                $result[] = $value;
250
            }
251
        }
252
        return array_filter($result);
253
    }
254
255
    /**
256
     * Get products
257
     *
258
     * @return array
259
     */
260
    public function getProducts()
261
    {
262
        $result = array_merge(
263
            [
264
                $this->product,
265
                preg_replace('/[^A-Za-z0-9]/', '', $this->product), // in case of special characters
266
            ],
267
            $this->explode($this->product, '-')
268
        );
269
        return $this->filterDuplicates($result);
270
    }
271
}
272