AbstractDetector::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * Platine User Agent
5
 *
6
 * Platine User Agent is a lightweight library for detecting
7
 * user browser, device, OS, CPU
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine User Agent
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file AbstractDetector.php
34
 *
35
 *  The base class for detector
36
 *
37
 *  @package    Platine\UserAgent\Detector
38
 *  @author Platine Developers Team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   https://www.platine-php.com
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\UserAgent\Detector;
49
50
use Platine\UserAgent\Entity\Browser;
51
use Platine\UserAgent\Entity\Cpu;
52
use Platine\UserAgent\Entity\Device;
53
use Platine\UserAgent\Entity\Engine;
54
use Platine\UserAgent\Entity\Os;
55
use Platine\UserAgent\Util\Helper;
56
57
/**
58
 * @class AbstractDetector
59
 * @package Platine\UserAgent\Detector
60
 */
61
abstract class AbstractDetector
62
{
63
    /**
64
     * Data type and definitions
65
     */
66
    protected const NAME = 'name';
67
    protected const VERSION = 'version';
68
    protected const ARCHITECTURE = 'architecture';
69
    protected const MODEL = 'model';
70
    protected const VENDOR = 'vendor';
71
    protected const TYPE = 'type';
72
    protected const CONSOLE = 'console';
73
    protected const MOBILE = 'mobile';
74
    protected const TABLET = 'tablet';
75
    protected const SMART_TV = 'smart-tv';
76
    protected const WEARABLE = 'wearable';
77
    protected const UNKNOWN = '?';
78
79
    /**
80
     * The map for entity
81
     * @var array<string, mixed>
82
     */
83
    protected array $maps = [];
84
85
    /**
86
     * The entity regex pattern definition
87
     * @var array<array<string>>|array<array<array<string>>>
88
     */
89
    protected array $regex = [];
90
91
    /**
92
     * The current entity
93
     * @var Browser|Cpu|Os|Device|Engine
94
     */
95
    protected Browser|Cpu|Os|Device|Engine $entity;
96
97
    /**
98
     * Create new instance
99
     */
100
    public function __construct()
101
    {
102
        $this->maps = $this->maps();
103
        $this->regex = $this->regex();
104
    }
105
106
    /**
107
     * Detect the entity for the given user agent
108
     * @param string $userAgent
109
     * @return void
110
     */
111
    public function detect(string $userAgent): void
112
    {
113
        $regex = $this->regex();
114
        $regexLength = count($regex);
115
        $i = 0;
116
        $match = '';
117
        $matches = [];
118
119
        while ($i < $regexLength && ! $matches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
120
            $reg = $regex[$i];
121
            $property = $regex[$i + 1];
122
123
            $j = 0;
124
            $k = 0;
125
126
            $regLength = count($reg);
127
            while ($j < $regLength && ! $matches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
128
                $pattern = $reg[$j++];
129
                if (is_string($pattern)) {
130
                    preg_match($pattern, $userAgent, $matches);
131
                }
132
133
                if (count($matches) > 0) {
134
                    $lengthProperty = count($property);
135
                    for ($p = 0; $p < $lengthProperty; $p++) {
136
                        $q = $property[$p];
137
                        if (array_key_exists(++$k, $matches)) {
138
                            $match = $matches[$k];
139
                        }
140
141
                        if (is_array($q) && count($q) > 0) {
142
                            if (count($q) === 2) {
143
                                if (Helper::startsWith($q[1], '__')) {
144
                                    $functionName = Helper::replaceFirst('__', '', $q[1]);
145
                                    $result = null;
146
                                    if (method_exists($this, $functionName)) {
147
                                        $result = $this->{$functionName}($match);
148
                                    }
149
                                    $this->fillEntity([$q[0] => $result]);
150
                                } else {
151
                                    $this->fillEntity([$q[0] => $q[1]]);
152
                                }
153
                            } elseif (count($q) === 3) {
154
                                if (Helper::startsWith($q[1], '__')) {
155
                                    $functionName = Helper::replaceFirst('__', '', $q[1]);
156
                                    $args = explode('.', $q[2]);
157
                                    $argument = $this->maps();
158
                                    foreach ($args as $key) {
159
                                        $argument = $argument[$key];
160
                                    }
161
162
                                    $result = null;
163
                                    if (method_exists($this, $functionName)) {
164
                                        $result = $this->{$functionName}($match, $argument);
165
                                    }
166
                                    $this->fillEntity([$q[0] => $result]);
167
                                } else {
168
                                    $replacedMatch = preg_replace($q[1], $q[2], (string)$match);
169
                                    if ($replacedMatch !== null) {
170
                                        $this->fillEntity([$q[0] => $replacedMatch]);
171
                                    }
172
                                }
173
                            } elseif (count($q) === 4) {
174
                                if (Helper::startsWith($q[3], '__')) {
175
                                    $functionName = Helper::replaceFirst('__', '', $q[3]);
176
                                    $result = preg_replace($q[1], $q[2], (string)$match);
177
                                    if (method_exists($this, $functionName)) {
178
                                        $result = $this->{$functionName}($result);
179
                                    }
180
                                    $this->fillEntity([$q[0] => $result]);
181
                                }
182
                            }
183
                        } else {
184
                            if (is_string($q)) {
185
                                $this->fillEntity([$q => $match]);
186
                            }
187
                        }
188
                    }
189
                }
190
            }
191
            $i += 2;
192
        }
193
    }
194
195
    /**
196
     * Return the entity instance
197
     * @return Browser|Cpu|Os|Device|Engine
198
     */
199
    public function entity(): Browser|Cpu|Os|Device|Engine
200
    {
201
        return $this->entity;
202
    }
203
204
    /**
205
     * Return the entity map
206
     * @return array<string, mixed>
207
     */
208
    abstract public function maps(): array;
209
210
    /**
211
     * Return the entity regex pattern
212
     * @return array<array<string>>|array<array<array<string>>>
213
     */
214
    abstract public function regex(): array;
215
216
    /**
217
     * Fill the entity data
218
     * @param array<string, string|int> $data
219
     * @return void
220
     */
221
    protected function fillEntity(array $data): void
222
    {
223
        $this->entity->setData($data);
224
    }
225
226
    /**
227
     * Put the string to lower case
228
     * @param string $value
229
     * @return string
230
     */
231
    protected function lowerize(string $value): string
232
    {
233
        if (function_exists('mb_strtolower')) {
234
            return mb_strtolower($value);
235
        }
236
237
        return strtolower($value);
238
    }
239
240
    /**
241
     * Split version by DOT.
242
     * @param string $str
243
     * @return string|string[]|null
244
     */
245
    protected function trim(string $str): string|array|null
246
    {
247
        return preg_replace(
248
            '/^[\s\xA0]+|[\s\xA0]+$/',
249
            '',
250
            $str
251
        );
252
    }
253
254
    /**
255
     * Replace given string using map data.
256
     * @param string $str
257
     * @param array<string, mixed> $maps
258
     * @return string|null
259
     */
260
    protected function str(string $str, array $maps): ?string
261
    {
262
        foreach ($maps as $key => $value) {
263
            if (is_array($value) && count($value) > 0) {
264
                $len = count($value);
265
                for ($i = 0; $i < $len; $i++) {
266
                    if (strpos($value[$i], $str) !== false) {
267
                        return $key === self::UNKNOWN ? null : (string) $key;
268
                    }
269
                }
270
            } elseif (strpos($value, $str) !== false) {
271
                return $key === self::UNKNOWN ? null : $key;
272
            }
273
        }
274
275
        return null;
276
    }
277
}
278