Passed
Push — master ( 4ade7f...6b2cf4 )
by Blizzz
15:25 queued 12s
created

Manager::validateOperation()   C

Complexity

Conditions 12
Paths 11

Size

Total Lines 51
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 27
nc 11
nop 6
dl 0
loc 51
rs 6.9666
c 0
b 0
f 0

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
/**
3
 * @copyright Copyright (c) 2016 Morris Jobke <[email protected]>
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OCA\WorkflowEngine;
23
24
use Doctrine\DBAL\DBALException;
25
use OC\Cache\CappedMemoryCache;
26
use OCA\WorkflowEngine\AppInfo\Application;
27
use OCA\WorkflowEngine\Check\FileMimeType;
28
use OCA\WorkflowEngine\Check\FileName;
29
use OCA\WorkflowEngine\Check\FileSize;
30
use OCA\WorkflowEngine\Check\FileSystemTags;
31
use OCA\WorkflowEngine\Check\RequestRemoteAddress;
32
use OCA\WorkflowEngine\Check\RequestTime;
33
use OCA\WorkflowEngine\Check\RequestURL;
34
use OCA\WorkflowEngine\Check\RequestUserAgent;
35
use OCA\WorkflowEngine\Check\UserGroupMembership;
36
use OCA\WorkflowEngine\Entity\File;
37
use OCA\WorkflowEngine\Helper\ScopeContext;
38
use OCA\WorkflowEngine\Service\Logger;
39
use OCA\WorkflowEngine\Service\RuleMatcher;
40
use OCP\AppFramework\QueryException;
41
use OCP\DB\QueryBuilder\IQueryBuilder;
42
use OCP\EventDispatcher\IEventDispatcher;
43
use OCP\Files\Storage\IStorage;
44
use OCP\IConfig;
45
use OCP\IDBConnection;
46
use OCP\IL10N;
47
use OCP\ILogger;
48
use OCP\IServerContainer;
49
use OCP\IUserSession;
50
use OCP\WorkflowEngine\Events\RegisterChecksEvent;
51
use OCP\WorkflowEngine\Events\RegisterEntitiesEvent;
52
use OCP\WorkflowEngine\Events\RegisterOperationsEvent;
53
use OCP\WorkflowEngine\ICheck;
54
use OCP\WorkflowEngine\IComplexOperation;
55
use OCP\WorkflowEngine\IEntity;
56
use OCP\WorkflowEngine\IEntityEvent;
57
use OCP\WorkflowEngine\IManager;
58
use OCP\WorkflowEngine\IOperation;
59
use OCP\WorkflowEngine\IRuleMatcher;
60
use Symfony\Component\EventDispatcher\EventDispatcherInterface as LegacyDispatcher;
61
use Symfony\Component\EventDispatcher\GenericEvent;
62
63
class Manager implements IManager {
64
65
	/** @var IStorage */
66
	protected $storage;
67
68
	/** @var string */
69
	protected $path;
70
71
	/** @var object */
72
	protected $entity;
73
74
	/** @var array[] */
75
	protected $operations = [];
76
77
	/** @var array[] */
78
	protected $checks = [];
79
80
	/** @var IDBConnection */
81
	protected $connection;
82
83
	/** @var IServerContainer|\OC\Server */
84
	protected $container;
85
86
	/** @var IL10N */
87
	protected $l;
88
89
	/** @var LegacyDispatcher */
90
	protected $legacyEventDispatcher;
91
92
	/** @var IEntity[] */
93
	protected $registeredEntities = [];
94
95
	/** @var IOperation[] */
96
	protected $registeredOperators = [];
97
98
	/** @var ICheck[] */
99
	protected $registeredChecks = [];
100
101
	/** @var ILogger */
102
	protected $logger;
103
104
	/** @var CappedMemoryCache */
105
	protected $operationsByScope = [];
106
107
	/** @var IUserSession */
108
	protected $session;
109
110
	/** @var IEventDispatcher */
111
	private $dispatcher;
112
113
	/** @var IConfig */
114
	private $config;
