Completed
Push — master ( 2b3f4c...5ada1f )
by Raffael
77:32 queued 61:21
created

Filesystem::findReferenceNode()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 34
ccs 0
cts 17
cp 0
rs 9.0648
c 0
b 0
f 0
cc 5
nc 4
nop 1
crap 30
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('node '.$id.' not found', Exception\NotFound::NODE_NOT_FOUND);
185
        }
186
187
        $this->raw_cache[(string) $id] = $node;
188
189
        return $node;
190
    }
191
192
    /**
193
     * Factory loader.
194
     */
195
    public function findNodeById($id, ?string $class = null, int $deleted = NodeInterface::DELETED_INCLUDE): NodeInterface
196
    {
197
        if (isset($this->cache[(string) $id])) {
198
            return $this->cache[(string) $id];
199
        }
200
201
        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...
202
            throw new Exception\InvalidArgument($id.' node id has to be a string or instance of \MongoDB\BSON\ObjectId');
203
        }
204
205
        try {
206
            if (is_string($id)) {
207
                $id = new ObjectId($id);
208
            }
209
        } catch (\Exception $e) {
210
            throw new Exception\InvalidArgument('invalid node id specified');
211
        }
212
213
        $filter = [
214
            '_id' => $id,
215
        ];
216
217
        if (NodeInterface::DELETED_EXCLUDE === $deleted) {
218
            $filter['deleted'] = false;
219
        } elseif (NodeInterface::DELETED_ONLY === $deleted) {
220
            $filter['deleted'] = ['$type' => 9];
221
        }
222
223
        $result = iterator_to_array($this->findNodesByFilterRecursiveChildren($filter, $deleted, 0, 1));
224
225
        if (count($result) === 0) {
226
            throw new Exception\NotFound('node ['.$id.'] not found', Exception\NotFound::NODE_NOT_FOUND);
227
        }
228
229
        $node = array_shift($result);
230
        if (null !== $class && !($node instanceof $class)) {
231
            throw new Exception('node '.get_class($node).' is not instance of '.$class);
232
        }
233
234
        return $node;
235
    }
236
237
    /**
238
     * Find one.
239
     */
240
    public function findOne(array $filter, int $deleted = NodeInterface::DELETED_INCLUDE, ?Collection $parent = null): NodeInterface
241
    {
242
        $result = iterator_to_array($this->findNodesByFilterRecursiveChildren($filter, $deleted, 0, 1, $parent));
243
244
        if (count($result) === 0) {
245
            throw new Exception\NotFound('requested node not found', Exception\NotFound::NODE_NOT_FOUND);
246
        }
247
248
        return array_shift($result);
249
    }
250
251
    /**
252
     * Load nodes by id.
253
     *
254
     * @deprecated
255
     */
256
    public function findNodesById(array $id = [], ?string $class = null, int $deleted = NodeInterface::DELETED_INCLUDE): Generator
257
    {
258
        $find = [];
259
        foreach ($id as $i) {
260
            $find[] = new ObjectId($i);
261
        }
262
263
        $filter = [
264
            '_id' => ['$in' => $find],
265
        ];
266
267
        switch ($deleted) {
268
            case NodeInterface::DELETED_INCLUDE:
269
                break;
270
            case NodeInterface::DELETED_EXCLUDE:
271
                $filter['deleted'] = false;
272
273
                break;
274
            case NodeInterface::DELETED_ONLY:
275
                $filter['deleted'] = ['$type' => 9];
276
277
                break;
278
        }
279
280
        $result = $this->db->storage->find($filter);
281
282
        $nodes = [];
283
        foreach ($result as $node) {
284
            try {
285
                $return = $this->initNode($node);
286
287
                if (in_array($return->getId(), $nodes)) {
288
                    continue;
289
                }
290
291
                $nodes[] = $return->getId();
292
            } catch (\Exception $e) {
293
                $this->logger->error('remove node from result list, failed load node', [
294
                    'category' => get_class($this),
295
                    'exception' => $e,
296
                ]);
297
298
                continue;
299
            }
300
301
            if (null !== $class && !($return instanceof $class)) {
302
                throw new Exception('node is not an instance of '.$class);
303
            }
304
305
            yield $return;
306
        }
307
    }
