Passed
Push — main ( 5a01f2...ad28d2 )
by Will
03:19
created

versions   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 149
Duplicated Lines 0 %

Test Coverage

Coverage 83.33%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 74
c 1
b 0
f 0
dl 0
loc 149
ccs 60
cts 72
cp 0.8333
rs 9.52
wmc 36

4 Methods

Rating   Name   Duplication   Size   Complexity  
B released() 0 25 7
B load() 0 34 11
C get() 0 66 14
A latest() 0 14 4
1
<?php
2
declare(strict_types = 1);
3
namespace hexydec\agentzero;
4
5
class versions {
6
7
	protected static array|false|null $versions = null;
8
9 96
	protected static function load(string $source, ?string $cache = null, ?int $life = 604800) : array|false {
10
11
		// cache for this session
12 96
		$data = self::$versions;
13 96
		if ($data === null) {
14
15
			// fetch from cache
16 1
			if (\file_exists($cache) && \filemtime($cache) > \time() - $life && ($json = \file_get_contents($cache)) !== false) {
0 ignored issues
show
Bug introduced by
It seems like $cache can also be of type null; however, parameter $filename of file_get_contents() 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 ignore-type  annotation

16
			if (\file_exists($cache) && \filemtime($cache) > \time() - $life && ($json = \file_get_contents(/** @scrutinizer ignore-type */ $cache)) !== false) {
Loading history...
Bug introduced by
It seems like $cache can also be of type null; however, parameter $filename of file_exists() 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 ignore-type  annotation

16
			if (\file_exists(/** @scrutinizer ignore-type */ $cache) && \filemtime($cache) > \time() - $life && ($json = \file_get_contents($cache)) !== false) {
Loading history...
Bug introduced by
It seems like $cache can also be of type null; however, parameter $filename of filemtime() 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 ignore-type  annotation

16
			if (\file_exists($cache) && \filemtime(/** @scrutinizer ignore-type */ $cache) > \time() - $life && ($json = \file_get_contents($cache)) !== false) {
Loading history...
17
18
			// fetch from server
19 1
			} elseif (($json = \file_get_contents($source)) === false) {
20
21
				// get stale cache
22
				if ($cache !== null && ($json = \file_get_contents($cache)) !== false) {
23
					self::$versions = false;
24
					return false;
25
				}
26
27
			// update cache
28 1
			} elseif ($cache !== null) {
29
30
				// create directory
31 1
				$dir = \dirname($cache);
32 1
				if (!\is_dir($dir) && \mkdir($dir, 0755)) {
33
34
					// cache file
35 1
					\file_put_contents($cache, $json);
36
				}
37
			}
38
39
			// decode JSON
40 1
			self::$versions = $data = \json_decode($json, true);
41
		}
42 96
		return $data ?? false;
43
	}
44
45 91
	protected static function latest(array $versions, ?\DateTime $now = null) : ?string {
46
47
		// no date restriction
48 91
		if ($now === null) {
49 91
			return \strval(\array_key_first($versions));
50
		} else {
51
			$date = \intval($now->format('Ymd'));
52
			foreach ($versions AS $key => $item) {
53
				if ($date < $item) {
54
					return \strval($key);
55
				}
56
			}
57
		}
58
		return null;
59
	}
60
61 90
	protected static function released(array $data, string $version) : ?string {
62 90
		$major = \intval($version);
63 90
		$len = 0;
64 90
		$i = 0;
65 90
		$vlen = \strlen($version);
66 90
		$released = null;
67 90
		foreach ($data AS $ver => $date) {
68 90
			if (\intval($ver) === $major) {
69 90
				$ver = \strval($ver); // cast as string to get letters, string keys cast to int when array keys
70 90
				$match = 0;
71 90
				for ($n = 0; $n < $vlen; $n++) {
72 90
					if ($version[$n] === ($ver[$n] ?? null)) {
73 90
						$match++;
74
					} else {
75 83
						break;
76
					}
77
				}
78 90
				if ($match > $len) {
79 90
					$len = $match;
80 90
					$released = $date;
81
				}
82
			}
83 90
			$i++;
84
		}
85 90
		return $released !== null ? (new \DateTime(\strval($released)))->format('Y-m-d') : null;
0 ignored issues
show
introduced by
The condition $released !== null is always false.
Loading history...
86
	}
87
88 96
	public static function get(string $browser, string $version, array $config) : array {
89 96
		if (($versions = self::load($config['source'], $config['cache'])) !== false) {
90 96
			$data = [];
91 96
			if (isset($versions[$browser])) {
92
93
				// get latest version of the browser
94 91
				$data['browserlatest'] = self::latest($versions[$browser]);
95
				
96
				// check if version is greater than latest version
97 91
				$major = \intval($version);
98 91
				$latest = \intval($data['browserlatest']);
99 91
				$first = \intval(\array_key_last($versions[$browser]));
100
101
				// version is way out of bounds (This happens sometimes, for example if the safari engine version is reported instead of the browser version)
102 91
				if ($latest + 3 < $major) {
103 25
					return [];
104
105
				// nightly build?
106 91
				} elseif ($latest + 3 === $major) {
107
					$data['browserstatus'] = 'nightly';
108
109
				// canary build
110 91
				} elseif ($latest + 2 === $major) {
111
					$data['browserstatus'] = 'canary';
112
113
				// beta release
114 91
				} elseif ($latest + 1 === $major) {
115
					$data['browserstatus'] = 'beta';
116
117
				// so old we don't have data for it
118 91
				} elseif ($major < $first) {
119 22
					$data['browserstatus'] = 'legacy';
120
121
				// find closes match for version
122
				} else {
123
124
					// get current version
125 90
					$data['browserreleased'] = self::released($versions[$browser], $version);
0 ignored issues
show
Bug introduced by
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 getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
126
127
					// calculate status
128 90
					if (isset($data['browserreleased'])) {
129 90
						$current = \explode('.', $data['browserlatest'])[0] === \explode('.', $version)[0];
130 90
						$released = new \DateTime($data['browserreleased']);
131
132
						// legacy
133 90
						if ($released < \date_create('-5 years')) {
134 71
							$data['browserstatus'] = 'legacy';
135
136
						// outdated
137 65
						} elseif ($released < \date_create('-2 years')) {
138 55
							$data['browserstatus'] = 'outdated';
139
140
						// current
141 27
						} elseif ($current && ($released >= \date_create('-1 year') || $data['browserlatest'] === $version)) {
142 1
							$data['browserstatus'] = 'current';
143
144
						// previous
145
						} else {
146 26
							$data['browserstatus'] = 'previous';
147
						}
148
					}
149
				}
150
			}
151 96
			return $data;
152
		}
153
		return [];
154
	}
155
}