115
116
	public function __construct(
117
		IDBConnection $connection,
118
		IServerContainer $container,
119
		IL10N $l,
120
		LegacyDispatcher $eventDispatcher,
121
		ILogger $logger,
122
		IUserSession $session,
123
		IEventDispatcher $dispatcher,
124
		IConfig $config
125
	) {
126
		$this->connection = $connection;
127
		$this->container = $container;
128
		$this->l = $l;
129
		$this->legacyEventDispatcher = $eventDispatcher;
130
		$this->logger = $logger;
131
		$this->operationsByScope = new CappedMemoryCache(64);
132
		$this->session = $session;
133
		$this->dispatcher = $dispatcher;
134
		$this->config = $config;
135
	}
136
137
	public function getRuleMatcher(): IRuleMatcher {
138
		return new RuleMatcher(
139
			$this->session,
140
			$this->container,
141
			$this->l,
142
			$this,
143
			$this->container->query(Logger::class)
144
		);
145
	}
146
147
	public function getAllConfiguredEvents() {
148
		$query = $this->connection->getQueryBuilder();
149
150
		$query->select('class', 'entity', $query->expr()->castColumn('events', IQueryBuilder::PARAM_STR))
151
			->from('flow_operations')
152
			->where($query->expr()->neq('events', $query->createNamedParameter('[]'), IQueryBuilder::PARAM_STR))
153
			->groupBy('class', 'entity', $query->expr()->castColumn('events', IQueryBuilder::PARAM_STR));
154
155
		$result = $query->execute();
156
		$operations = [];
157
		while ($row = $result->fetch()) {
158
			$eventNames = \json_decode($row['events']);
159
160
			$operation = $row['class'];
161
			$entity = $row['entity'];
162
163
			$operations[$operation] = $operations[$row['class']] ?? [];
164
			$operations[$operation][$entity] = $operations[$operation][$entity] ?? [];
165
166
			$operations[$operation][$entity] = array_unique(array_merge($operations[$operation][$entity], $eventNames ?? []));
167
		}
168
		$result->closeCursor();
169
170
		return $operations;
171
	}
172
173
	/**
174
	 * @param string $operationClass
175
	 * @return ScopeContext[]
176
	 */
177
	public function getAllConfiguredScopesForOperation(string $operationClass): array {
178
		static $scopesByOperation = [];
179
		if (isset($scopesByOperation[$operationClass])) {
180
			return $scopesByOperation[$operationClass];
181
		}
182
183
		$query = $this->connection->getQueryBuilder();
184
185
		$query->selectDistinct('s.type')
186
			->addSelect('s.value')
187
			->from('flow_operations', 'o')
188
			->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
189
			->where($query->expr()->eq('o.class', $query->createParameter('operationClass')));
190
191
		$query->setParameters(['operationClass' => $operationClass]);
192
		$result = $query->execute();
193
194
		$scopesByOperation[$operationClass] = [];
195
		while ($row = $result->fetch()) {
196
			$scope = new ScopeContext($row['type'], $row['value']);
197
			$scopesByOperation[$operationClass][$scope->getHash()] = $scope;
198
		}
199
200
		return $scopesByOperation[$operationClass];
201
	}
202
203
	public function getAllOperations(ScopeContext $scopeContext): array {
204
		if (isset($this->operations[$scopeContext->getHash()])) {
205
			return $this->operations[$scopeContext->getHash()];
206
		}
207
208
		$query = $this->connection->getQueryBuilder();
209
210
		$query->select('o.*')
211
			->selectAlias('s.type', 'scope_type')
212
			->selectAlias('s.value', 'scope_actor_id')
213
			->from('flow_operations', 'o')
214
			->leftJoin('o', 'flow_operations_scope', 's', $query->expr()->eq('o.id', 's.operation_id'))
215
			->where($query->expr()->eq('s.type', $query->createParameter('scope')));
216
217
		if ($scopeContext->getScope() === IManager::SCOPE_USER) {
218
			$query->andWhere($query->expr()->eq('s.value', $query->createParameter('scopeId')));
219
		}
220
221
		$query->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
222
		$result = $query->execute();
223
224
		$this->operations[$scopeContext->getHash()] = [];
225
		while ($row = $result->fetch()) {
226
			if (!isset($this->operations[$scopeContext->getHash()][$row['class']])) {
227
				$this->operations[$scopeContext->getHash()][$row['class']] = [];
228
			}
229
			$this->operations[$scopeContext->getHash()][$row['class']][] = $row;
230
		}
231
232
		return $this->operations[$scopeContext->getHash()];
233
	}