308
309
    /**
310
     * Load nodes by id.
311
     *
312
     * @param null|mixed $class
313
     *
314
     * @deprecated
315
     */
316
    public function getNodes(?array $id = null, $class = null, int $deleted = NodeInterface::DELETED_EXCLUDE): Generator
317
    {
318
        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...
319
    }
320
321
    /**
322
     * Load node.
323
     *
324
     * @param null|mixed $id
325
     * @param null|mixed $class
326
     *
327
     * @deprecated
328
     */
329
    public function getNode($id = null, $class = null, bool $multiple = false, bool $allow_root = false, ?int $deleted = null): NodeInterface
330
    {
331
        if (empty($id)) {
332
            if (true === $allow_root) {
333
                return $this->getRoot();
334
            }
335
336
            throw new Exception\InvalidArgument('invalid id given');
337
        }
338
339
        if (null === $deleted) {
340
            $deleted = NodeInterface::DELETED_INCLUDE;
341
        }
342
343
        if (true === $multiple && is_array($id)) {
344
            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...
345
        }
346
347
        return $this->findNodeById($id, $class, $deleted);
348
    }
349
350
    /**
351
     * Find node with custom filter.
352
     */
353
    public function findNodeByFilter(array $filter): NodeInterface
354
    {
355
        $result = $this->db->storage->findOne($filter);
356
        if (null === $result) {
357
            throw new Exception\NotFound('node with custom filter was not found', Exception\NotFound::NODE_NOT_FOUND);
358
        }
359
360
        return $this->initNode($result);
361
    }
362
363
    /**
364
     * Count.
365
     */
366
    public function countNodes(array $filter = []): int
367
    {
368
        return $this->db->storage->count($filter);
369
    }
370
371
    /**
372
     * Find nodes with custom filters.
373
     */
374
    public function findNodesByFilter(array $filter, ?int $offset = null, ?int $limit = null): Generator
375
    {
376
        $result = $this->db->storage->find($filter, [
377
            'skip' => $offset,
378
            'limit' => $limit,
379
        ]);
380
381
        $count = $this->countNodes($filter);
382
383
        foreach ($result as $node) {
384
            try {
385
                yield $this->initNode($node);
386
            } catch (\Exception $e) {
387
                $this->logger->error('remove node from result list, failed load node', [
388
                    'category' => get_class($this),
389
                    'exception' => $e,
390
                ]);
391
            }
392
        }
393
394
        return $count;
395
    }
396
397
    /**
398
     * Find nodes with custom filter recursive.
399
     */
400
    public function findNodesByFilterRecursiveToArray(Collection $collection, array $filter = []): array
401
    {
402
        $graph = [
403
            'from' => 'storage',
404
            'startWith' => '$pointer',
405
            'connectFromField' => 'pointer',
406
            'connectToField' => 'parent',
407
            'as' => 'children',
408
        ];
409
410
        if (count($filter) > 0) {
411
            $graph['restrictSearchWithMatch'] = $filter;
412
        }
413
414
        $query = [
415
            ['$match' => ['_id' => $collection->getId()]],
416
            ['$graphLookup' => $graph],
417
            ['$unwind' => '$children'],
418
            ['$project' => ['id' => '$children._id']],
419
        ];
420
421
        $result = $this->db->storage->aggregate($query);
422
423
        return array_column(iterator_to_array($result), 'id');
424
    }
425
426
    /**
427
     * Get deleted nodes.
428
     *
429
     * Note this query excludes deleted nodes which have a deleted parent
430
     */
431
    public function getTrash(array $query = [], ?int $offset = null, ?int $limit = null): Generator
