1
|
|
|
<?php |
2
|
|
|
declare(strict_types = 1); |
3
|
|
|
namespace hexydec\agentzero; |
4
|
|
|
|
5
|
|
|
class versions { |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* @var array|false|null An array of browser version numbers |
9
|
|
|
*/ |
10
|
|
|
protected static array|false|null $versions = null; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Loads browser version information from an external source |
14
|
|
|
* |
15
|
|
|
* @param string $source The URL of the source JSON containing the version information |
16
|
|
|
* @param string $cache The absolute file address of the cache file |
17
|
|
|
* @param ?int $life The maximum life of the cache file in seconds |
18
|
|
|
* @return array<string,array<string,string>> An array of browser versioning information, or false if the data source not be retrieved |
19
|
|
|
*/ |
20
|
96 |
|
protected static function load(string $source, string $cache, ?int $life = 604800) : array|false { |
21
|
|
|
|
22
|
|
|
// cache for this session |
23
|
96 |
|
$data = self::$versions; |
24
|
96 |
|
if ($data === null) { |
25
|
|
|
|
26
|
|
|
// fetch from cache |
27
|
1 |
|
if (\file_exists($cache) && \filemtime($cache) > \time() - $life && ($json = \file_get_contents($cache)) !== false) { |
28
|
|
|
|
29
|
|
|
// fetch from server |
30
|
1 |
|
} elseif (($json = \file_get_contents($source)) === false) { |
31
|
|
|
|
32
|
|
|
// get stale cache |
33
|
|
|
if ($cache !== null && ($json = \file_get_contents($cache)) !== false) { |
34
|
|
|
self::$versions = false; |
35
|
|
|
return false; |
|
|
|
|
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
// update cache |
39
|
|
|
} else { |
40
|
|
|
|
41
|
|
|
// create directory and cache file |
42
|
1 |
|
$dir = \dirname($cache); |
43
|
1 |
|
if (\is_dir($dir) || \mkdir($dir, 0755)) { |
44
|
1 |
|
\file_put_contents($cache, $json); |
45
|
|
|
} |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
// decode JSON |
49
|
1 |
|
self::$versions = $data = \json_decode($json, true); |
50
|
|
|
} |
51
|
96 |
|
return $data ?? false; |
|
|
|
|
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Determines the latest version of a browser, optionally capped by the supplied date |
56
|
|
|
* |
57
|
|
|
* @param array<string,string> $versions An array of browser versions, where the key is the version number and the value is the release date (In Ymd format) |
58
|
|
|
*/ |
59
|
91 |
|
protected static function latest(array $versions, ?\DateTime $now = null) : ?string { |
60
|
|
|
|
61
|
|
|
// no date restriction |
62
|
91 |
|
if ($now === null) { |
63
|
91 |
|
return \strval(\array_key_first($versions)); |
64
|
|
|
} else { |
65
|
|
|
$date = \intval($now->format('Ymd')); |
66
|
|
|
foreach ($versions AS $key => $item) { |
67
|
|
|
if ($date < $item) { |
68
|
|
|
return \strval($key); |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
} |
72
|
|
|
return null; |
73
|
|
|
} |
74
|
|
|
|
75
|
90 |
|
protected static function released(array $data, string $version) : ?string { |
76
|
90 |
|
$major = \intval($version); |
77
|
90 |
|
$len = 0; |
78
|
90 |
|
$i = 0; |
79
|
90 |
|
$vlen = \strlen($version); |
80
|
90 |
|
$released = null; |
81
|
90 |
|
foreach ($data AS $ver => $date) { |
82
|
90 |
|
if (\intval($ver) === $major) { |
83
|
90 |
|
$ver = \strval($ver); // cast as string to get letters, string keys cast to int when array keys |
84
|
90 |
|
$match = 0; |
85
|
90 |
|
for ($n = 0; $n < $vlen; $n++) { |
86
|
90 |
|
if ($version[$n] === ($ver[$n] ?? null)) { |
87
|
90 |
|
$match++; |
88
|
|
|
} else { |
89
|
83 |
|
break; |
90
|
|
|
} |
91
|
|
|
} |
92
|
90 |
|
if ($match > $len) { |
93
|
90 |
|
$len = $match; |
94
|
90 |
|
$released = $date; |
95
|
|
|
} |
96
|
|
|
} |
97
|
90 |
|
$i++; |
98
|
|
|
} |
99
|
90 |
|
return $released !== null ? (new \DateTime(\strval($released)))->format('Y-m-d') : null; |
|
|
|
|
100
|
|
|
} |
101
|
|
|
|
102
|
96 |
|
public static function get(string $browser, string $version, array $config) : array { |
103
|
96 |
|
$source = $config['versionssource']; |
104
|
96 |
|
$cache = $config['versionscache']; |
105
|
96 |
|
$life = $config['versionscachelife']; |
106
|
96 |
|
if ($cache !== null && ($versions = self::load($source, $cache, $life)) !== false) { |
107
|
96 |
|
$data = []; |
108
|
96 |
|
if (isset($versions[$browser])) { |
109
|
|
|
|
110
|
|
|
// get latest version of the browser |
111
|
91 |
|
$data['browserlatest'] = self::latest($versions[$browser], $config['currentdate']); |
112
|
|
|
|
113
|
|
|
// check if version is greater than latest version |
114
|
91 |
|
$major = \intval($version); |
115
|
91 |
|
$latest = \intval($data['browserlatest']); |
116
|
91 |
|
$first = \intval(\array_key_last($versions[$browser])); |
117
|
|
|
|
118
|
|
|
// version is way out of bounds (This happens sometimes, for example if the safari engine version is reported instead of the browser version) |
119
|
91 |
|
if ($latest + 3 < $major) { |
120
|
24 |
|
return []; |
121
|
|
|
|
122
|
|
|
// nightly build? |
123
|
91 |
|
} elseif ($latest + 3 === $major) { |
124
|
1 |
|
$data['browserstatus'] = 'nightly'; |
125
|
|
|
|
126
|
|
|
// canary build |
127
|
91 |
|
} elseif ($latest + 2 === $major) { |
128
|
|
|
$data['browserstatus'] = 'canary'; |
129
|
|
|
|
130
|
|
|
// beta release |
131
|
91 |
|
} elseif ($latest + 1 === $major) { |
132
|
|
|
$data['browserstatus'] = 'beta'; |
133
|
|
|
|
134
|
|
|
// so old we don't have data for it |
135
|
91 |
|
} elseif ($major < $first) { |
136
|
22 |
|
$data['browserstatus'] = 'legacy'; |
137
|
|
|
|
138
|
|
|
// find closes match for version |
139
|
|
|
} else { |
140
|
|
|
|
141
|
|
|
// get current version |
142
|
90 |
|
$data['browserreleased'] = self::released($versions[$browser], $version); |
|
|
|
|
143
|
|
|
|
144
|
|
|
// calculate status |
145
|
90 |
|
if (isset($data['browserreleased'])) { |
146
|
90 |
|
$current = \explode('.', $data['browserlatest'])[0] === \explode('.', $version)[0]; |
|
|
|
|
147
|
90 |
|
$released = new \DateTime($data['browserreleased']); |
148
|
|
|
|
149
|
|
|
// legacy |
150
|
90 |
|
if ($released < \date_create('-5 years')) { |
151
|
75 |
|
$data['browserstatus'] = 'legacy'; |
152
|
|
|
|
153
|
|
|
// outdated |
154
|
64 |
|
} elseif ($released < \date_create('-2 years')) { |
155
|
56 |
|
$data['browserstatus'] = 'outdated'; |
156
|
|
|
|
157
|
|
|
// current |
158
|
25 |
|
} elseif ($current && ($released >= \date_create('-1 year') || $data['browserlatest'] === $version)) { |
159
|
1 |
|
$data['browserstatus'] = 'current'; |
160
|
|
|
|
161
|
|
|
// previous |
162
|
|
|
} else { |
163
|
24 |
|
$data['browserstatus'] = 'previous'; |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
} |
168
|
96 |
|
return $data; |
169
|
|
|
} |
170
|
|
|
return []; |
171
|
|
|
} |
172
|
|
|
} |