234
235
	public function getOperations(string $class, ScopeContext $scopeContext): array {
236
		if (!isset($this->operations[$scopeContext->getHash()])) {
237
			$this->getAllOperations($scopeContext);
238
		}
239
		return $this->operations[$scopeContext->getHash()][$class] ?? [];
240
	}
241
242
	/**
243
	 * @param int $id
244
	 * @return array
245
	 * @throws \UnexpectedValueException
246
	 */
247
	protected function getOperation($id) {
248
		$query = $this->connection->getQueryBuilder();
249
		$query->select('*')
250
			->from('flow_operations')
251
			->where($query->expr()->eq('id', $query->createNamedParameter($id)));
252
		$result = $query->execute();
253
		$row = $result->fetch();
254
		$result->closeCursor();
255
256
		if ($row) {
257
			return $row;
258
		}
259
260
		throw new \UnexpectedValueException($this->l->t('Operation #%s does not exist', [$id]));
261
	}
262
263
	protected function insertOperation(
264
		string $class,
265
		string $name,
266
		array $checkIds,
267
		string $operation,
268
		string $entity,
269
		array $events
270
	): int {
271
		$query = $this->connection->getQueryBuilder();
272
		$query->insert('flow_operations')
273
			->values([
274
				'class' => $query->createNamedParameter($class),
275
				'name' => $query->createNamedParameter($name),
276
				'checks' => $query->createNamedParameter(json_encode(array_unique($checkIds))),
277
				'operation' => $query->createNamedParameter($operation),
278
				'entity' => $query->createNamedParameter($entity),
279
				'events' => $query->createNamedParameter(json_encode($events))
280
			]);
281
		$query->execute();
282
283
		return $query->getLastInsertId();
284
	}
285
286
	/**
287
	 * @param string $class
288
	 * @param string $name
289
	 * @param array[] $checks
290
	 * @param string $operation
291
	 * @return array The added operation
292
	 * @throws \UnexpectedValueException
293
	 * @throws DBALException
294
	 */
295
	public function addOperation(
296
		string $class,
297
		string $name,
298
		array $checks,
299
		string $operation,
300
		ScopeContext $scope,
301
		string $entity,
302
		array $events
303
	) {
304
		$this->validateOperation($class, $name, $checks, $operation, $entity, $events);
305
306
		$this->connection->beginTransaction();
307
308
		try {
309
			$checkIds = [];
310
			foreach ($checks as $check) {
311
				$checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
312
			}
313
314
			$id = $this->insertOperation($class, $name, $checkIds, $operation, $entity, $events);
315
			$this->addScope($id, $scope);
316
317
			$this->connection->commit();
318
		} catch (DBALException $e) {
319
			$this->connection->rollBack();
320
			throw $e;
321
		}
322
323
		return $this->getOperation($id);
324
	}
325
326
	protected function canModify(int $id, ScopeContext $scopeContext):bool {
327
		if (isset($this->operationsByScope[$scopeContext->getHash()])) {
328
			return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
329
		}
330
331
		$qb = $this->connection->getQueryBuilder();
332
		$qb = $qb->select('o.id')
333
			->from('flow_operations', 'o')
334
			->leftJoin('o', 'flow_operations_scope', 's', $qb->expr()->eq('o.id', 's.operation_id'))
335
			->where($qb->expr()->eq('s.type', $qb->createParameter('scope')));
336
337
		if ($scopeContext->getScope() !== IManager::SCOPE_ADMIN) {
338
			$qb->where($qb->expr()->eq('s.value', $qb->createParameter('scopeId')));
339
		}
340
341
		$qb->setParameters(['scope' => $scopeContext->getScope(), 'scopeId' => $scopeContext->getScopeId()]);
342
		$result = $qb->execute();
343
344
		$this->operationsByScope[$scopeContext->getHash()] = [];
345
		while ($opId = $result->fetchColumn(0)) {
346
			$this->operationsByScope[$scopeContext->getHash()][] = (int)$opId;
347
		}
348
		$result->closeCursor();
349
350
		return in_array($id, $this->operationsByScope[$scopeContext->getHash()], true);
351
	}