432
    {
433
        $shares = $this->user->getShares();
434
        $parent_filter = ['$and' => [
435
            ['deleted' => ['$ne' => false]],
436
            ['$or' => [
437
                ['owner' => $this->user->getId()],
438
                ['shared' => ['$in' => $shares]],
439
            ]],
440
        ]];
441
442
        if (count($query) > 0) {
443
            $parent_filter = [
444
                '$and' => [$parent_filter, $query],
445
            ];
446
        }
447
448
        $query = [
449
            ['$match' => $parent_filter],
450
            ['$graphLookup' => [
451
                'from' => 'storage',
452
                'startWith' => '$parent',
453
                'connectFromField' => 'parent',
454
                'connectToField' => 'pointer',
455
                'as' => 'parents',
456
                'maxDepth' => 0,
457
                'restrictSearchWithMatch' => [
458
                    '$or' => [
459
                        [
460
                            'shared' => true,
461
                            'owner' => $this->user->getId(),
462
                        ],
463
                        [
464
                            'shared' => ['$ne' => true],
465
                        ],
466
                    ],
467
                ],
468
            ]], [
469
                '$addFields' => [
470
                    'parents' => [
471
                        '$arrayElemAt' => ['$parents', 0],
472
                    ],
473
                ],
474
            ], [
475
                '$match' => [
476
                    '$or' => [
477
                        ['parents' => null],
478
                        ['parents.deleted' => false],
479
                    ],
480
                ],
481
            ], ['$graphLookup' => [
482
                'from' => 'storage',
483
                'startWith' => '$pointer',
484
                'connectFromField' => 'pointer',
485
                'connectToField' => 'parent',
486
                'as' => 'children',
487
                'maxDepth' => 0,
488
                'restrictSearchWithMatch' => $this->prepareChildrenFilter(NodeInterface::DELETED_ONLY),
489
            ]],
490
            ['$addFields' => [
491
                'size' => [
492
                    '$cond' => [
493
                        'if' => ['$eq' => ['$directory', true]],
494
                        'then' => ['$size' => '$children'],
495
                        'else' => '$size',
496
                    ],
497
                ],
498
            ]],
499
            ['$project' => ['children' => 0, 'parents' => 0]],
500
            ['$group' => ['_id' => null, 'total' => ['$sum' => 1]]],
501
        ];
502
503
        return $this->executeAggregation($query, $offset, $limit);
504
    }
505
506
    /**
507
     * Find nodes with custom filter recursive.
508
     */
509
    public function findNodesByFilterRecursiveChildren(array $parent_filter, int $deleted, ?int $offset = null, ?int $limit = null, ?Collection $parent = null): Generator
510
    {
511
        $query = [
512
            ['$match' => $parent_filter],
513
            ['$graphLookup' => [
514
                'from' => 'storage',
515
                'startWith' => '$pointer',
516
                'connectFromField' => 'pointer',
517
                'connectToField' => 'parent',
518
                'as' => 'children',
519
                'maxDepth' => 0,
520
                'restrictSearchWithMatch' => $this->prepareChildrenFilter($deleted),
521
            ]],
522
            ['$addFields' => [
523
                'size' => [
524
                    '$cond' => [
525
                        'if' => ['$eq' => ['$directory', true]],
526
                        'then' => ['$size' => '$children'],
527
                        'else' => '$size',
528
                    ],
529
                ],
530
            ]],
531
            ['$project' => ['children' => 0]],
532
            ['$group' => ['_id' => null, 'total' => ['$sum' => 1]]],
533
        ];
534
535
        return $this->executeAggregation($query, $offset, $limit, $parent);
536
    }
537
538
    /**
539
     * Find nodes with custom filter recursive.
540
     */
541
    public function findNodesByFilterRecursive(Collection $collection, array $filter = [], ?int $offset = null, ?int $limit = null): Generator
542
    {
543
        $graph = [
544
            'from' => 'storage',
545
            'startWith' => '$pointer',
546
            'connectFromField' => 'pointer',
547
            'connectToField' => 'parent',
548
            'as' => 'children',
549
        ];
550
551
        if (count($filter) > 0) {
552
            $graph['restrictSearchWithMatch'] = $filter;
553
        }
554
555
        $query = [
556
            ['$match' => ['_id' => $collection->getId()]],
557
            ['$graphLookup' => $graph],
558
            ['$unwind' => '$children'],
559
            ['$group' => ['_id' => null, 'total' => ['$sum' => 1]]],
560
        ];
561
562
        return $this->executeAggregation($query, $offset, $limit, $collection);
563
    }
