Passed
Push — main ( 683f0d...4c4127 )
by Will
04:44 queued 53s
created

versions::get()   C

Complexity

Conditions 16
Paths 12

Size

Total Lines 68
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 16.1922

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 16
eloc 34
c 1
b 0
f 0
nc 12
nop 3
dl 0
loc 68
ccs 30
cts 33
cp 0.9091
crap 16.1922
rs 5.5666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
The expression return false returns the type false which is incompatible with the documented return type array<string,array<string,string>>.
Loading history...
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
Bug Best Practice introduced by
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.

Loading history...
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
introduced by
The condition $released !== null is always false.
Loading history...
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
109
			// get latest version of the browser
110 96
			if (isset($versions[$browser]) && ($data['browserlatest'] = self::latest($versions[$browser], $config['currentdate'])) !== null) {
111
				
112
				// check if version is greater than latest version
113 91
				$major = \intval($version);
114 91
				$latest = \intval($data['browserlatest']);
115 91
				$first = \intval(\array_key_last($versions[$browser]));
116
117
				// version is way out of bounds (This happens sometimes, for example if the safari engine version is reported instead of the browser version)
118 91
				if ($latest + 3 < $major) {
119 27
					return [];
120
121
				// nightly build?
122 91
				} elseif ($latest + 3 === $major) {
123 2
					$data['browserstatus'] = 'nightly';
124
125
				// canary build
126 91
				} elseif ($latest + 2 === $major) {
127
					$data['browserstatus'] = 'canary';
128
129
				// beta release
130 91
				} elseif ($latest + 1 === $major) {
131
					$data['browserstatus'] = 'beta';
132
133
				// so old we don't have data for it
134 91
				} elseif ($major < $first) {
135 22
					$data['browserstatus'] = 'legacy';
136
137
				// find closes match for version
138
				} else {
139
140
					// get current version
141 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...
142
143
					// calculate status
144 90
					if (isset($data['browserreleased'], $data['browserlatest'])) {
145 90
						$current = \explode('.', $data['browserlatest'])[0] === \explode('.', $version)[0];
146 90
						$released = new \DateTime($data['browserreleased']);
147
148
						// legacy
149 90
						if ($released < \date_create('-5 years')) {
150 74
							$data['browserstatus'] = 'legacy';
151
152
						// outdated
153 63
						} elseif ($released < \date_create('-2 years')) {
154 59
							$data['browserstatus'] = 'outdated';
155
156
						// current
157 13
						} elseif ($current && ($released >= \date_create('-1 year') || $data['browserlatest'] === $version)) {
158 1
							$data['browserstatus'] = 'current';
159
160
						// previous
161
						} else {
162 12
							$data['browserstatus'] = 'previous';
163
						}
164
					}
165
				}
166
			}
167 96
			return $data;
168
		}
169
		return [];
170
	}
171
}