352
353
	/**
354
	 * @param int $id
355
	 * @param string $name
356
	 * @param array[] $checks
357
	 * @param string $operation
358
	 * @return array The updated operation
359
	 * @throws \UnexpectedValueException
360
	 * @throws \DomainException
361
	 * @throws DBALException
362
	 */
363
	public function updateOperation(
364
		int $id,
365
		string $name,
366
		array $checks,
367
		string $operation,
368
		ScopeContext $scopeContext,
369
		string $entity,
370
		array $events
371
	): array {
372
		if (!$this->canModify($id, $scopeContext)) {
373
			throw new \DomainException('Target operation not within scope');
374
		};
375
		$row = $this->getOperation($id);
376
		$this->validateOperation($row['class'], $name, $checks, $operation, $entity, $events);
377
378
		$checkIds = [];
379
		try {
380
			$this->connection->beginTransaction();
381
			foreach ($checks as $check) {
382
				$checkIds[] = $this->addCheck($check['class'], $check['operator'], $check['value']);
383
			}
384
385
			$query = $this->connection->getQueryBuilder();
386
			$query->update('flow_operations')
387
				->set('name', $query->createNamedParameter($name))
388
				->set('checks', $query->createNamedParameter(json_encode(array_unique($checkIds))))
389
				->set('operation', $query->createNamedParameter($operation))
390
				->set('entity', $query->createNamedParameter($entity))
391
				->set('events', $query->createNamedParameter(json_encode($events)))
392
				->where($query->expr()->eq('id', $query->createNamedParameter($id)));
393
			$query->execute();
394
			$this->connection->commit();
395
		} catch (DBALException $e) {
396
			$this->connection->rollBack();
397
			throw $e;
398
		}
399
		unset($this->operations[$scopeContext->getHash()]);
400
401
		return $this->getOperation($id);
402
	}
403
404
	/**
405
	 * @param int $id
406
	 * @return bool
407
	 * @throws \UnexpectedValueException
408
	 * @throws DBALException
409
	 * @throws \DomainException
410
	 */
411
	public function deleteOperation($id, ScopeContext $scopeContext) {
412
		if (!$this->canModify($id, $scopeContext)) {
413
			throw new \DomainException('Target operation not within scope');
414
		};
415
		$query = $this->connection->getQueryBuilder();
416
		try {
417
			$this->connection->beginTransaction();
418
			$result = (bool)$query->delete('flow_operations')
419
				->where($query->expr()->eq('id', $query->createNamedParameter($id)))
420
				->execute();
421
			if ($result) {
422
				$qb = $this->connection->getQueryBuilder();
423
				$result &= (bool)$qb->delete('flow_operations_scope')
424
					->where($qb->expr()->eq('operation_id', $qb->createNamedParameter($id)))
425
					->execute();
426
			}
427
			$this->connection->commit();
428
		} catch (DBALException $e) {
429
			$this->connection->rollBack();
430
			throw $e;
431
		}
432
433
		if (isset($this->operations[$scopeContext->getHash()])) {
434
			unset($this->operations[$scopeContext->getHash()]);
435
		}
436
437
		return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
438
	}