564
565
    /**
566
     * Get custom filtered children.
567
     *
568
     * @deprecated
569
     */
570
    public function findNodesByFilterUser(int $deleted, array $filter, ?int $offset = null, ?int $limit = null): Generator
571
    {
572
        $shares = $this->user->getShares();
573
        $stored_filter = ['$and' => [
574
            [],
575
            ['$or' => [
576
                ['owner' => $this->user->getId()],
577
                ['shared' => ['$in' => $shares]],
578
            ]],
579
        ]];
580
581
        if (NodeInterface::DELETED_EXCLUDE === $deleted) {
582
            $stored_filter['$and'][0]['deleted'] = false;
583
        } elseif (NodeInterface::DELETED_ONLY === $deleted) {
584
            $stored_filter['$and'][0]['deleted'] = ['$type' => 9];
585
        }
586
587
        $stored_filter['$and'][0] = array_merge($filter, $stored_filter['$and'][0]);
588
589
        return $this->findNodesByFilterRecursiveChildren($stored_filter, $deleted, $offset, $limit);
590
    }
591
592
    /**
593
     * Init node.
594
     */
595
    public function initNode(array $node, ?Collection $parent = null): NodeInterface
596
    {
597
        $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...
598
599
        if (isset($node['shared']) && true === $node['shared'] && null !== $this->user && $node['owner'] != $this->user->getId()) {
600
            $node = $this->findReferenceNode($node);
601
        }
602
603
        if (isset($node['parent'])) {
604
            if ($parent === null || $parent->getId() != $node['parent']) {
605
                $parent = $this->findNodeById($node['parent']);
606
            }
607
        } elseif ($node['_id'] !== null) {
608
            $parent = $this->getRoot();
609
        }
610
611
        if (!array_key_exists('directory', $node)) {
612
            throw new Exception('invalid node ['.$node['_id'].'] found, directory attribute does not exists');
613
        }
614
615
        $instance = $this->node_factory->build($this, $node, $parent);
616
        $loaded = isset($this->cache[(string) $node['_id']]);
617
618
        if ($loaded === false) {
619
            $this->cache[(string) $node['_id']] = $instance;
620
        }
621
622
        if (!$this->acl->isAllowed($instance, 'r')) {
623
            if ($instance->isReference()) {
624
                $instance->delete(true);
625
            }
626
627
            throw new ForbiddenException('not allowed to access node', ForbiddenException::NOT_ALLOWED_TO_ACCESS);
628
        }
629
630
        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...
631
            $this->logger->info('node ['.$node['_id'].'] is not accessible anmyore, destroy node cause of expired destroy flag', [
632
                'category' => get_class($this),
633
            ]);
634
635
            $instance->delete(true);
636
637
            throw new Exception\Conflict('node is not available anymore');
638
        }
639
640
        if (PHP_SAPI === 'cli') {
641
            unset($this->cache[(string) $node['_id']]);
642
        }
643
644
        return $instance;
645
    }
646
647
    /**
648
     * Find node with path.
649
     */
650
    public function findNodeByPath(string $path = '', ?string $class = null): NodeInterface
651
    {
652
        if (empty($path) || '/' !== $path[0]) {
653
            $path = '/'.$path;
654
        }
655
        $last = strlen($path) - 1;
656
        if ('/' === $path[$last]) {
657
            $path = substr($path, 0, -1);
658
        }
659
        $parts = explode('/', $path);
660
        $parent = $this->getRoot();
661
        array_shift($parts);
662
        $count = count($parts);
663
        $i = 0;
664
        $filter = [];
665
        foreach ($parts as $node) {
666
            ++$i;
667
            if ($count === $i && $class !== null) {
668
                $filter = [
669
                    'directory' => ($class === Collection::class),
670
                ];
671
            }
672
673
            try {
674
                $parent = $parent->getChild($node, NodeInterface::DELETED_EXCLUDE, $filter);
675
            } catch (Exception\NotFound $e) {
676
                if ($count == $i) {
677
                    $parent = $parent->getChild($node, NodeInterface::DELETED_INCLUDE, $filter);
678
                } else {
679
                    throw $e;
680
                }
681
            }
682
        }
683
        if (null !== $class && !($parent instanceof $class)) {
684
            throw new Exception('node is not instance of '.$class);
685
        }
686
687
        return $parent;
688
    }
