Passed
Push — develop ( 7fbd2c...82694f )
by nguereza
02:49
created

AbstractDetector::str()   B

Complexity

Conditions 9
Paths 8

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 9
c 1
b 0
f 0
nc 8
nop 2
dl 0
loc 16
rs 8.0555
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   http://www.iacademy.cf
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
use function Platine\UserAgent\Util\preg_replace;
57
58
/**
59
 * Class AbstractDetector
60
 * @package Platine\UserAgent\Detector
61
 */
62
abstract class AbstractDetector
63
{
64
65
    /**
66
     * Data type and definitions
67
     */
68
    protected const NAME = 'name';
69
    protected const VERSION = 'version';
70
    protected const ARCHITECTURE = 'architecture';
71
    protected const MODEL = 'model';
72
    protected const VENDOR = 'vendor';
73
    protected const TYPE = 'type';
74
    protected const CONSOLE = 'console';
75
    protected const MOBILE = 'mobile';
76
    protected const TABLET = 'tablet';
77
    protected const SMART_TV = 'smart-tv';
78
    protected const WEARABLE = 'wearable';
79
    protected const UNKNOWN = '?';
80
81
    /**
82
     * The map for entity
83
     * @var array<string, mixed>
84
     */
85
    protected array $maps = [];
86
87
    /**
88
     * The entity regex pattern definition
89
     * @var array<array<string>>|array<array<array<string>>>
90
     */
91
    protected array $regex = [];
92
93
    /**
94
     * The current entity
95
     * @var Browser|Cpu|Os|Device|Engine
96
     */
97
    protected $entity;
98
99
    /**
100
     * Create new instance
101
     */
102
    public function __construct()
103
    {
104
        $this->maps = $this->maps();
105
        $this->regex = $this->regex();
106
    }
107
108
    /**
109
     * Detect the entity for the given user agent
110
     * @param string $userAgent
111
     * @return void
112
     */
113
    public function detect(string $userAgent): void
114
    {
115
        $regex = $this->regex();
116
        $regexLength = count($regex);
117
        $i = 0;
118
        $match = null;
119
        $matches = [];
120
121
        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...
122
            $reg = $regex[$i];
123
            $property = $regex[$i + 1];
124
125
            $j = 0;
126
            $k = 0;
127
128
            $regLength = count($reg);
129
            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...
130
                $pattern = $reg[$j++];
131
                if (is_string($pattern)) {
132
                    preg_match($pattern, $userAgent, $matches);
133
                }
134
135
                if (count($matches) > 0) {
136
                    $lengthProperty = count($property);
137
                    for ($p = 0; $p < $lengthProperty; $p++) {
138
                        $q = $property[$p];
139
                        if (array_key_exists(++$k, $matches)) {
140
                            $match = $matches[$k];
141
                        }
142
143
                        if (is_array($q) && count($q) > 0) {
144
                            if (count($q) === 2) {
145
                                if (Helper::startsWith($q[1], '__')) {
146
                                    $functionName = Helper::replaceFirst('__', '', $q[1]);
147
                                    $result = null;
148
                                    if (method_exists($this, $functionName)) {
149
                                        $result = $this->{$functionName}($match);
150
                                    }
151
                                    $this->fillEntity([$q[0] => $result]);
152
                                } else {
153
                                    $this->fillEntity([$q[0] => $q[1]]);
154
                                }
155
                            } elseif (count($q) === 3) {
156
                                if (Helper::startsWith($q[1], '__')) {
157
                                    $functionName = Helper::replaceFirst('__', '', $q[1]);
158
                                    $args = explode('.', $q[2]);
159
                                    $argument = $this->maps();
160
                                    if (is_array($args)) {
161
                                        foreach ($args as $key) {
162
                                            $argument = $argument[$key];
163
                                        }
164
                                    }
165
                                    $result = null;
166
                                    if (method_exists($this, $functionName)) {
167
                                        $result = $this->{$functionName}($match, $argument);
168
                                    }
169
                                    $this->fillEntity([$q[0] => $result]);
170
                                } else {
171
                                    $replacedMatch = preg_replace($q[1], $q[2], $match);
172
                                    if ($replacedMatch !== null) {
173
                                        $this->fillEntity([$q[0] => $replacedMatch]);
174
                                    }
175
                                }
176
                            } elseif (count($q) === 4) {
177
                                if (Helper::startsWith($q[3], '__')) {
178
                                    $functionName = Helper::replaceFirst('__', '', $q[3]);
179
                                    $result = preg_replace($q[1], $q[2], $match);
180
                                    if (method_exists($this, $functionName)) {
181
                                        $result = $this->{$functionName}($result);
182
                                    }
183
                                    $this->fillEntity([$q[0] => $result]);
184
                                }
185
                            }
186
                        } else {
187
                            if (is_string($q)) {
188
                                $this->fillEntity([$q => $match]);
189
                            }
190
                        }
191
                    }
192
                }
193
            }
194
            $i += 2;
195
        }
196
    }
197
198
    /**
199
     * Return the entity instance
200
     * @return Browser|Cpu|Os|Device|Engine
201
     */
202
    public function entity()
203
    {
204
        return $this->entity;
205
    }
206
207
    /**
208
     * Return the entity map
209
     * @return array<string, mixed>
210
     */
211
    abstract public function maps(): array;
212
213
    /**
214
     * Return the entity regex pattern
215
     * @return array<array<string>>|array<array<array<string>>>
216
     */
217
    abstract public function regex(): array;
218
219
    /**
220
     * Fill the entity data
221
     * @param array<string, string|int> $data
222
     * @return void
223
     */
224
    protected function fillEntity(array $data): void
225
    {
226
        $this->entity->setData($data);
227
    }
228
229
    /**
230
     * Put the string to lower case
231
     * @param string $value
232
     * @return string
233
     */
234
    protected function lowerize(string $value): string
235
    {
236
        if (function_exists('mb_strtolower')) {
237
            return mb_strtolower($value);
238
        }
239
240
        return strtolower($value);
241
    }
242
243
    /**
244
     * Split version by DOT.
245
     * @param string $str
246
     * @return string|string[]|null
247
     */
248
    protected function trim(string $str)
249
    {
250
        return preg_replace(
251
            '/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/',
252
            '',
253
            $str
254
        );
255
    }
256
257
    /**
258
     * Replace given string using map data.
259
     * @param string $str
260
     * @param array<string, mixed> $maps
261
     * @return string|null
262
     */
263
    protected function str(string $str, array $maps): ?string
264
    {
265
        foreach ($maps as $key => $value) {
266
            if (is_array($value) && count($value) > 0) {
267
                $len = count($value);
268
                for ($i = 0; $i < $len; $i++) {
269
                    if (strpos($value[$i], $str) !== false) {
270
                        return $key === self::UNKNOWN ? null : (string) $key;
271
                    }
272
                }
273
            } elseif (strpos($value, $str) !== false) {
274
                return $key === self::UNKNOWN ? null : $key;
275
            }
276
        }
277
278
        return null;
279
    }
280
}
281