Passed
Push — master ( 4209dc...b3eb0b )
by Julius
16:39 queued 12s
created

ReferenceManager::getProviders()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
dl 0
loc 25
rs 9.7666
c 0
b 0
f 0
eloc 15
nc 3
nop 0
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * @copyright Copyright (c) 2022 Julius Härtl <[email protected]>
6
 *
7
 * @author Julius Härtl <[email protected]>
8
 *
9
 * @license GNU AGPL version 3 or any later version
10
 *
11
 * This program is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License as
13
 * published by the Free Software Foundation, either version 3 of the
14
 * License, or (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace OC\Collaboration\Reference;
26
27
use OC\AppFramework\Bootstrap\Coordinator;
28
use OC\Collaboration\Reference\File\FileReferenceProvider;
29
use OCP\Collaboration\Reference\IReference;
30
use OCP\Collaboration\Reference\IReferenceManager;
31
use OCP\Collaboration\Reference\IReferenceProvider;
32
use OCP\ICache;
33
use OCP\ICacheFactory;
34
use OCP\IURLGenerator;
35
use Psr\Container\ContainerInterface;
36
use Psr\Log\LoggerInterface;
37
use Throwable;
38
39
class ReferenceManager implements IReferenceManager {
40
	public const CACHE_TTL = 3600;
41
42
	/** @var IReferenceProvider[]|null */
43
	private ?array $providers = null;
44
	private ICache $cache;
45
	private Coordinator $coordinator;
46
	private ContainerInterface $container;
47
	private LinkReferenceProvider $linkReferenceProvider;
48
	private LoggerInterface $logger;
49
50
	public function __construct(LinkReferenceProvider $linkReferenceProvider, ICacheFactory $cacheFactory, Coordinator $coordinator, ContainerInterface $container, LoggerInterface $logger) {
51
		$this->linkReferenceProvider = $linkReferenceProvider;
52
		$this->cache = $cacheFactory->createDistributed('reference');
53
		$this->coordinator = $coordinator;
54
		$this->container = $container;
55
		$this->logger = $logger;
56
	}
57
58
	public function extractReferences(string $text): array {
59
		preg_match_all(IURLGenerator::URL_REGEX, $text, $matches);
60
		$references = $matches[0] ?? [];
61
		return array_map(function ($reference) {
62
			return trim($reference);
63
		}, $references);
64
	}
65
66
	public function getReferenceFromCache(string $referenceId): ?IReference {
67
		$matchedProvider = $this->getMatchedProvider($referenceId);
68
69
		if ($matchedProvider === null) {
70
			return null;
71
		}
72
73
		$cacheKey = $this->getFullCacheKey($matchedProvider, $referenceId);
74
		return $this->getReferenceByCacheKey($cacheKey);
75
	}
76
77
	public function getReferenceByCacheKey(string $cacheKey): ?IReference {
78
		$cached = $this->cache->get($cacheKey);
79
		if ($cached) {
80
			return Reference::fromCache($cached);
81
		}
82
83
		return null;
84
	}
85
86
	public function resolveReference(string $referenceId): ?IReference {
87
		$matchedProvider = $this->getMatchedProvider($referenceId);
88
89
		if ($matchedProvider === null) {
90
			return null;
91
		}
92
93
		$cacheKey = $this->getFullCacheKey($matchedProvider, $referenceId);
94
		$cached = $this->cache->get($cacheKey);
95
		if ($cached) {
96
			return Reference::fromCache($cached);
97
		}
98
99
		$reference = $matchedProvider->resolveReference($referenceId);
100
		if ($reference) {
101
			$this->cache->set($cacheKey, Reference::toCache($reference), self::CACHE_TTL);
102
			return $reference;
103
		}
104
105
		return null;
106
	}
107
108
	private function getMatchedProvider(string $referenceId): ?IReferenceProvider {
109
		$matchedProvider = null;
110
		foreach ($this->getProviders() as $provider) {
111
			$matchedProvider = $provider->matchReference($referenceId) ? $provider : null;
112
			if ($matchedProvider !== null) {
113
				break;
114
			}
115
		}
116
117
		if ($matchedProvider === null && $this->linkReferenceProvider->matchReference($referenceId)) {
118
			$matchedProvider = $this->linkReferenceProvider;
119
		}
120
121
		return $matchedProvider;
122
	}
123
124
	private function getFullCacheKey(IReferenceProvider $provider, string $referenceId): string {
125
		$cacheKey = $provider->getCacheKey($referenceId);
126
		return md5($provider->getCachePrefix($referenceId)) . (
127
			$cacheKey !== null ? ('-' . md5($cacheKey)) : ''
128
		);
129
	}
130
131
	public function invalidateCache(string $cachePrefix, ?string $cacheKey = null): void {
132
		if ($cacheKey === null) {
133
			$this->cache->clear(md5($cachePrefix));
134
			return;
135
		}
136
137
		$this->cache->remove(md5($cachePrefix) . '-' . md5($cacheKey));
138
	}
139
140
	/**
141
	 * @return IReferenceProvider[]
142
	 */
143
	public function getProviders(): array {
144
		if ($this->providers === null) {
145
			$context = $this->coordinator->getRegistrationContext();
146
			if ($context === null) {
147
				return [];
148
			}
149
150
			$this->providers = array_filter(array_map(function ($registration): ?IReferenceProvider {
151
				try {
152
					/** @var IReferenceProvider $provider */
153
					$provider = $this->container->get($registration->getService());
154
				} catch (Throwable $e) {
155
					$this->logger->error('Could not load reference provider ' . $registration->getService() . ': ' . $e->getMessage(), [
156
						'exception' => $e,
157
					]);
158
					return null;
159
				}
160
161
				return $provider;
162
			}, $context->getReferenceProviders()));
163
164
			$this->providers[] = $this->container->get(FileReferenceProvider::class);
165
		}
166
167
		return $this->providers;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->providers could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
168
	}
169
}
170