439
440
	protected function validateEvents(string $entity, array $events, IOperation $operation) {
441
		try {
442
			/** @var IEntity $instance */
443
			$instance = $this->container->query($entity);
444
		} catch (QueryException $e) {
445
			throw new \UnexpectedValueException($this->l->t('Entity %s does not exist', [$entity]));
446
		}
447
448
		if (!$instance instanceof IEntity) {
0 ignored issues
show
introduced by
$instance is always a sub-type of OCP\WorkflowEngine\IEntity.
Loading history...
449
			throw new \UnexpectedValueException($this->l->t('Entity %s is invalid', [$entity]));
450
		}
451
452
		if (empty($events)) {
453
			if (!$operation instanceof IComplexOperation) {
454
				throw new \UnexpectedValueException($this->l->t('No events are chosen.'));
455
			}
456
			return;
457
		}
458
459
		$availableEvents = [];
460
		foreach ($instance->getEvents() as $event) {
461
			/** @var IEntityEvent $event */
462
			$availableEvents[] = $event->getEventName();
463
		}
464
465
		$diff = array_diff($events, $availableEvents);
466
		if (!empty($diff)) {
467
			throw new \UnexpectedValueException($this->l->t('Entity %s has no event %s', [$entity, array_shift($diff)]));
468
		}
469
	}
470
471
	/**
472
	 * @param string $class
473
	 * @param string $name
474
	 * @param array[] $checks
475
	 * @param string $operation
476
	 * @throws \UnexpectedValueException
477
	 */
478
	public function validateOperation($class, $name, array $checks, $operation, string $entity, array $events) {
479
		try {
480
			/** @var IOperation $instance */
481
			$instance = $this->container->query($class);
482
		} catch (QueryException $e) {
483
			throw new \UnexpectedValueException($this->l->t('Operation %s does not exist', [$class]));
484
		}
485
486
		if (!($instance instanceof IOperation)) {
0 ignored issues
show
introduced by
$instance is always a sub-type of OCP\WorkflowEngine\IOperation.
Loading history...
487
			throw new \UnexpectedValueException($this->l->t('Operation %s is invalid', [$class]));
488
		}
489
490
		$this->validateEvents($entity, $events, $instance);
491
492
		if (count($checks) === 0) {
493
			throw new \UnexpectedValueException($this->l->t('At least one check needs to be provided'));
494
		}
495
496
		if (strlen((string)$operation) > IManager::MAX_OPERATION_VALUE_BYTES) {
497
			throw new \UnexpectedValueException($this->l->t('The provided operation data is too long'));
498
		}
499
500
		$instance->validateOperation($name, $checks, $operation);
501
502
		foreach ($checks as $check) {
503
			if (!is_string($check['class'])) {
504
				throw new \UnexpectedValueException($this->l->t('Invalid check provided'));
505
			}
506
507
			try {
508
				/** @var ICheck $instance */
509
				$instance = $this->container->query($check['class']);
510
			} catch (QueryException $e) {
511
				throw new \UnexpectedValueException($this->l->t('Check %s does not exist', [$class]));
512
			}
513
514
			if (!($instance instanceof ICheck)) {
515
				throw new \UnexpectedValueException($this->l->t('Check %s is invalid', [$class]));
516
			}
517
518
			if (!empty($instance->supportedEntities())
519
				&& !in_array($entity, $instance->supportedEntities())
520
			) {
521
				throw new \UnexpectedValueException($this->l->t('Check %s is not allowed with this entity', [$class]));
522
			}
523
524
			if (strlen((string)$check['value']) > IManager::MAX_CHECK_VALUE_BYTES) {
525
				throw new \UnexpectedValueException($this->l->t('The provided check value is too long'));
526
			}
527
528
			$instance->validateCheck($check['operator'], $check['value']);
529
		}
530
	}
531
532
	/**
533
	 * @param int[] $checkIds
534
	 * @return array[]
535
	 */
