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; |
||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||
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; |
|||
0 ignored issues
–
show
The expression
return $data ?? false could also return false which is incompatible with the documented return type array<string,array<string,string>> . Did you maybe forget to handle an error condition?
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled. ![]() |
|||||
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; |
|||
0 ignored issues
–
show
|
|||||
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 | $data['browserstatus'] = 'nightly'; |
||||
125 | |||||
126 | // canary build |
||||
127 | 91 | } elseif ($latest + 2 === $major) { |
|||
128 | 1 | $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); |
|||
0 ignored issues
–
show
Are you sure the assignment to
$data['browserreleased'] is correct as self::released($versions[$browser], $version) targeting hexydec\agentzero\versions::released() seems to always return null.
This check looks for function or method calls that always return null and whose return value is assigned to a variable. class A
{
function getObject()
{
return null;
}
}
$a = new A();
$object = $a->getObject();
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
143 | |||||
144 | // calculate status |
||||
145 | 90 | if (isset($data['browserreleased'])) { |
|||
146 | 90 | $current = \explode('.', $data['browserlatest'])[0] === \explode('.', $version)[0]; |
|||
0 ignored issues
–
show
It seems like
$data['browserlatest'] can also be of type null ; however, parameter $string of explode() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
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 | 63 | } elseif ($released < \date_create('-2 years')) { |
|||
155 | 59 | $data['browserstatus'] = 'outdated'; |
|||
156 | |||||
157 | // current |
||||
158 | 19 | } elseif ($current && ($released >= \date_create('-1 year') || $data['browserlatest'] === $version)) { |
|||
159 | 1 | $data['browserstatus'] = 'current'; |
|||
160 | |||||
161 | // previous |
||||
162 | } else { |
||||
163 | 18 | $data['browserstatus'] = 'previous'; |
|||
164 | } |
||||
165 | } |
||||
166 | } |
||||
167 | } |
||||
168 | 96 | return $data; |
|||
169 | } |
||||
170 | return []; |
||||
171 | } |
||||
172 | } |