Complex classes like FederatedEventService 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 FederatedEventService, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
86 | class FederatedEventService extends NC22Signature { |
||
87 | |||
88 | |||
89 | use TNC22Request; |
||
90 | use TStringTools; |
||
91 | |||
92 | |||
93 | /** @var EventWrapperRequest */ |
||
94 | private $eventWrapperRequest; |
||
95 | |||
96 | /** @var RemoteRequest */ |
||
97 | private $remoteRequest; |
||
98 | |||
99 | /** @var ShareLockRequest */ |
||
100 | private $shareLockRequest; |
||
101 | |||
102 | /** @var MemberRequest */ |
||
103 | private $memberRequest; |
||
104 | |||
105 | /** @var RemoteUpstreamService */ |
||
106 | private $remoteUpstreamService; |
||
107 | |||
108 | /** @var InterfaceService */ |
||
109 | private $interfaceService; |
||
110 | |||
111 | /** @var ConfigService */ |
||
112 | private $configService; |
||
113 | |||
114 | |||
115 | /** |
||
116 | * FederatedEventService constructor. |
||
117 | * |
||
118 | * @param EventWrapperRequest $eventWrapperRequest |
||
119 | * @param RemoteRequest $remoteRequest |
||
120 | * @param MemberRequest $memberRequest |
||
121 | * @param ShareLockRequest $shareLockRequest |
||
122 | * @param RemoteUpstreamService $remoteUpstreamService |
||
123 | * @param InterfaceService $interfaceService |
||
124 | * @param ConfigService $configService |
||
125 | */ |
||
126 | public function __construct( |
||
127 | EventWrapperRequest $eventWrapperRequest, |
||
128 | RemoteRequest $remoteRequest, |
||
129 | MemberRequest $memberRequest, |
||
130 | ShareLockRequest $shareLockRequest, |
||
131 | RemoteUpstreamService $remoteUpstreamService, |
||
132 | InterfaceService $interfaceService, |
||
133 | ConfigService $configService |
||
134 | ) { |
||
135 | $this->eventWrapperRequest = $eventWrapperRequest; |
||
136 | $this->remoteRequest = $remoteRequest; |
||
137 | $this->shareLockRequest = $shareLockRequest; |
||
138 | $this->memberRequest = $memberRequest; |
||
139 | $this->remoteUpstreamService = $remoteUpstreamService; |
||
140 | $this->interfaceService = $interfaceService; |
||
141 | $this->configService = $configService; |
||
142 | } |
||
143 | |||
144 | |||
145 | /** |
||
146 | * Called when creating a new Event. |
||
147 | * This method will manage the event locally and upstream the payload if needed. |
||
148 | * |
||
149 | * @param FederatedEvent $event |
||
150 | * |
||
151 | * @return array |
||
152 | * @throws FederatedEventException |
||
153 | * @throws FederatedItemException |
||
154 | * @throws InitiatorNotConfirmedException |
||
155 | * @throws OwnerNotFoundException |
||
156 | * @throws RemoteNotFoundException |
||
157 | * @throws RemoteResourceNotFoundException |
||
158 | * @throws UnknownRemoteException |
||
159 | * @throws RemoteInstanceException |
||
160 | * @throws RequestBuilderException |
||
161 | */ |
||
162 | public function newEvent(FederatedEvent $event): array { |
||
163 | $federatedItem = $this->getFederatedItem($event, false); |
||
164 | $this->confirmInitiator($event, true); |
||
165 | |||
166 | if ($event->canBypass(FederatedEvent::BYPASS_CIRCLE)) { |
||
167 | $instance = $this->interfaceService->getLocalInstance(); |
||
168 | } else { |
||
169 | $instance = $event->getCircle()->getInstance(); |
||
170 | } |
||
171 | |||
172 | $event->setSource($instance); |
||
173 | if ($this->configService->isLocalInstance($instance)) { |
||
174 | $event->setIncomingOrigin($instance); |
||
175 | $federatedItem->verify($event); |
||
176 | |||
177 | if ($event->isDataRequestOnly()) { |
||
178 | return $event->getOutcome(); |
||
179 | } |
||
180 | |||
181 | if (!$event->isAsync()) { |
||
182 | $federatedItem->manage($event); |
||
183 | } |
||
184 | |||
185 | $this->initBroadcast($event); |
||
186 | } else { |
||
187 | $this->remoteUpstreamService->confirmEvent($event); |
||
188 | if ($event->isDataRequestOnly()) { |
||
189 | return $event->getOutcome(); |
||
190 | } |
||
191 | |||
192 | // if (!$event->isAsync()) { |
||
193 | // $federatedItem->manage($event); |
||
194 | // } |
||
195 | } |
||
196 | |||
197 | return $event->getOutcome(); |
||
198 | } |
||
199 | |||
200 | |||
201 | /** |
||
202 | * This confirmation is optional, method is just here to avoid going too far away on the process |
||
203 | * |
||
204 | * @param FederatedEvent $event |
||
205 | * @param bool $local |
||
206 | * |
||
207 | * @throws InitiatorNotConfirmedException |
||
208 | */ |
||
209 | public function confirmInitiator(FederatedEvent $event, bool $local = false): void { |
||
238 | |||
239 | |||
240 | /** |
||
241 | * @param FederatedEvent $event |
||
242 | * @param bool $checkLocalOnly |
||
243 | * |
||
244 | * @return IFederatedItem |
||
245 | * @throws FederatedEventException |
||
246 | */ |
||
247 | public function getFederatedItem(FederatedEvent $event, bool $checkLocalOnly = true): IFederatedItem { |
||
248 | $class = $event->getClass(); |
||
249 | try { |
||
250 | $test = new ReflectionClass($class); |
||
251 | } catch (ReflectionException $e) { |
||
252 | throw new FederatedEventException('ReflectionException with ' . $class . ': ' . $e->getMessage()); |
||
253 | } |
||
254 | |||
255 | if (!in_array(IFederatedItem::class, $test->getInterfaceNames())) { |
||
256 | throw new FederatedEventException($class . ' does not implements IFederatedItem'); |
||
257 | } |
||
258 | |||
259 | $item = OC::$server->get($class); |
||
260 | if (!($item instanceof IFederatedItem)) { |
||
261 | throw new FederatedEventException($class . ' not an IFederatedItem'); |
||
262 | } |
||
263 | |||
264 | $this->setFederatedEventBypass($event, $item); |
||
265 | $this->confirmRequiredCondition($event, $item, $checkLocalOnly); |
||
266 | $this->configureEvent($event, $item); |
||
267 | |||
268 | // $this->confirmSharedItem($event, $item); |
||
269 | |||
270 | return $item; |
||
271 | } |
||
272 | |||
273 | |||
274 | /** |
||
275 | * Some event might need to bypass some checks |
||
276 | * |
||
277 | * @param FederatedEvent $event |
||
278 | * @param IFederatedItem $item |
||
279 | */ |
||
280 | private function setFederatedEventBypass(FederatedEvent $event, IFederatedItem $item) { |
||
281 | if ($item instanceof IFederatedItemLoopbackTest) { |
||
282 | $event->bypass(FederatedEvent::BYPASS_CIRCLE); |
||
283 | $event->bypass(FederatedEvent::BYPASS_INITIATORCHECK); |
||
284 | } |
||
285 | if ($item instanceof IFederatedItemCircleCheckNotRequired) { |
||
286 | $event->bypass(FederatedEvent::BYPASS_LOCALCIRCLECHECK); |
||
287 | } |
||
288 | if ($item instanceof IFederatedItemMemberCheckNotRequired) { |
||
289 | $event->bypass(FederatedEvent::BYPASS_LOCALMEMBERCHECK); |
||
290 | } |
||
291 | if ($item instanceof IFederatedItemInitiatorCheckNotRequired) { |
||
292 | $event->bypass(FederatedEvent::BYPASS_INITIATORCHECK); |
||
293 | } |
||
294 | if ($item instanceof IFederatedItemInitiatorMembershipNotRequired) { |
||
295 | $event->bypass(FederatedEvent::BYPASS_INITIATORMEMBERSHIP); |
||
296 | } |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Some event might require additional check |
||
301 | * |
||
302 | * @param FederatedEvent $event |
||
303 | * @param IFederatedItem $item |
||
304 | * @param bool $checkLocalOnly |
||
305 | * |
||
306 | * @throws FederatedEventException |
||
307 | */ |
||
308 | private function confirmRequiredCondition( |
||
309 | FederatedEvent $event, |
||
310 | IFederatedItem $item, |
||
311 | bool $checkLocalOnly = true |
||
312 | ) { |
||
313 | if (!$event->canBypass(FederatedEvent::BYPASS_CIRCLE) && !$event->hasCircle()) { |
||
314 | throw new FederatedEventException('FederatedEvent has no Circle linked'); |
||
315 | } |
||
316 | |||
317 | // TODO: enforce IFederatedItemMemberEmpty if no member |
||
318 | if ($item instanceof IFederatedItemMemberEmpty) { |
||
319 | $event->setMember(null); |
||
320 | } else if ($item instanceof IFederatedItemMemberRequired && !$event->hasMember()) { |
||
321 | throw new FederatedEventException('FederatedEvent has no Member linked'); |
||
322 | } |
||
323 | |||
324 | if ($event->hasMember() |
||
325 | && !($item instanceof IFederatedItemMemberRequired) |
||
326 | && !($item instanceof IFederatedItemMemberOptional)) { |
||
327 | throw new FederatedEventException( |
||
328 | get_class($item) |
||
329 | . ' does not implements IFederatedItemMemberOptional nor IFederatedItemMemberRequired' |
||
330 | ); |
||
331 | } |
||
332 | |||
333 | if ($item instanceof IFederatedItemMustBeInitializedLocally && $checkLocalOnly) { |
||
334 | throw new FederatedEventException('FederatedItem must be executed locally'); |
||
335 | } |
||
336 | } |
||
337 | |||
338 | |||
339 | /** |
||
340 | * @param FederatedEvent $event |
||
341 | * @param IFederatedItem $item |
||
342 | * |
||
343 | * @throws FederatedEventException |
||
344 | * @throws FederatedShareBelongingException |
||
345 | * @throws FederatedShareNotFoundException |
||
346 | * @throws OwnerNotFoundException |
||
347 | */ |
||
348 | private function confirmSharedItem(FederatedEvent $event, IFederatedItem $item): void { |
||
349 | if (!$item instanceof IFederatedItemSharedItem) { |
||
350 | return; |
||
351 | } |
||
352 | |||
353 | if ($event->getItemId() === '') { |
||
354 | throw new FederatedEventException('FederatedItem must contains ItemId'); |
||
355 | } |
||
356 | |||
357 | if ($this->configService->isLocalInstance($event->getCircle()->getInstance())) { |
||
358 | $shareLock = $this->shareLockRequest->getShare($event->getItemId()); |
||
359 | if ($shareLock->getInstance() !== $event->getIncomingOrigin()) { |
||
360 | throw new FederatedShareBelongingException('ShareLock belongs to another instance'); |
||
361 | } |
||
362 | } |
||
363 | } |
||
364 | |||
365 | |||
366 | /** |
||
367 | * @param FederatedEvent $event |
||
368 | * @param IFederatedItem $item |
||
369 | */ |
||
370 | private function configureEvent(FederatedEvent $event, IFederatedItem $item) { |
||
381 | |||
382 | |||
383 | /** |
||
384 | * async the process, generate a local request that will be closed. |
||
385 | * |
||
386 | * @param FederatedEvent $event |
||
387 | * |
||
388 | * @throws RequestBuilderException |
||
389 | */ |
||
390 | public function initBroadcast(FederatedEvent $event): void { |
||
433 | |||
434 | |||
435 | /** |
||
436 | * @param FederatedEvent $event |
||
437 | * |
||
438 | * @return RemoteInstance[] |
||
439 | * @throws RequestBuilderException |
||
440 | */ |
||
441 | public function getInstances(FederatedEvent $event): array { |
||
484 | |||
485 | |||
486 | /** |
||
487 | * should be used to manage results from events, like sending mails on user creation |
||
488 | * |
||
489 | * @param string $token |
||
490 | */ |
||
491 | public function manageResults(string $token): void { |
||
522 | |||
523 | } |
||
524 | |||
525 |