536
	public function getChecks(array $checkIds) {
537
		$checkIds = array_map('intval', $checkIds);
538
539
		$checks = [];
540
		foreach ($checkIds as $i => $checkId) {
541
			if (isset($this->checks[$checkId])) {
542
				$checks[$checkId] = $this->checks[$checkId];
543
				unset($checkIds[$i]);
544
			}
545
		}
546
547
		if (empty($checkIds)) {
548
			return $checks;
549
		}
550
551
		$query = $this->connection->getQueryBuilder();
552
		$query->select('*')
553
			->from('flow_checks')
554
			->where($query->expr()->in('id', $query->createNamedParameter($checkIds, IQueryBuilder::PARAM_INT_ARRAY)));
555
		$result = $query->execute();
556
557
		while ($row = $result->fetch()) {
558
			$this->checks[(int) $row['id']] = $row;
559
			$checks[(int) $row['id']] = $row;
560
		}
561
		$result->closeCursor();
562
563
		$checkIds = array_diff($checkIds, array_keys($checks));
564
565
		if (!empty($checkIds)) {
566
			$missingCheck = array_pop($checkIds);
567
			throw new \UnexpectedValueException($this->l->t('Check #%s does not exist', $missingCheck));
568
		}
569
570
		return $checks;
571
	}
572
573
	/**
574
	 * @param string $class
575
	 * @param string $operator
576
	 * @param string $value
577
	 * @return int Check unique ID
578
	 */
579
	protected function addCheck($class, $operator, $value) {
580
		$hash = md5($class . '::' . $operator . '::' . $value);
581
582
		$query = $this->connection->getQueryBuilder();
583
		$query->select('id')
584
			->from('flow_checks')
585
			->where($query->expr()->eq('hash', $query->createNamedParameter($hash)));
586
		$result = $query->execute();
587
588
		if ($row = $result->fetch()) {
589
			$result->closeCursor();
590
			return (int) $row['id'];
591
		}
592
593
		$query = $this->connection->getQueryBuilder();
594
		$query->insert('flow_checks')
595
			->values([
596
				'class' => $query->createNamedParameter($class),
597
				'operator' => $query->createNamedParameter($operator),
598
				'value' => $query->createNamedParameter($value),
599
				'hash' => $query->createNamedParameter($hash),
600
			]);
601
		$query->execute();
602
603
		return $query->getLastInsertId();
604
	}
605
606
	protected function addScope(int $operationId, ScopeContext $scope): void {
607
		$query = $this->connection->getQueryBuilder();
608
609
		$insertQuery = $query->insert('flow_operations_scope');
610
		$insertQuery->values([
611
			'operation_id' => $query->createNamedParameter($operationId),
612
			'type' => $query->createNamedParameter($scope->getScope()),
613
			'value' => $query->createNamedParameter($scope->getScopeId()),
614
		]);
615
		$insertQuery->execute();
616
	}
617
618
	public function formatOperation(array $operation): array {
619
		$checkIds = json_decode($operation['checks'], true);
620
		$checks = $this->getChecks($checkIds);
621
622
		$operation['checks'] = [];
623
		foreach ($checks as $check) {
624
			// Remove internal values
625
			unset($check['id']);
626
			unset($check['hash']);
627
628
			$operation['checks'][] = $check;
629
		}
630
		$operation['events'] = json_decode($operation['events'], true) ?? [];
631
632
633
		return $operation;
634
	}
635
636
	/**
637
	 * @return IEntity[]
638
	 */
639
	public function getEntitiesList(): array {
640
		$this->dispatcher->dispatchTyped(new RegisterEntitiesEvent($this));
641
		$this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_ENTITY, new GenericEvent($this));
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with new Symfony\Component\Ev...her\GenericEvent($this). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

641
		$this->legacyEventDispatcher->/** @scrutinizer ignore-call */ 
642
                                dispatch(IManager::EVENT_NAME_REG_ENTITY, new GenericEvent($this));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
OCP\WorkflowEngine\IManager::EVENT_NAME_REG_ENTITY of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

641
		$this->legacyEventDispatcher->dispatch(/** @scrutinizer ignore-type */ IManager::EVENT_NAME_REG_ENTITY, new GenericEvent($this));
Loading history...
642
643
		return array_values(array_merge($this->getBuildInEntities(), $this->registeredEntities));
644
	}
645
646
	/**
647
	 * @return IOperation[]
648
	 */
649
	public function getOperatorList(): array {
650
		$this->dispatcher->dispatchTyped(new RegisterOperationsEvent($this));
651
		$this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_OPERATION, new GenericEvent($this));
0 ignored issues
show
Bug introduced by
OCP\WorkflowEngine\IMana...VENT_NAME_REG_OPERATION of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

