Test Failed
Push — master ( 8c814b...380005 )
by Raffael
08:49
created

Filesystem::findReferenceNode()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 40
ccs 0
cts 21
cp 0
rs 8.9688
c 0
b 0
f 0
cc 5
crap 30
nc 4
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * balloon
7
 *
8
 * @copyright   Copryright (c) 2012-2019 gyselroth GmbH (https://gyselroth.com)
9
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
10
 */
11
12
namespace Balloon;
13
14
use Balloon\Filesystem\Acl;
15
use Balloon\Filesystem\Acl\Exception\Forbidden as ForbiddenException;
16
use Balloon\Filesystem\Delta;
17
use Balloon\Filesystem\Exception;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Balloon\Exception.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
18
use Balloon\Filesystem\Node\Collection;
19
use Balloon\Filesystem\Node\Factory as NodeFactory;
20
use Balloon\Filesystem\Node\NodeInterface;
21
use Balloon\Server\User;
22
use Generator;
23
use MongoDB\BSON\ObjectId;
24
use MongoDB\BSON\UTCDateTime;
25
use MongoDB\Database;
26
use Psr\Log\LoggerInterface;
27
28
class Filesystem
29
{
30
    /**
31
     * Database.
32
     *
33
     * @var Database
34
     */
35
    protected $db;
36
37
    /**
38
     * LoggerInterface.
39
     *
40
     * @var LoggerInterface
41
     */
42
    protected $logger;
43
44
    /**
45
     * Hook.
46
     *
47
     * @var Hook
48
     */
49
    protected $hook;
50
51
    /**
52
     * Server.
53
     *
54
     * @var Server
55
     */
56
    protected $server;
57
58
    /**
59
     * Root collection.
60
     *
61
     * @var Collection
62
     */
63
    protected $root;
64
65
    /**
66
     * User.
67
     *
68
     * @var Delta
69
     */
70
    protected $delta;
71
72
    /**
73
     * Get user.
74
     *
75
     * @var User
76
     */
77
    protected $user;
78
79
    /**
80
     * Node factory.
81
     *
82
     * @var NodeFactory
83
     */
84
    protected $node_factory;
85
86
    /**
87
     * Acl.
88
     *
89
     * @var Acl
90
     */
91
    protected $acl;
92
93
    /**
94
     * Cache.
95
     *
96
     * @var array
97
     */
98
    protected $cache = [];
99
100
    /**
101
     * RAW Cache.
102
     *
103
     * @var array
104
     */
105
    protected $raw_cache = [];
106
107
    /**
108
     * Initialize.
109
     */
110
    public function __construct(Server $server, Database $db, Hook $hook, LoggerInterface $logger, NodeFactory $node_factory, Acl $acl, ?User $user = null)
111
    {
112
        $this->user = $user;
113
        $this->server = $server;
114
        $this->db = $db;
115
        $this->logger = $logger;
116
        $this->hook = $hook;
117
        $this->node_factory = $node_factory;
118
        $this->acl = $acl;
119
    }
120
121
    /**
122
     * Get user.
123
     */
124
    public function getUser(): ?User
125
    {
126
        return $this->user;
127
    }
128
129
    /**
130
     * Get server.
131
     */
132
    public function getServer(): Server
133
    {
134
        return $this->server;
135
    }
136
137
    /**
138
     * Get database.
139
     */
140
    public function getDatabase(): Database
141
    {
142
        return $this->db;
143
    }
144
145
    /**
146
     * Get root.
147
     */
148
    public function getRoot(): Collection
149
    {
150
        if ($this->root instanceof Collection) {
151
            return $this->root;
152
        }
153
154
        return $this->root = $this->initNode([
155
            'directory' => true,
156
            '_id' => null,
157
            'owner' => $this->user ? $this->user->getId() : null,
158
        ]);
159
    }
160
161
    /**
162
     * Get delta.
163
     */
164
    public function getDelta(): Delta
165
    {
166
        if ($this->delta instanceof Delta) {
167
            return $this->delta;
168
        }
169
170
        return $this->delta = new Delta($this, $this->db, $this->acl);
171
    }
172
173
    /**
174
     * Find raw node.
175
     */
176
    public function findRawNode(ObjectId $id): array
177
    {
178
        if (isset($this->raw_cache[(string) $id])) {
179
            return $this->raw_cache[(string) $id];
180
        }
181
182
        $node = $this->db->storage->findOne(['_id' => $id]);
183
        if (null === $node) {
184
            throw new Exception\NotFound(
185
                'node '.$id.' not found',
186
                Exception\NotFound::NODE_NOT_FOUND
187
            );
188
        }
189
190
        $this->raw_cache[(string) $id] = $node;
191
192
        return $node;
193
    }
194
195
    /**
196
     * Factory loader.
197
     */
198
    public function findNodeById($id, ?string $class = null, int $deleted = NodeInterface::DELETED_INCLUDE): NodeInterface
199
    {
200
        if (isset($this->cache[(string) $id])) {
201
            return $this->cache[(string) $id];
202
        }
203
204
        if (!is_string($id) && !($id instanceof ObjectId)) {
0 ignored issues
show
Bug introduced by
The class MongoDB\BSON\ObjectId does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
205
            throw new Exception\InvalidArgument($id.' node id has to be a string or instance of \MongoDB\BSON\ObjectId');
206
        }
207
208
        try {
209
            if (is_string($id)) {
210
                $id = new ObjectId($id);
211
            }
212
        } catch (\Exception $e) {
213
            throw new Exception\InvalidArgument('invalid node id specified');
214
        }
215
216
        $filter = [
217
            '_id' => $id,
218
        ];
219
220
        if (NodeInterface::DELETED_EXCLUDE === $deleted) {
221
            $filter['deleted'] = false;
222
        } elseif (NodeInterface::DELETED_ONLY === $deleted) {
223
            $filter['deleted'] = ['$type' => 9];
224
        }
225
226
        $result = iterator_to_array($this->findNodesByFilterRecursiveChildren($filter, $deleted, 0, 1));
227
228
        if (count($result) === 0) {
229
            throw new Exception\NotFound(
230
                'node ['.$id.'] not found',
231
                Exception\NotFound::NODE_NOT_FOUND
232
            );
233
        }
234
235
        $node = array_shift($result);
236
        if (null !== $class && !($node instanceof $class)) {
237
            throw new Exception('node '.get_class($node).' is not instance of '.$class);
238
        }
239
240
        return $node;
241
    }
242
243
    /**
244
     * Find one.
245
     */
246
    public function findOne(array $filter, int $deleted = NodeInterface::DELETED_INCLUDE, ?Collection $parent = null): NodeInterface
247
    {
248
        $result = iterator_to_array($this->findNodesByFilterRecursiveChildren($filter, $deleted, 0, 1, $parent));
249
250
        if (count($result) === 0) {
251
            throw new Exception\NotFound(
252
                'requested node not found',
253
                Exception\NotFound::NODE_NOT_FOUND
254
            );
255
        }
256
257
        return array_shift($result);
258
    }
259
260
    /**
261
     * Load nodes by id.
262
     *
263
     * @deprecated
264
     */
265
    public function findNodesById(array $id = [], ?string $class = null, int $deleted = NodeInterface::DELETED_INCLUDE): Generator
266
    {
267
        $find = [];
268
        foreach ($id as $i) {
269
            $find[] = new ObjectId($i);
270
        }
271
272
        $filter = [
273
            '_id' => ['$in' => $find],
274
        ];
275
276
        switch ($deleted) {
277
            case NodeInterface::DELETED_INCLUDE:
278
                break;
279
            case NodeInterface::DELETED_EXCLUDE:
280
                $filter['deleted'] = false;
281
282
                break;
283
            case NodeInterface::DELETED_ONLY:
284
                $filter['deleted'] = ['$type' => 9];
285
286
                break;
287
        }
288
289
        $result = $this->db->storage->find($filter);
290
291
        $nodes = [];
292
        foreach ($result as $node) {
293
            try {
294
                $return = $this->initNode($node);
295
296
                if (in_array($return->getId(), $nodes)) {
297
                    continue;
298
                }
299
300
                $nodes[] = $return->getId();
301
            } catch (\Exception $e) {
302
                $this->logger->error('remove node from result list, failed load node', [
303
                    'category' => get_class($this),
304
                    'exception' => $e,
305
                ]);
306
307
                continue;
308
            }
309
310
            if (null !== $class && !($return instanceof $class)) {
311
                throw new Exception('node is not an instance of '.$class);
312
            }
313
314
            yield $return;
315
        }
316
    }
317
318
    /**
319
     * Load nodes by id.
320
     *
321
     * @param null|mixed $class
322
     *
323
     * @deprecated
324
     */
325
    public function getNodes(?array $id = null, $class = null, int $deleted = NodeInterface::DELETED_EXCLUDE): Generator
326
    {
327
        return $this->findNodesById($id, $class, $deleted);
0 ignored issues
show
Deprecated Code introduced by
The method Balloon\Filesystem::findNodesById() has been deprecated.

This method has been deprecated.

Loading history...
328
    }
329
330
    /**
331
     * Load node.
332
     *
333
     * @param null|mixed $id
334
     * @param null|mixed $class
335
     *
336
     * @deprecated
337
     */
338
    public function getNode($id = null, $class = null, bool $multiple = false, bool $allow_root = false, ?int $deleted = null): NodeInterface
339
    {
340
        if (empty($id)) {
341
            if (true === $allow_root) {
342
                return $this->getRoot();
343
            }
344
345
            throw new Exception\InvalidArgument('invalid id given');
346
        }
347
348
        if (null === $deleted) {
349
            $deleted = NodeInterface::DELETED_INCLUDE;
350
        }
351
352
        if (true === $multiple && is_array($id)) {
353
            return $this->findNodesById($id, $class, $deleted);
0 ignored issues
show
Deprecated Code introduced by
The method Balloon\Filesystem::findNodesById() has been deprecated.

This method has been deprecated.

Loading history...
354
        }
355
356
        return $this->findNodeById($id, $class, $deleted);
357
    }
358
359
    /**
360
     * Find node with custom filter.
361
     */
362
    public function findNodeByFilter(array $filter): NodeInterface
363
    {
364
        $result = $this->db->storage->findOne($filter);
365
        if (null === $result) {
366
            throw new Exception\NotFound(
367
                'node with custom filter was not found',
368
                Exception\NotFound::NODE_NOT_FOUND
369
            );
370
        }
371
372
        return $this->initNode($result);
373
    }
374
375
    /**
376
     * Count.
377
     */
378
    public function countNodes(array $filter = []): int
379
    {
380
        return $this->db->storage->count($filter);
381
    }
382
383
    /**
384
     * Find nodes with custom filters.
385
     */
386
    public function findNodesByFilter(array $filter, ?int $offset = null, ?int $limit = null): Generator
387
    {
388
        $result = $this->db->storage->find($filter, [
389
            'skip' => $offset,
390
            'limit' => $limit,
391
        ]);
392
393
        $count = $this->countNodes($filter);
394
395
        foreach ($result as $node) {
396
            try {
397
                yield $this->initNode($node);
398
            } catch (\Exception $e) {
399
                $this->logger->error('remove node from result list, failed load node', [
400
                    'category' => get_class($this),
401
                    'exception' => $e,
402
                ]);
403
            }
404
        }
405
406
        return $count;
407
    }
408
409
    /**
410
     * Find nodes with custom filter recursive.
411
     */
412
    public function findNodesByFilterRecursiveToArray(Collection $collection, array $filter = []): array
413
    {
414
        $graph = [
415
            'from' => 'storage',
416
            'startWith' => '$pointer',
417
            'connectFromField' => 'pointer',
418
            'connectToField' => 'parent',
419
            'as' => 'children',
420
        ];
421
422
        if (count($filter) > 0) {
423
            $graph['restrictSearchWithMatch'] = $filter;
424
        }
425
426
        $query = [
427
            ['$match' => ['_id' => $collection->getId()]],
428
            ['$graphLookup' => $graph],
429
            ['$unwind' => '$children'],
430
            ['$project' => ['id' => '$children._id']],
431
        ];
432
433
        $result = $this->db->storage->aggregate($query);
434
435
        return array_column(iterator_to_array($result), 'id');
436
    }
437
438
    /**
439
     * Get deleted nodes.
440
     *
441
     * Note this query excludes deleted nodes which have a deleted parent
442
     */
443
    public function getTrash(array $query = [], ?int $offset = null, ?int $limit = null): Generator
444
    {
445
        $shares = $this->user->getShares();
446
        $parent_filter = ['$and' => [
447
            ['deleted' => ['$ne' => false]],
448
            ['$or' => [
449
                ['owner' => $this->user->getId()],
450
                ['shared' => ['$in' => $shares]],
451
            ]],
452
        ]];
453
454
        if (count($query) > 0) {
455
            $parent_filter = [
456
                '$and' => [$parent_filter, $query],
457
            ];
458
        }
459
460
        $query = [
461
            ['$match' => $parent_filter],
462
            ['$graphLookup' => [
463
                'from' => 'storage',
464
                'startWith' => '$parent',
465
                'connectFromField' => 'parent',
466
                'connectToField' => 'pointer',
467
                'as' => 'parents',
468
                'maxDepth' => 0,
469
                'restrictSearchWithMatch' => [
470
                    '$or' => [
471
                        [
472
                            'shared' => true,
473
                            'owner' => $this->user->getId(),
474
                        ],
475
                        [
476
                            'shared' => ['$ne' => true],
477
                        ],
478
                    ],
479
                ],
480
            ]], [
481
                '$addFields' => [
482
                    'parents' => [
483
                        '$arrayElemAt' => ['$parents', 0],
484
                    ],
485
                ],
486
            ], [
487
                '$match' => [
488
                    '$or' => [
489
                        ['parents' => null],
490
                        ['parents.deleted' => false],
491
                    ],
492
                ],
493
            ], ['$graphLookup' => [
494
                'from' => 'storage',
495
                'startWith' => '$pointer',
496
                'connectFromField' => 'pointer',
497
                'connectToField' => 'parent',
498
                'as' => 'children',
499
                'maxDepth' => 0,
500
                'restrictSearchWithMatch' => $this->prepareChildrenFilter(NodeInterface::DELETED_ONLY),
501
            ]],
502
            ['$addFields' => [
503
                'size' => [
504
                    '$cond' => [
505
                        'if' => ['$eq' => ['$directory', true]],
506
                        'then' => ['$size' => '$children'],
507
                        'else' => '$size',
508
                    ],
509
                ],
510
            ]],
511
            ['$project' => ['children' => 0, 'parents' => 0]],
512
            ['$group' => ['_id' => null, 'total' => ['$sum' => 1]]],
513
        ];
514
515
        return $this->executeAggregation($query, $offset, $limit);
516
    }
517
518
    /**
519
     * Find nodes with custom filter recursive.
520
     */
521
    public function findNodesByFilterRecursiveChildren(array $parent_filter, int $deleted, ?int $offset = null, ?int $limit = null, ?Collection $parent = null): Generator
522
    {
523
        $query = [
524
            ['$match' => $parent_filter],
525
            ['$graphLookup' => [
526
                'from' => 'storage',
527
                'startWith' => '$pointer',
528
                'connectFromField' => 'pointer',
529
                'connectToField' => 'parent',
530
                'as' => 'children',
531
                'maxDepth' => 0,
532
                'restrictSearchWithMatch' => $this->prepareChildrenFilter($deleted),
533
            ]],
534
            ['$addFields' => [
535
                'size' => [
536
                    '$cond' => [
537
                        'if' => ['$eq' => ['$directory', true]],
538
                        'then' => ['$size' => '$children'],
539
                        'else' => '$size',
540
                    ],
541
                ],
542
            ]],
543
            ['$project' => ['children' => 0]],
544
            ['$group' => ['_id' => null, 'total' => ['$sum' => 1]]],
545
        ];
546
547
        return $this->executeAggregation($query, $offset, $limit, $parent);
548
    }
549
550
    /**
551
     * Find nodes with custom filter recursive.
552
     */
553
    public function findNodesByFilterRecursive(Collection $collection, array $filter = [], ?int $offset = null, ?int $limit = null): Generator
554
    {
555
        $graph = [
556
            'from' => 'storage',
557
            'startWith' => '$pointer',
558
            'connectFromField' => 'pointer',
559
            'connectToField' => 'parent',
560
            'as' => 'children',
561
        ];
562
563
        if (count($filter) > 0) {
564
            $graph['restrictSearchWithMatch'] = $filter;
565
        }
566
567
        $query = [
568
            ['$match' => ['_id' => $collection->getId()]],
569
            ['$graphLookup' => $graph],
570
            ['$unwind' => '$children'],
571
            ['$group' => ['_id' => null, 'total' => ['$sum' => 1]]],
572
        ];
573
574
        return $this->executeAggregation($query, $offset, $limit, $collection);
575
    }
576
577
    /**
578
     * Get custom filtered children.
579
     *
580
     * @deprecated
581
     */
582
    public function findNodesByFilterUser(int $deleted, array $filter, ?int $offset = null, ?int $limit = null): Generator
583
    {
584
        $shares = $this->user->getShares();
585
        $stored_filter = ['$and' => [
586
            [],
587
            ['$or' => [
588
                ['owner' => $this->user->getId()],
589
                ['shared' => ['$in' => $shares]],
590
            ]],
591
        ]];
592
593
        if (NodeInterface::DELETED_EXCLUDE === $deleted) {
594
            $stored_filter['$and'][0]['deleted'] = false;
595
        } elseif (NodeInterface::DELETED_ONLY === $deleted) {
596
            $stored_filter['$and'][0]['deleted'] = ['$type' => 9];
597
        }
598
599
        $stored_filter['$and'][0] = array_merge($filter, $stored_filter['$and'][0]);
600
601
        return $this->findNodesByFilterRecursiveChildren($stored_filter, $deleted, $offset, $limit);
602
    }
603
604
    /**
605
     * Init node.
606
     */
607
    public function initNode(array $node, ?Collection $parent = null): NodeInterface
608
    {
609
        $id = $node['_id'];
0 ignored issues
show
Unused Code introduced by
$id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
610
611
        if (isset($node['shared']) && true === $node['shared'] && null !== $this->user && $node['owner'] != $this->user->getId()) {
612
            $node = $this->findReferenceNode($node);
613
        }
614
615
        if (isset($node['parent'])) {
616
            if ($parent === null || $parent->getId() != $node['parent']) {
617
                $parent = $this->findNodeById($node['parent']);
618
            }
619
        } elseif ($node['_id'] !== null) {
620
            $parent = $this->getRoot();
621
        }
622
623
        if (!array_key_exists('directory', $node)) {
624
            throw new Exception('invalid node ['.$node['_id'].'] found, directory attribute does not exists');
625
        }
626
627
        $instance = $this->node_factory->build($this, $node, $parent);
628
        $loaded = isset($this->cache[(string) $node['_id']]);
629
630
        if ($loaded === false) {
631
            $this->cache[(string) $node['_id']] = $instance;
632
        }
633
634
        if (!$this->acl->isAllowed($instance, 'r')) {
635
            if ($instance->isReference()) {
636
                $instance->delete(true);
637
            }
638
639
            throw new ForbiddenException(
640
                'not allowed to access node',
641
                ForbiddenException::NOT_ALLOWED_TO_ACCESS
642
            );
643
        }
644
645
        if ($loaded === false && isset($node['destroy']) && $node['destroy'] instanceof UTCDateTime && $node['destroy']->toDateTime()->format('U') <= time()) {
0 ignored issues
show
Bug introduced by
The class MongoDB\BSON\UTCDateTime does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
646
            $this->logger->info('node ['.$node['_id'].'] is not accessible anmyore, destroy node cause of expired destroy flag', [
647
                'category' => get_class($this),
648
            ]);
649
650
            $instance->delete(true);
651
652
            throw new Exception\Conflict('node is not available anymore');
653
        }
654
655
        if (PHP_SAPI === 'cli') {
656
            unset($this->cache[(string) $node['_id']]);
657
        }
658
659
        return $instance;
660
    }
661
662
    /**
663
     * Find node with path.
664
     */
665
    public function findNodeByPath(string $path = '', ?string $class = null): NodeInterface
666
    {
667
        if (empty($path) || '/' !== $path[0]) {
668
            $path = '/'.$path;
669
        }
670
        $last = strlen($path) - 1;
671
        if ('/' === $path[$last]) {
672
            $path = substr($path, 0, -1);
673
        }
674
        $parts = explode('/', $path);
675
        $parent = $this->getRoot();
676
        array_shift($parts);
677
        $count = count($parts);
678
        $i = 0;
679
        $filter = [];
680
        foreach ($parts as $node) {
681
            ++$i;
682
            if ($count === $i && $class !== null) {
683
                $filter = [
684
                    'directory' => ($class === Collection::class),
685
                ];
686
            }
687
688
            try {
689
                $parent = $parent->getChild($node, NodeInterface::DELETED_EXCLUDE, $filter);
690
            } catch (Exception\NotFound $e) {
691
                if ($count == $i) {
692
                    $parent = $parent->getChild($node, NodeInterface::DELETED_INCLUDE, $filter);
693
                } else {
694
                    throw $e;
695
                }
696
            }
697
        }
698
        if (null !== $class && !($parent instanceof $class)) {
699
            throw new Exception('node is not instance of '.$class);
700
        }
701
702
        return $parent;
703
    }
704
705
    /**
706
     * Prepare children filter.
707
     */
708
    protected function prepareChildrenFilter(int $deleted)
709
    {
710
        $deleted_filter = [];
711
        if (NodeInterface::DELETED_EXCLUDE === $deleted) {
712
            $deleted_filter['deleted'] = false;
713
        } elseif (NodeInterface::DELETED_ONLY === $deleted) {
714
            $deleted_filter['deleted'] = ['$type' => 9];
715
        }
716
717
        $query = ['_id' => ['$exists' => true]];
718
719
        if ($this->user !== null) {
720
            $query = [
721
                '$or' => [
722
                    [
723
                        'owner' => $this->user->getId(),
724
                    ],
725
                    [
726
                        'acl' => ['$exists' => false],
727
                    ],
728
                    [
729
                        'acl.id' => (string) $this->user->getId(),
730
                        'type' => 'user',
731
                    ],
732
                    [
733
                        'acl.id' => ['$in' => array_map('strval', $this->user->getGroups())],
734
                        'type' => 'group',
735
                    ],
736
                ],
737
            ];
738
        }
739
740
        if (count($deleted_filter) > 0) {
741
            $query = ['$and' => [$deleted_filter, $query]];
742
        }
743
744
        return $query;
745
    }
746
747
    /**
748
     * Execute complex aggregation.
749
     */
750
    protected function executeAggregation(array $query, ?int $offset = null, ?int $limit = null, ?Collection $parent = null): Generator
751
    {
752
        $result = $this->db->storage->aggregate($query);
753
754
        $total = 0;
755
        $result = iterator_to_array($result);
756
        if (count($result) > 0) {
757
            $total = $result[0]['total'];
758
        }
759
760
        array_pop($query);
761
762
        $offset !== null ? $query[] = ['$skip' => $offset] : false;
763
        $limit !== null ? $query[] = ['$limit' => $limit] : false;
764
        $result = $this->db->storage->aggregate($query);
765
766
        foreach ($result as $node) {
767
            try {
768
                yield $this->initNode($node, $parent);
769
            } catch (\Exception $e) {
770
                $this->logger->error('remove node from result list, failed load node', [
771
                    'category' => get_class($this),
772
                    'exception' => $e,
773
                ]);
774
            }
775
        }
776
777
        return $total;
778
    }
779
780
    /**
781
     * Resolve shared node to reference or share depending who requested.
782
     */
783
    protected function findReferenceNode(array $node): array
784
    {
785
        if (isset($node['reference']) && ($node['reference'] instanceof ObjectId)) {
0 ignored issues
show
Bug introduced by
The class MongoDB\BSON\ObjectId does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
786
            $this->logger->debug('reference node ['.$node['_id'].'] requested from share owner, trying to find the shared node', [
787
                'category' => get_class($this),
788
            ]);
789
790
            $result = $this->db->storage->findOne([
791
                'owner' => $this->user->getId(),
792
                'shared' => true,
793
                '_id' => $node['reference'],
794
            ]);
795
796
            if (null === $result) {
797
                throw new Exception\NotFound(
798
                    'no share node for reference node '.$node['reference'].' found',
799
                    Exception\NotFound::SHARE_NOT_FOUND
800
                );
801
            }
802
        } else {
803
            $this->logger->debug('share node ['.$node['_id'].'] requested from member, trying to find the reference node', [
804
                'category' => get_class($this),
805
            ]);
806
807
            $result = $this->db->storage->findOne([
808
                'owner' => $this->user->getId(),
809
                'shared' => true,
810
                'reference' => $node['_id'],
811
            ]);
812
813
            if (null === $result) {
814
                throw new Exception\NotFound(
815
                    'no share reference for node '.$node['_id'].' found',
816
                    Exception\NotFound::REFERENCE_NOT_FOUND
817
                );
818
            }
819
        }
820
821
        return $result;
822
    }
823
}
824