689
690
    /**
691
     * Prepare children filter.
692
     */
693
    protected function prepareChildrenFilter(int $deleted)
694
    {
695
        $deleted_filter = [];
696
        if (NodeInterface::DELETED_EXCLUDE === $deleted) {
697
            $deleted_filter['deleted'] = false;
698
        } elseif (NodeInterface::DELETED_ONLY === $deleted) {
699
            $deleted_filter['deleted'] = ['$type' => 9];
700
        }
701
702
        $query = ['_id' => ['$exists' => true]];
703
704
        if ($this->user !== null) {
705
            $query = [
706
                '$or' => [
707
                    [
708
                        'owner' => $this->user->getId(),
709
                    ],
710
                    [
711
                        'acl' => ['$exists' => false],
712
                    ],
713
                    [
714
                        'acl.id' => (string) $this->user->getId(),
715
                        'type' => 'user',
716
                    ],
717
                    [
718
                        'acl.id' => ['$in' => array_map('strval', $this->user->getGroups())],
719
                        'type' => 'group',
720
                    ],
721
                ],
722
            ];
723
        }
724
725
        if (count($deleted_filter) > 0) {
726
            $query = ['$and' => [$deleted_filter, $query]];
727
        }
728
729
        return $query;
730
    }
731
732
    /**
733
     * Execute complex aggregation.
734
     */
735
    protected function executeAggregation(array $query, ?int $offset = null, ?int $limit = null, ?Collection $parent = null): Generator
736
    {
737
        $result = $this->db->storage->aggregate($query);
738
739
        $total = 0;
740
        $result = iterator_to_array($result);
741
        if (count($result) > 0) {
742
            $total = $result[0]['total'];
743
        }
744
745
        array_pop($query);
746
747
        $offset !== null ? $query[] = ['$skip' => $offset] : false;
748
        $limit !== null ? $query[] = ['$limit' => $limit] : false;
749
        $result = $this->db->storage->aggregate($query);
750
751
        foreach ($result as $node) {
752
            try {
753
                yield $this->initNode($node, $parent);
754
            } catch (\Exception $e) {
755
                $this->logger->error('remove node from result list, failed load node', [
756
                    'category' => get_class($this),
757
                    'exception' => $e,
758
                ]);
759
            }
760
        }
761
762
        return $total;
763
    }
764
765
    /**
766
     * Resolve shared node to reference or share depending who requested.
767
     */
768
    protected function findReferenceNode(array $node): array
769
    {
770
        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...
771
            $this->logger->debug('reference node ['.$node['_id'].'] requested from share owner, trying to find the shared node', [
772
                'category' => get_class($this),
773
            ]);
774
775
            $result = $this->db->storage->findOne([
776
                'owner' => $this->user->getId(),
777
                'shared' => true,
778
                '_id' => $node['reference'],
779
            ]);
780
781
            if (null === $result) {
782
                throw new Exception\NotFound('no share node for reference node '.$node['reference'].' found', Exception\NotFound::SHARE_NOT_FOUND);
783
            }
784
        } else {
785
            $this->logger->debug('share node ['.$node['_id'].'] requested from member, trying to find the reference node', [
786
                'category' => get_class($this),
787
            ]);
788
789
            $result = $this->db->storage->findOne([
790
                'owner' => $this->user->getId(),
791
                'shared' => true,
792
                'reference' => $node['_id'],
793
            ]);
794
795
            if (null === $result) {
796
                throw new Exception\NotFound('no share reference for node '.$node['_id'].' found', Exception\NotFound::REFERENCE_NOT_FOUND);
797
            }
798
        }
799
800
        return $result;
801
    }
802
}
803