Passed
Pull Request — master (#799)
by Vitor
19:42 queued 15:16
created

AFactory   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 88
Duplicated Lines 0 %

Test Coverage

Coverage 95.92%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 19
eloc 48
c 1
b 0
f 0
dl 0
loc 88
ccs 47
cts 49
cp 0.9592
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
B get() 0 34 9
A getFqcnList() 0 17 5
A typeFrom() 0 15 5
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8
9
namespace OCA\TwoFactorGateway\Provider;
10
11
/**
12
 * @template T of object
13
 */
14
abstract class AFactory {
15
	/** @var array<string, T> */
16
	protected array $instances = [];
17
	/** @var array<string> */
18
	protected array $fqcn = [];
19
20
	abstract protected function getPrefix(): string;
21
22
	abstract protected function getSuffix(): string;
23
24
	/** @return class-string<T> */
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
25
	abstract protected function getBaseClass(): string;
26
27
	/**
28
	 * @param class-string<T> $name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
29
	 * @return T
30
	 */
31 7
	public function get(string $name): object {
32 7
		$needle = match(str_contains($name, '\\')) {
33 6
			true => $this->typeFrom($name),
34 2
			false => strtolower($name),
35 7
		};
36 7
		if (isset($this->instances[$needle])) {
37 3
			return $this->instances[$needle];
38
		}
39
40 6
		foreach ($this->getFqcnList() as $fqcn) {
41 6
			$type = $this->typeFrom($fqcn);
42 6
			if ($type !== $needle) {
43 6
				if (str_ends_with($fqcn, '\\Gateway')) {
44 6
					$factoryFqcn = str_replace('\\Gateway', '\\Factory', $fqcn);
45 6
					if ($factoryFqcn !== $fqcn && class_exists($factoryFqcn)) {
46 5
						$factory = \OCP\Server::get($factoryFqcn);
47 5
						foreach ($factory->getFqcnList() as $driverFqcn) {
48 5
							$gateway = $factory->get($driverFqcn);
49 5
							$settings = $gateway->getSettings();
50 5
							$this->instances[$settings->id] = $gateway;
51 5
							if ($settings->id === $name) {
52
								return $gateway;
53
							}
54
						}
55
					}
56
				}
57 6
				continue;
58
			}
59 5
			$instance = \OCP\Server::get($fqcn);
60 5
			$this->instances[$type] = $instance;
61 5
			return $instance;
62
		}
63
64 1
		throw new \InvalidArgumentException("Invalid type <$name>");
65
	}
66
67
	/** @return array<string> */
68 8
	public function getFqcnList(): array {
69 8
		if (!empty($this->fqcn)) {
70 7
			return $this->fqcn;
71
		}
72
73 7
		$loader = require __DIR__ . '/../../vendor/autoload.php';
74 7
		foreach ($loader->getClassMap() as $fqcn => $_) {
75 7
			$type = $this->typeFrom($fqcn);
76 7
			if ($type === null) {
77 7
				continue;
78
			}
79 7
			if (!is_subclass_of($fqcn, $this->getBaseClass(), true)) {
80
				continue;
81
			}
82 7
			$this->fqcn[] = $fqcn;
83
		}
84 7
		return $this->fqcn;
85
	}
86
87 8
	protected function typeFrom(string $fqcn): ?string {
88 8
		$prefix = $this->getPrefix();
89 8
		if (strncmp($fqcn, $prefix, strlen($prefix)) !== 0) {
90 7
			return null;
91
		}
92 8
		$suffix = $this->getSuffix();
93 8
		if (!str_ends_with($fqcn, $suffix)) {
94 6
			return null;
95
		}
96 8
		$type = substr($fqcn, strlen($prefix));
97 8
		$type = substr($type, 0, -strlen('\\' . $suffix));
98 8
		if (strpos($type, '\\') !== false || $type === '') {
99 2
			return null;
100
		}
101 8
		return strtolower($type);
102
	}
103
}
104