651
		$this->legacyEventDispatcher->dispatch(/** @scrutinizer ignore-type */ IManager::EVENT_NAME_REG_OPERATION, new GenericEvent($this));
Loading history...
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with new Symfony\Component\Ev...her\GenericEvent($this). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

651
		$this->legacyEventDispatcher->/** @scrutinizer ignore-call */ 
652
                                dispatch(IManager::EVENT_NAME_REG_OPERATION, new GenericEvent($this));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
652
653
		return array_merge($this->getBuildInOperators(), $this->registeredOperators);
654
	}
655
656
	/**
657
	 * @return ICheck[]
658
	 */
659
	public function getCheckList(): array {
660
		$this->dispatcher->dispatchTyped(new RegisterChecksEvent($this));
661
		$this->legacyEventDispatcher->dispatch(IManager::EVENT_NAME_REG_CHECK, new GenericEvent($this));
0 ignored issues
show
Bug introduced by
OCP\WorkflowEngine\IManager::EVENT_NAME_REG_CHECK of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

661
		$this->legacyEventDispatcher->dispatch(/** @scrutinizer ignore-type */ IManager::EVENT_NAME_REG_CHECK, new GenericEvent($this));
Loading history...
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with new Symfony\Component\Ev...her\GenericEvent($this). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

661
		$this->legacyEventDispatcher->/** @scrutinizer ignore-call */ 
662
                                dispatch(IManager::EVENT_NAME_REG_CHECK, new GenericEvent($this));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
662
663
		return array_merge($this->getBuildInChecks(), $this->registeredChecks);
664
	}
665
666
	public function registerEntity(IEntity $entity): void {
667
		$this->registeredEntities[get_class($entity)] = $entity;
668
	}
669
670
	public function registerOperation(IOperation $operator): void {
671
		$this->registeredOperators[get_class($operator)] = $operator;
672
	}
673
674
	public function registerCheck(ICheck $check): void {
675
		$this->registeredChecks[get_class($check)] = $check;
676
	}
677
678
	/**
679
	 * @return IEntity[]
680
	 */
681
	protected function getBuildInEntities(): array {
682
		try {
683
			return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array(OCA\Workflo...ne\Entity\File::class)) returns an array which contains values of type stdClass which are incompatible with the documented value type OCP\WorkflowEngine\IEntity.
Loading history...
684
				File::class => $this->container->query(File::class),
685
			];
686
		} catch (QueryException $e) {
687
			$this->logger->logException($e);
688
			return [];
689
		}
690
	}
691
692
	/**
693
	 * @return IOperation[]
694
	 */
695
	protected function getBuildInOperators(): array {
696
		try {
697
			return [
698
				// None yet
699
			];
700
		} catch (QueryException $e) {
0 ignored issues
show
Unused Code introduced by
catch (\OCP\AppFramework\QueryException $e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
701
			$this->logger->logException($e);
702
			return [];
703
		}
704
	}
705
706
	/**
707
	 * @return ICheck[]
708
	 */
709
	protected function getBuildInChecks(): array {
710
		try {
711
			return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($this->cont...roupMembership::class)) returns an array which contains values of type stdClass which are incompatible with the documented value type OCP\WorkflowEngine\ICheck.
Loading history...
712
				$this->container->query(FileMimeType::class),
713
				$this->container->query(FileName::class),
714
				$this->container->query(FileSize::class),
715
				$this->container->query(FileSystemTags::class),
716
				$this->container->query(RequestRemoteAddress::class),
717
				$this->container->query(RequestTime::class),
718
				$this->container->query(RequestURL::class),
719
				$this->container->query(RequestUserAgent::class),
720
				$this->container->query(UserGroupMembership::class),
721
			];
722
		} catch (QueryException $e) {
723
			$this->logger->logException($e);
724
			return [];
725
		}
726
	}
727
728
	public function isUserScopeEnabled(): bool {
729
		return $this->config->getAppValue(Application::APP_ID, 'user_scope_disabled', 'no') === 'no';
730
	}
731
}
732