Completed
Push — master ( e4992c...6d0a35 )
by
unknown
10:42
created

Manager   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 267
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 267
rs 9.0399
c 0
b 0
f 0
wmc 42
lcom 1
cbo 7

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A registerApp() 0 4 1
A registerNotifier() 0 6 1
A registerBuiltApp() 0 3 1
A registerBuiltNotifier() 0 10 3
A getApps() 0 21 4
A getNotifiers() 0 23 5
B listNotifiers() 0 26 9
A createNotification() 0 3 1
A hasNotifiers() 0 3 1
A notify() 0 14 4
B prepare() 0 21 7
A markProcessed() 0 7 2
A getCount() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like Manager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Manager, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Joas Schilling <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 *
6
 * @copyright Copyright (c) 2018, ownCloud GmbH
7
 * @license AGPL-3.0
8
 *
9
 * This code is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License, version 3,
11
 * as published by the Free Software Foundation.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License, version 3,
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
20
 *
21
 */
22
23
namespace OC\Notification;
24
25
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
26
use OCP\Notification\IApp;
27
use OCP\Notification\IManager;
28
use OCP\Notification\INotification;
29
use OCP\Notification\INotifier;
30
use OCP\Notification\Exceptions\NotifierIdInUseException;
31
use OCP\Notification\Events\RegisterConsumerEvent;
32
use OCP\Notification\Events\RegisterNotifierEvent;
33
use OC\Notification\Events\RegisterConsumerEventImpl;
34
use OC\Notification\Events\RegisterNotifierEventImpl;
35
36
class Manager implements IManager {
37
	/** @var EventDispatcherInterface */
38
	protected $dispatcher;
39
40
	/** @var IApp[] */
41
	protected $apps;
42
43
	/** @var INotifier */
44
	protected $notifiers;
45
46
	/** @var array[] */
47
	protected $notifiersInfo;
48
49
	/** @var \Closure[] */
50
	protected $appsClosures;
51
52
	/** @var \Closure[] */
53
	protected $notifiersClosures;
54
55
	/** @var \Closure[] */
56
	protected $notifiersInfoClosures;
57
58
	/** @var IApp[] */
59
	protected $builtAppsHolder;
60
61
	/** @var INotifier[] */
62
	protected $builtNotifiersHolder;
63
64
	public function __construct(EventDispatcherInterface $dispatcher) {
65
		$this->dispatcher = $dispatcher;
66
67
		$this->apps = [];
68
		$this->notifiers = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type object<OCP\Notification\INotifier> of property $notifiers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
69
		$this->notifiersInfo = [];
70
		$this->appsClosures = [];
71
		$this->notifiersClosures = [];
72
		$this->notifiersInfoClosures = [];
73
74
		$this->builtAppsHolder = [];
75
	}
76
77
	/**
78
	 * @param \Closure $service The service must implement IApp, otherwise a
79
	 *                          \InvalidArgumentException is thrown later
80
	 * @return null
81
	 * @since 8.2.0
82
	 */
83
	public function registerApp(\Closure $service) {
84
		$this->appsClosures[] = $service;
85
		$this->apps = [];
86
	}
87
88
	/**
89
	 * @param \Closure $service The service must implement INotifier, otherwise a
90
	 *                          \InvalidArgumentException is thrown later
91
	 * @param \Closure $info    An array with the keys 'id' and 'name' containing
92
	 *                          the app id and the app name
93
	 * @return null
94
	 * @since 8.2.0 - Parameter $info was added in 9.0.0
95
	 */
96
	public function registerNotifier(\Closure $service, \Closure $info) {
97
		$this->notifiersClosures[] = $service;
98
		$this->notifiersInfoClosures[] = $info;
99
		$this->notifiers = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type object<OCP\Notification\INotifier> of property $notifiers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
100
		$this->notifiersInfo = [];
101
	}
102
103
	/**
104
	 * INTERNAL USE ONLY!! This method isn't part of the IManager interface
105
	 * @internal This should only be used by the RegisterConsumerEventImpl (the real implementation).
106
	 * Do NOT use this method outside as it might not work as expected.
107
	 */
108
	public function registerBuiltApp(IApp $app) {
109
		$this->builtAppsHolder[] = $app;
110
	}
111
112
	/**
113
	 * INTERNAL USE ONLY!! This method isn't part of the IManager interface
114
	 * @internal This should only be used by the RegisterNotifierEventImpl (the real implementation).
115
	 * Do NOT use this method outside as it might not work as expected.
116
	 */
117
	public function registerBuiltNotifier(INotifier $notifier, $id, $name) {
118
		if (!isset($this->builtNotifiersHolder[$id]) && !isset($this->notifiersInfo[$id])) {
119
			// we have to check also in the notifiersInfo
120
			$this->builtNotifiersHolder[$id] = [];
121
			$this->builtNotifiersHolder[$id]['notifier'] = $notifier;
122
			$this->builtNotifiersHolder[$id]['name'] = $name;
123
		} else {
124
			throw new NotifierIdInUseException("The given notifier ID $id is already in use");
125
		}
126
	}
127
128
	/**
129
	 * @return IApp[]
130
	 */
131
	protected function getApps() {
132
		if (!empty($this->apps)) {
133
			return $this->apps;
134
		}
135
136
		$this->apps = [];
137
		foreach ($this->appsClosures as $closure) {
138
			$app = $closure();
139
			if (!($app instanceof IApp)) {
140
				throw new \InvalidArgumentException('The given notification app does not implement the IApp interface');
141
			}
142
			$this->apps[] = $app;
143
		}
144
145
		$this->builtAppsHolder = [];
146
		$registerAppEvent = new RegisterConsumerEventImpl($this);
147
		$this->dispatcher->dispatch(RegisterConsumerEvent::NAME, $registerAppEvent);
148
		$this->apps = \array_merge($this->apps, $this->builtAppsHolder);
0 ignored issues
show
Documentation Bug introduced by
It seems like \array_merge($this->apps, $this->builtAppsHolder) of type array is incompatible with the declared type array<integer,object<OCP\Notification\IApp>> of property $apps.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
149
150
		return $this->apps;
151
	}
152
153
	/**
154
	 * @return INotifier[]
155
	 */
156
	protected function getNotifiers() {
157
		if (!empty($this->notifiers)) {
158
			return $this->notifiers;
159
		}
160
161
		$this->notifiers = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type object<OCP\Notification\INotifier> of property $notifiers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
162
		foreach ($this->notifiersClosures as $closure) {
163
			$notifier = $closure();
164
			if (!($notifier instanceof INotifier)) {
165
				throw new \InvalidArgumentException('The given notifier does not implement the INotifier interface');
166
			}
167
			$this->notifiers[] = $notifier;
168
		}
169
170
		$this->builtNotifiersHolder = [];
171
		$registerNotifierEvent = new RegisterNotifierEventImpl($this);
172
		$this->dispatcher->dispatch(RegisterNotifierEvent::NAME, $registerNotifierEvent);
173
		foreach ($this->builtNotifiersHolder as $notifierData) {
174
			$this->notifiers[] = $notifierData['notifier'];
175
		}
176
177
		return $this->notifiers;
178
	}
179
180
	/**
181
	 * @return array[]
182
	 */
183
	public function listNotifiers() {
184
		if (!empty($this->notifiersInfo)) {
185
			return $this->notifiersInfo;
186
		}
187
188
		$this->notifiersInfo = [];
189
		foreach ($this->notifiersInfoClosures as $closure) {
190
			$notifier = $closure();
191
			if (!\is_array($notifier) || \sizeof($notifier) !== 2 || !isset($notifier['id']) || !isset($notifier['name'])) {
192
				throw new \InvalidArgumentException('The given notifier information is invalid');
193
			}
194
			if (isset($this->notifiersInfo[$notifier['id']])) {
195
				throw new \InvalidArgumentException('The given notifier ID ' . $notifier['id'] . ' is already in use');
196
			}
197
			$this->notifiersInfo[$notifier['id']] = $notifier['name'];
198
		}
199
200
		$this->builtNotifiersHolder = [];
201
		$registerNotifierEvent = new RegisterNotifierEventImpl($this);
202
		$this->dispatcher->dispatch(RegisterNotifierEvent::NAME, $registerNotifierEvent);
203
		foreach ($this->builtNotifiersHolder as $id => $notifierData) {
204
			$this->notifiersInfo[$id] = $notifierData['name'];
205
		}
206
207
		return $this->notifiersInfo;
208
	}
209
210
	/**
211
	 * @return INotification
212
	 * @since 8.2.0
213
	 */
214
	public function createNotification() {
215
		return new Notification();
216
	}
217
218
	/**
219
	 * @return bool
220
	 * @since 8.2.0
221
	 */
222
	public function hasNotifiers() {
223
		return !empty($this->notifiersClosures);
224
	}
225
226
	/**
227
	 * @param INotification $notification
228
	 * @return null
229
	 * @throws \InvalidArgumentException When the notification is not valid
230
	 * @since 8.2.0
231
	 */
232
	public function notify(INotification $notification) {
233
		if (!$notification->isValid()) {
234
			throw new \InvalidArgumentException('The given notification is invalid');
235
		}
236
237
		$apps = $this->getApps();
238
239
		foreach ($apps as $app) {
240
			try {
241
				$app->notify($notification);
242
			} catch (\InvalidArgumentException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
243
			}
244
		}
245
	}
246
247
	/**
248
	 * @param INotification $notification
249
	 * @param string $languageCode The code of the language that should be used to prepare the notification
250
	 * @return INotification
251
	 * @throws \InvalidArgumentException When the notification was not prepared by a notifier
252
	 * @since 8.2.0
253
	 */
254
	public function prepare(INotification $notification, $languageCode) {
255
		$notifiers = $this->getNotifiers();
256
257
		foreach ($notifiers as $notifier) {
0 ignored issues
show
Bug introduced by
The expression $notifiers of type object<OCP\Notification\INotifier>|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
258
			try {
259
				$notification = $notifier->prepare($notification, $languageCode);
260
			} catch (\InvalidArgumentException $e) {
261
				continue;
262
			}
263
264
			if (!($notification instanceof INotification) || !$notification->isValidParsed()) {
265
				throw new \InvalidArgumentException('The given notification has not been handled');
266
			}
267
		}
268
269
		if (!($notification instanceof INotification) || !$notification->isValidParsed()) {
270
			throw new \InvalidArgumentException('The given notification has not been handled');
271
		}
272
273
		return $notification;
274
	}
275
276
	/**
277
	 * @param INotification $notification
278
	 * @return null
279
	 */
280
	public function markProcessed(INotification $notification) {
281
		$apps = $this->getApps();
282
283
		foreach ($apps as $app) {
284
			$app->markProcessed($notification);
285
		}
286
	}
287
288
	/**
289
	 * @param INotification $notification
290
	 * @return int
291
	 */
292
	public function getCount(INotification $notification) {
293
		$apps = $this->getApps();
294
295
		$count = 0;
296
		foreach ($apps as $app) {
297
			$count += $app->getCount($notification);
298
		}
299
300
		return $count;
301
	}
302
}
303