Passed
Push — main ( 0b3ef0...fa30e1 )
by Will
02:47
created

agentzero::parse()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.0312

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 13
ccs 7
cts 8
cp 0.875
rs 10
cc 4
nc 4
nop 1
crap 4.0312
1
<?php
2
declare(strict_types = 1);
3
namespace hexydec\agentzero;
4
5
/**
6
 * @phpstan-import-type MatchValue from config
7
 */
8
class agentzero {
9
10
	// ua string
11
	public readonly string $string;
12
13
	// categories
14
	public readonly ?string $type;
15
	public readonly ?string $category;
16
17
	// device
18
	public readonly ?string $vendor;
19
	public readonly ?string $device;
20
	public readonly ?string $model;
21
	public readonly ?string $build;
22
	public readonly ?int $ram;
23
24
	// architecture
25
	public readonly ?string $processor;
26
	public readonly ?string $architecture;
27
	public readonly ?int $bits;
28
	public readonly ?string $cpu;
29
	public readonly ?int $cpuclock;
30
31
	// platform
32
	public readonly ?string $kernel;
33
	public readonly ?string $platform;
34
	public readonly ?string $platformversion;
35
36
	// browser
37
	public readonly ?string $engine;
38
	public readonly ?string $engineversion;
39
	public readonly ?string $browser;
40
	public readonly ?string $browserversion;
41
	public readonly ?string $language;
42
43
	// app
44
	public readonly ?string $app;
45
	public readonly ?string $appname;
46
	public readonly ?string $appversion;
47
	public readonly ?string $url;
48
49
	// network
50
	public readonly ?string $nettype;
51
	public readonly ?string $proxy;
52
53
	// screen
54
	public readonly ?int $width;
55
	public readonly ?int $height;
56
	public readonly ?int $dpi;
57
	public readonly ?float $density;
58
	public readonly ?bool $darkmode;
59
60
	/**
61
	 * Constructs a new AgentZero object, private because it can only be created internally
62
	 * 
63
	 * @param \stdClass $data A stdClass object containing the UA details
64
	 */
65 87
	private function __construct(string $ua, \stdClass $data) {
66 87
		$this->string = $ua;
0 ignored issues
show
Bug introduced by
The property string is declared read-only in hexydec\agentzero\agentzero.
Loading history...
67
68
		// categories
69 87
		$this->type = $data->type ?? null;
0 ignored issues
show
Bug introduced by
The property type is declared read-only in hexydec\agentzero\agentzero.
Loading history...
70 87
		$this->category = $data->category ?? null;
0 ignored issues
show
Bug introduced by
The property category is declared read-only in hexydec\agentzero\agentzero.
Loading history...
71
72
		// device
73 87
		$this->vendor = $data->vendor ?? null;
0 ignored issues
show
Bug introduced by
The property vendor is declared read-only in hexydec\agentzero\agentzero.
Loading history...
74 87
		$this->device = $data->device ?? null;
0 ignored issues
show
Bug introduced by
The property device is declared read-only in hexydec\agentzero\agentzero.
Loading history...
75 87
		$this->model = $data->model ?? null;
0 ignored issues
show
Bug introduced by
The property model is declared read-only in hexydec\agentzero\agentzero.
Loading history...
76 87
		$this->build = $data->build ?? null;
0 ignored issues
show
Bug introduced by
The property build is declared read-only in hexydec\agentzero\agentzero.
Loading history...
77 87
		$this->ram = $data->ram ?? null;
0 ignored issues
show
Bug introduced by
The property ram is declared read-only in hexydec\agentzero\agentzero.
Loading history...
78
79
		// architecture
80 87
		$this->processor = $data->processor ?? null;
0 ignored issues
show
Bug introduced by
The property processor is declared read-only in hexydec\agentzero\agentzero.
Loading history...
81 87
		$this->architecture = $data->architecture ?? null;
0 ignored issues
show
Bug introduced by
The property architecture is declared read-only in hexydec\agentzero\agentzero.
Loading history...
82 87
		$this->bits = $data->bits ?? null;
0 ignored issues
show
Bug introduced by
The property bits is declared read-only in hexydec\agentzero\agentzero.
Loading history...
83 87
		$this->cpu = $data->cpu ?? null;
0 ignored issues
show
Bug introduced by
The property cpu is declared read-only in hexydec\agentzero\agentzero.
Loading history...
84 87
		$this->cpuclock = $data->cpuclock ?? null;
0 ignored issues
show
Bug introduced by
The property cpuclock is declared read-only in hexydec\agentzero\agentzero.
Loading history...
85
86
		// platform
87 87
		$this->kernel = $data->kernel ?? null;
0 ignored issues
show
Bug introduced by
The property kernel is declared read-only in hexydec\agentzero\agentzero.
Loading history...
88 87
		$this->platform = $data->platform ?? null;
0 ignored issues
show
Bug introduced by
The property platform is declared read-only in hexydec\agentzero\agentzero.
Loading history...
89 87
		$this->platformversion = $data->platformversion ?? null;
0 ignored issues
show
Bug introduced by
The property platformversion is declared read-only in hexydec\agentzero\agentzero.
Loading history...
90
91
		// browser
92 87
		$this->engine = $data->engine ?? null;
0 ignored issues
show
Bug introduced by
The property engine is declared read-only in hexydec\agentzero\agentzero.
Loading history...
93 87
		$this->engineversion = $data->engineversion ?? null;
0 ignored issues
show
Bug introduced by
The property engineversion is declared read-only in hexydec\agentzero\agentzero.
Loading history...
94 87
		$this->browser = $data->browser ?? null;
0 ignored issues
show
Bug introduced by
The property browser is declared read-only in hexydec\agentzero\agentzero.
Loading history...
95 87
		$this->browserversion = $data->browserversion ?? null;
0 ignored issues
show
Bug introduced by
The property browserversion is declared read-only in hexydec\agentzero\agentzero.
Loading history...
96 87
		$this->language = $data->language ?? null;
0 ignored issues
show
Bug introduced by
The property language is declared read-only in hexydec\agentzero\agentzero.
Loading history...
97
98
		// app
99 87
		$this->app = $data->app ?? null;
0 ignored issues
show
Bug introduced by
The property app is declared read-only in hexydec\agentzero\agentzero.
Loading history...
100 87
		$this->appname = $data->appname ?? null;
0 ignored issues
show
Bug introduced by
The property appname is declared read-only in hexydec\agentzero\agentzero.
Loading history...
101 87
		$this->appversion = $data->appversion ?? null;
0 ignored issues
show
Bug introduced by
The property appversion is declared read-only in hexydec\agentzero\agentzero.
Loading history...
102 87
		$this->url = $data->url ?? null;
0 ignored issues
show
Bug introduced by
The property url is declared read-only in hexydec\agentzero\agentzero.
Loading history...
103
104
		// network
105 87
		$this->nettype = $data->nettype ?? null;
0 ignored issues
show
Bug introduced by
The property nettype is declared read-only in hexydec\agentzero\agentzero.
Loading history...
106 87
		$this->proxy = $data->proxy ?? null;
0 ignored issues
show
Bug introduced by
The property proxy is declared read-only in hexydec\agentzero\agentzero.
Loading history...
107
108
		// screen
109 87
		$this->width = $data->width ?? null;
0 ignored issues
show
Bug introduced by
The property width is declared read-only in hexydec\agentzero\agentzero.
Loading history...
110 87
		$this->height = $data->height ?? null;
0 ignored issues
show
Bug introduced by
The property height is declared read-only in hexydec\agentzero\agentzero.
Loading history...
111 87
		$this->dpi = $data->dpi ?? null;
0 ignored issues
show
Bug introduced by
The property dpi is declared read-only in hexydec\agentzero\agentzero.
Loading history...
112 87
		$this->density = $data->density ?? null;
0 ignored issues
show
Bug introduced by
The property density is declared read-only in hexydec\agentzero\agentzero.
Loading history...
113 87
		$this->darkmode = $data->darkmode ?? null;
0 ignored issues
show
Bug introduced by
The property darkmode is declared read-only in hexydec\agentzero\agentzero.
Loading history...
114
	}
115
116
	/**
117
	 * Retrieves calculated properties
118
	 * 
119
	 * @param string $key The name of the property to retrieve
120
	 * @return string|int|null The requested property or null if it doesn't exist
121
	 */
122
	public function __get(string $key) : string|int|null {
123
		switch ($key) {
124
			case 'host':
125
				if ($this->url !== null && ($host = \parse_url($this->url, PHP_URL_HOST)) !== false && $host !== null) {
126
					return \str_starts_with($host, 'www.') ? \substr($host, 4) : $host;
127
				}
128
				return null;
129
			case 'browsermajorversion':
130
			case 'enginemajorversion':
131
			case 'platformmajorversion':
132
			case 'appmajorversion':
133
				$item = \str_replace('major', '', $key);
134
				$value = $this->{$item} ?? null;
135
				return $value === null ? null : \intval(\substr($value, 0, \strspn($value, '0123456789')));
136
		}
137
		return $this->{$key} ?? null;
138
	}
139
140
	/**
141
	 * Extracts tokens from a UA string
142
	 * 
143
	 * @param string $ua The User Agent string to be tokenised
144
	 * @param array<string> $single An array of strings that can appear on their own, enables the tokens to be split correctly
145
	 * @param array<string> $ignore An array of tokens that can be ignored in the UA string
146
	 * @return false|array<int,string> An array of tokens, or false if no tokens could be extracted
147
	 */
148 87
	protected static function getTokens(string $ua, array $single, array $ignore) : array|false {
149
150
		// prepare regexp
151 87
		$single = \implode('|', \array_map('preg_quote', $single));
152 87
		$pattern = '/\{[^}]++\}|[^()\[\];,\/  _-](?:(?<!'.$single.') (?!https?:\/\/)|(?<=[a-z])\([^)]+\)|[^()\[\];,\/ ]*)*[^()\[\];,\/  _-](?:\/[^;,()\[\]  ]++)?|[0-9]/i';
153
154
		// split up ua string
155 87
		if (\preg_match_all($pattern, $ua, $match)) {
156
157
			// remove ignore values
158 87
			$tokens = \array_diff($match[0], $ignore);
159
160
			// special case for handling like
161 87
			foreach ($tokens AS $key => $item) {
162 87
				if (\str_starts_with($item, 'like ')) {
163
164
					// chop off words up to a useful token e.g. Platform/Version
165 6
					if (\str_contains($item, '/') && ($pos = \mb_strrpos($item, ' ')) !== false) {
166 5
						$tokens[$key] = \mb_substr($item, $pos + 1);
167
168
					// just remove the token
169
					} else {
170 1
						unset($tokens[$key]);
171
					}
172
				}
173
			}
174
175
			// rekey and return
176 87
			return \array_values($tokens);
177
		}
178
		return false;
179
	}
180
181
	/**
182
	 * Parses a User Agent string
183
	 * 
184
	 * @param string $ua The User Agent string to be parsed
185
	 * @return agentzero|false An agentzero object containing the parsed values of the input UA, or false if it could not be parsed
186
	 */
187 87
	public static function parse(string $ua) : agentzero|false {
188 87
		if (($config = config::get()) === null) {
189
190 87
		} elseif (($tokens = self::getTokens($ua, $config['single'], $config['ignore'])) !== false) {
191
192
			// extract UA info
193 87
			$browser = new \stdClass();
194 87
			foreach ($config['match'] AS $key => $item) {
195 87
				$item->match($browser, $key, $tokens);
196
			}
197 87
			return new agentzero($ua, $browser);
198
		}
199
		return false;
200
	}
201
}