Passed
Push — master ( b552da...09c1d8 )
by Roeland
12:09 queued 12s
created

Manager::getOperations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

602
		$this->eventDispatcher->/** @scrutinizer ignore-call */ 
603
                          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

602
		$this->eventDispatcher->dispatch(/** @scrutinizer ignore-type */ IManager::EVENT_NAME_REG_ENTITY, new GenericEvent($this));
Loading history...
603
604
		return array_merge($this->getBuildInEntities(), $this->registeredEntities);
605
	}
606
607
	/**
608
	 * @return IOperation[]
609
	 */
610
	public function getOperatorList(): array {
611
		$this->eventDispatcher->dispatch(IManager::EVENT_NAME_REG_OPERATION, 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

611
		$this->eventDispatcher->/** @scrutinizer ignore-call */ 
612
                          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...
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

611
		$this->eventDispatcher->dispatch(/** @scrutinizer ignore-type */ IManager::EVENT_NAME_REG_OPERATION, new GenericEvent($this));
Loading history...
612
613
		return array_merge($this->getBuildInOperators(), $this->registeredOperators);
614
	}
615
616
	/**
617
	 * @return ICheck[]
618
	 */
619
	public function getCheckList(): array {
620
		$this->eventDispatcher->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

620
		$this->eventDispatcher->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

620
		$this->eventDispatcher->/** @scrutinizer ignore-call */ 
621
                          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...
621
622
		return array_merge($this->getBuildInChecks(), $this->registeredChecks);
623
	}
624
625
	public function registerEntity(IEntity $entity): void {
626
		$this->registeredEntities[get_class($entity)] = $entity;
627
	}
628
629
	public function registerOperation(IOperation $operator): void {
630
		$this->registeredOperators[get_class($operator)] = $operator;
631
	}
632
633
	public function registerCheck(ICheck $check): void {
634
		$this->registeredChecks[get_class($check)] = $check;
635
	}
636
637
	/**
638
	 * @return IEntity[]
639
	 */
640
	protected function getBuildInEntities(): array {
641
		try {
642
			return [
643
				$this->container->query(File::class),
644
			];
645
		} catch (QueryException $e) {
646
			$this->logger->logException($e);
647
			return [];
648
		}
649
	}
650
651
	/**
652
	 * @return IOperation[]
653
	 */
654
	protected function getBuildInOperators(): array {
655
		try {
656
			return [
657
				// None yet
658
			];
659
		} 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...
660
			$this->logger->logException($e);
661
			return [];
662
		}
663
	}
664
665
	/**
666
	 * @return IEntity[]
667
	 */
668
	protected function getBuildInChecks(): array {
669
		try {
670
			return [
671
				$this->container->query(FileMimeType::class),
672
				$this->container->query(FileName::class),
673
				$this->container->query(FileSize::class),
674
				$this->container->query(FileSystemTags::class),
675
				$this->container->query(RequestRemoteAddress::class),
676
				$this->container->query(RequestTime::class),
677
				$this->container->query(RequestURL::class),
678
				$this->container->query(RequestUserAgent::class),
679
				$this->container->query(UserGroupMembership::class),
680
			];
681
		} catch (QueryException $e) {
682
			$this->logger->logException($e);
683
			return [];
684
		}
685
	}
686
}
687