Passed
Push — master ( b9b259...1c5ea6 )
by SignpostMarv
05:47
created

MaybeGetLeafOrThrow()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 1
dl 0
loc 14
ccs 7
cts 7
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
* Base daft objects.
4
*
5
* @author SignpostMarv
6
*/
7
declare(strict_types=1);
8
9
namespace SignpostMarv\DaftObject;
10
11
use BadMethodCallException;
12
use InvalidArgumentException;
13
use RuntimeException;
14
15
/**
16
* @template T as DaftNestedWriteableObject
17
*
18
* @template-extends DaftObjectMemoryTree<T>
19
*
20
* @template-implements DaftNestedWriteableObjectTree<T>
21
*/
22
abstract class DaftWriteableObjectMemoryTree extends DaftObjectMemoryTree implements DaftNestedWriteableObjectTree
23
{
24
    const DEFINITELY_BELOW = false;
25
26
    const EXCLUDE_ROOT = false;
27
28
    const INSERT_AFTER = false;
29
30
    const LIMIT_ONE = 1;
31
32
    const RELATIVE_DEPTH_SAME = 0;
33
34
    const INT_ARG_INDEX_SECOND = 2;
35
36
    /**
37
    * {@inheritdoc}
38
    *
39
    * @psalm-return T
40
    */
41 62
    public function RecallDaftNestedWriteableObjectOrThrow($id) : DaftNestedWriteableObject
42
    {
43
        /**
44
        * @var DaftNestedWriteableObject|null
45
        *
46
        * @psalm-var T|null
47
        */
48 62
        $out = $this->RecallDaftNestedObjectOrThrow($id);
49
50 60
        if (is_null($out)) {
51
            throw new DaftObjectNotRecalledException(
52
                'Argument 1 passed to ' .
53
                DaftNestedWriteableObjectTree::class .
54
                '::RecallDaftNestedWriteableObjectOrThrow() did not resolve to an instance of ' .
55
                DaftNestedWriteableObject::class .
56
                ' from ' .
57
                static::class .
58
                '::RecallDaftObject()'
59
            );
60
        }
61
62 60
        return $out;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $out returns the type SignpostMarv\DaftObject\DaftNestedObject which includes types incompatible with the type-hinted return SignpostMarv\DaftObject\DaftNestedWriteableObject.
Loading history...
63
    }
64
65
    /**
66
    * @psalm-param T $newLeaf
67
    * @psalm-param T $referenceLeaf
68
    *
69
    * @psalm-return T
70
    */
71 84
    public function ModifyDaftNestedObjectTreeInsert(
72
        DaftNestedWriteableObject $newLeaf,
73
        DaftNestedWriteableObject $referenceLeaf,
74
        bool $before = self::INSERT_AFTER,
75
        bool $above = null
76
    ) : DaftNestedWriteableObject {
77 84
        if ($newLeaf->GetId() === $referenceLeaf->GetId()) {
78 40
            throw new InvalidArgumentException('Cannot modify leaf relative to itself!');
79
        }
80
81 60
        if ((bool) $above) {
82 48
            $this->ModifyDaftNestedObjectTreeInsertAbove($newLeaf, $referenceLeaf);
83 32
        } elseif (self::DEFINITELY_BELOW === $above) {
84 6
            $this->ModifyDaftNestedObjectTreeInsertBelow($newLeaf, $referenceLeaf);
85
        } else {
86 28
            $this->ModifyDaftNestedObjectTreeInsertAdjacent($newLeaf, $referenceLeaf, $before);
87
        }
88
89 60
        return $this->RebuildAfterInsert($newLeaf);
90
    }
91
92
    /**
93
    * @param DaftNestedWriteableObject|scalar|(scalar|array|object|null)[] $leaf
94
    * @param DaftNestedWriteableObject|scalar|(scalar|array|object|null)[] $referenceId
95
    *
96
    * @psalm-param T|scalar|(scalar|array|object|null)[] $leaf
97
    * @psalm-param T|scalar|(scalar|array|object|null)[] $referenceId
98
    *
99
    * @psalm-return T
100
    */
101 48
    public function ModifyDaftNestedObjectTreeInsertLoose(
102
        $leaf,
103
        $referenceId,
104
        bool $before = self::INSERT_AFTER,
105
        bool $above = null
106
    ) : DaftNestedWriteableObject {
107
        /**
108
        * @var DaftNestedWriteableObject
109
        *
110
        * @psalm-var T
111
        */
112 48
        $leaf = $this->MaybeGetLeafOrThrow($leaf);
113
114 24
        $reference = $referenceId;
115
116 24
        if ( ! ($referenceId instanceof DaftNestedWriteableObject)) {
117
            /**
118
            * @var scalar|(scalar|array|object|null)[]
119
            */
120 24
            $referenceId = $referenceId;
121
122 24
            $reference = $this->RecallDaftObject($referenceId);
123
        }
124
125
        /**
126
        * @var DaftNestedWriteableObject|null
127
        *
128
        * @psalm-var T|null
129
        */
130 24
        $reference = $reference;
131
132 24
        $resp = $this->ModifyDaftNestedObjectTreeInsertMaybeLooseIntoTree(
133 24
            $this,
134 24
            $leaf,
135 24
            $reference,
136 24
            $referenceId === $this->GetNestedObjectTreeRootId(),
137 24
            $before,
138 24
            $above
139
        );
140
141 24
        if ($resp instanceof DaftNestedWriteableObject) {
142 24
            return $resp;
143
        }
144
145
        throw new InvalidArgumentException(sprintf(
146
            'Argument %u passed to %s() did not resolve to a leaf node!',
147
            self::INT_ARG_INDEX_SECOND,
148
            __METHOD__
149
        ));
150
    }
151
152
    /**
153
    * @psalm-param T $root
154
    * @psalm-param T|null $replacementRoot
155
    */
156 8
    public function ModifyDaftNestedObjectTreeRemoveWithObject(
157
        DaftNestedWriteableObject $root,
158
        ? DaftNestedWriteableObject $replacementRoot
159
    ) : int {
160
        if (
161 8
            $this->CountDaftNestedObjectTreeWithObject(
162 8
                $root,
163 8
                false,
164 8
                null
165 8
            ) > AbstractArrayBackedDaftNestedObject::COUNT_EXPECT_NON_EMPTY &&
166 8
            is_null($replacementRoot)
167
        ) {
168 2
            throw new BadMethodCallException('Cannot leave orphan objects in a tree');
169
        }
170
171 6
        $root = $this->StoreThenRetrieveFreshLeaf($root);
172
173 6
        if ( ! is_null($replacementRoot)) {
174 4
            $this->ModifyDaftNestedObjectTreeRemoveWithObjectPrepareRemovalAndRebuild(
175 4
                $root,
176 4
                $replacementRoot
177
            );
178
        }
179
180 6
        $this->RemoveDaftObject($root);
181
182 6
        $this->RebuildTreeInefficiently();
183
184 6
        return $this->CountDaftNestedObjectFullTree();
185
    }
186
187
    /**
188
    * @param scalar|(scalar|array|object|null)[] $root
189
    * @param scalar|(scalar|array|object|null)[]|null $replacementRoot
190
    */
191 12
    public function ModifyDaftNestedObjectTreeRemoveWithId($root, $replacementRoot) : int
192
    {
193 12
        $rootObject = $this->RecallDaftObject($root);
194
195 12
        $resp = null;
196
197 12
        if ($rootObject instanceof DaftNestedWriteableObject) {
198 12
            $resp = $this->ModifyDaftNestedObjectTreeRemoveWithIdUsingRootObject(
199 12
                $replacementRoot,
200 12
                $rootObject
201
            );
202
        }
203
204 8
        return is_int($resp) ? $resp : $this->CountDaftNestedObjectFullTree();
205
    }
206
207
    /**
208
    * @psalm-param T $leaf
209
    *
210
    * @psalm-return T
211
    */
212 62
    public function StoreThenRetrieveFreshLeaf(
213
        DaftNestedWriteableObject $leaf
214
    ) : DaftNestedWriteableObject {
215 62
        $this->RememberDaftObject($leaf);
216 62
        $this->ForgetDaftObject($leaf);
217 62
        $this->ForgetDaftObjectById($leaf->GetId());
218
219 62
        return $this->RecallDaftNestedWriteableObjectOrThrow($leaf->GetId());
220
    }
221
222
    /**
223
    * @psalm-param T $object
224
    */
225 88
    public function RememberDaftObject(SuitableForRepositoryType $object) : void
226
    {
227
        /**
228
        * @var DaftNestedWriteableObject
229
        *
230
        * @psalm-var T
231
        */
232 88
        $object = $object;
233
234 88
        $left = $object->GetIntNestedLeft();
0 ignored issues
show
Bug introduced by
The method GetIntNestedLeft() does not exist on SignpostMarv\DaftObject\SuitableForRepositoryType. It seems like you code against a sub-type of SignpostMarv\DaftObject\SuitableForRepositoryType such as SignpostMarv\DaftObject\DaftNestedObject. ( Ignorable by Annotation )

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

234
        /** @scrutinizer ignore-call */ 
235
        $left = $object->GetIntNestedLeft();
Loading history...
235 88
        $right = $object->GetIntNestedRight();
0 ignored issues
show
Bug introduced by
The method GetIntNestedRight() does not exist on SignpostMarv\DaftObject\SuitableForRepositoryType. It seems like you code against a sub-type of SignpostMarv\DaftObject\SuitableForRepositoryType such as SignpostMarv\DaftObject\DaftNestedObject. ( Ignorable by Annotation )

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

235
        /** @scrutinizer ignore-call */ 
236
        $right = $object->GetIntNestedRight();
Loading history...
236 88
        $level = $object->GetIntNestedLevel();
0 ignored issues
show
Bug introduced by
The method GetIntNestedLevel() does not exist on SignpostMarv\DaftObject\SuitableForRepositoryType. It seems like you code against a sub-type of SignpostMarv\DaftObject\SuitableForRepositoryType such as SignpostMarv\DaftObject\DaftNestedObject. ( Ignorable by Annotation )

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

236
        /** @scrutinizer ignore-call */ 
237
        $level = $object->GetIntNestedLevel();
Loading history...
237
238 88
        if (0 === $left && 0 === $right && 0 === $level) {
239 86
            $fullTreeCount = $this->CountDaftNestedObjectFullTree();
240
241 86
            if ($fullTreeCount > AbstractArrayBackedDaftNestedObject::COUNT_EXPECT_NON_EMPTY) {
242 60
                $tree = $this->RecallDaftNestedObjectFullTree();
243
244
                /**
245
                * @var DaftNestedWriteableObject
246
                *
247
                * @psalm-var T
248
                */
249 60
                $end = end($tree);
250
251 60
                $left = $end->GetIntNestedRight() + 1;
252
            } else {
253 86
                $left = $fullTreeCount + $fullTreeCount;
254
            }
255
256 86
            $object->SetIntNestedLeft($left);
0 ignored issues
show
Bug introduced by
The method SetIntNestedLeft() does not exist on SignpostMarv\DaftObject\SuitableForRepositoryType. It seems like you code against a sub-type of SignpostMarv\DaftObject\SuitableForRepositoryType such as SignpostMarv\DaftObject\DaftNestedWriteableObject or SignpostMarv\DaftObject\...yBackedDaftNestedObject. ( Ignorable by Annotation )

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

256
            $object->/** @scrutinizer ignore-call */ 
257
                     SetIntNestedLeft($left);
Loading history...
257 86
            $object->SetIntNestedRight($left + 1);
0 ignored issues
show
Bug introduced by
The method SetIntNestedRight() does not exist on SignpostMarv\DaftObject\SuitableForRepositoryType. It seems like you code against a sub-type of SignpostMarv\DaftObject\SuitableForRepositoryType such as SignpostMarv\DaftObject\DaftNestedWriteableObject or SignpostMarv\DaftObject\...yBackedDaftNestedObject. ( Ignorable by Annotation )

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

257
            $object->/** @scrutinizer ignore-call */ 
258
                     SetIntNestedRight($left + 1);
Loading history...
258
        }
259
260 88
        parent::RememberDaftObject($object);
261 88
    }
262
263
    /**
264
    * @psalm-param T $newLeaf
265
    * @psalm-param T $referenceLeaf
266
    */
267 52
    protected function ModifyDaftNestedObjectTreeInsertAdjacent(
268
        DaftNestedWriteableObject $newLeaf,
269
        DaftNestedWriteableObject $referenceLeaf,
270
        bool $before
271
    ) : void {
272
        /**
273
        * @var array<int, DaftNestedWriteableObject>
274
        *
275
        * @psalm-var array<int, T>
276
        */
277 52
        $siblings = $this->SiblingsExceptLeaf($newLeaf, $referenceLeaf);
278
279 52
        $siblingIds = [];
280 52
        $siblingSort = [];
281 52
        $j = count($siblings);
282
283 52
        foreach ($siblings as $leaf) {
284
            /**
285
            * @var scalar|(scalar|array|object|null)[]
286
            */
287 28
            $siblingId = $leaf->GetId();
288 28
            $siblingIds[] = $siblingId;
289 28
            $siblingSort[] = $leaf->GetIntNestedSortOrder();
290
        }
291
292 52
        $pos = array_search($referenceLeaf->GetId(), $siblingIds, true);
293
294 52
        if (false === $pos) {
295 24
            throw new RuntimeException('Reference leaf not found in siblings tree!');
296
        }
297
298 28
        for ($i = 0; $i < $j; ++$i) {
299 28
            $siblings[$i]->SetIntNestedSortOrder(
300 28
                $siblingSort[$i] +
301 28
                (($before ? ($i < $pos) : ($i <= $pos)) ? self::DECREMENT : self::INCREMENT)
302
            );
303 28
            $this->StoreThenRetrieveFreshLeaf($siblings[$i]);
304
        }
305
306 28
        $newLeaf->SetIntNestedSortOrder($siblingSort[$pos]);
307 28
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->ObtainDaftNestedObjectParentId());
308
309 28
        $this->StoreThenRetrieveFreshLeaf($newLeaf);
310 28
    }
311
312 60
    protected function RebuildTreeInefficiently() : void
313
    {
314 60
        $rebuilder = new InefficientDaftNestedRebuild($this);
315 60
        $rebuilder->RebuildTree();
316 60
    }
317
318
    /**
319
    * @psalm-param T $leaf
320
    * @psalm-param T|null $reference
321
    *
322
    * @psalm-return T|null
323
    */
324 24
    private function ModifyDaftNestedObjectTreeInsertMaybeLooseIntoTree(
325
        DaftNestedWriteableObjectTree $tree,
326
        DaftNestedWriteableObject $leaf,
327
        ? DaftObject $reference,
328
        bool $isRoot,
329
        bool $before,
330
        ? bool $above
331
    ) : ? DaftNestedWriteableObject {
332 24
        if (($reference instanceof DaftNestedWriteableObject) || $isRoot) {
333 24
            if ($reference instanceof DaftNestedWriteableObject) {
334 18
                return $tree->ModifyDaftNestedObjectTreeInsert($leaf, $reference, $before, $above);
335
            }
336
337 24
            return $this->ModifyDaftNestedObjectTreeInsertLooseIntoTree($leaf, $before, $above);
338
        }
339
340
        return null;
341
    }
342
343
    /**
344
    * @psalm-param T $newLeaf
345
    *
346
    * @psalm-return T
347
    */
348 60
    private function RebuildAfterInsert(
349
        DaftNestedWriteableObject $newLeaf
350
    ) : DaftNestedWriteableObject {
351 60
        $this->RebuildTreeInefficiently();
352
353 60
        return $this->RecallDaftNestedWriteableObjectOrThrow($newLeaf->GetId());
354
    }
355
356
    /**
357
    * @psalm-param T $root
358
    * @psalm-param T $replacementRoot
359
    */
360 4
    private function ModifyDaftNestedObjectTreeRemoveWithObjectPrepareRemovalAndRebuild(
361
        DaftNestedWriteableObject $root,
362
        DaftNestedWriteableObject $replacementRoot
363
    ) : void {
364
        /**
365
        * @var scalar|(scalar|array|object|null)[]
366
        */
367 4
        $replacementRootId = $this->StoreThenRetrieveFreshLeaf($replacementRoot)->GetId();
368
369 4
        $this->UpdateRoots($root, $replacementRootId);
370 4
    }
371
372
    /**
373
    * @param scalar|(scalar|array|object|null)[] $replacementRootId
374
    *
375
    * @psalm-param T $root
376
    */
377 10
    private function UpdateRoots(DaftNestedWriteableObject $root, $replacementRootId) : void
378
    {
379
        /**
380
        * @var array<int, DaftNestedObject>
381
        */
382 10
        $alterThese = $this->RecallDaftNestedObjectTreeWithObject($root, false, self::LIMIT_ONE);
383
384 10
        foreach ($alterThese as $alter) {
385 4
            if ($alter instanceof DaftNestedWriteableObject) {
386 4
                $alter->AlterDaftNestedObjectParentId($replacementRootId);
387 4
                $this->RememberDaftObject($alter);
388
            }
389
        }
390 10
    }
391
392
    /**
393
    * @param DaftNestedWriteableObject|scalar|(scalar|array|object|null)[] $leaf
394
    *
395
    * @psalm-param T|scalar|(scalar|array|object|null)[] $leaf
396
    *
397
    * @psalm-return T
398
    */
399 48
    private function MaybeGetLeafOrThrow($leaf) : DaftNestedWriteableObject
400
    {
401 48
        if ($leaf === $this->GetNestedObjectTreeRootId()) {
402 24
            throw new InvalidArgumentException('Cannot pass root id as new leaf');
403 24
        } elseif ($leaf instanceof DaftNestedWriteableObject) {
404 24
            return $this->StoreThenRetrieveFreshLeaf($leaf);
405
        }
406
407
        /**
408
        * @psalm-var scalar|(scalar|array|object|null)[]
409
        */
410 16
        $leaf = $leaf;
411
412 16
        return $this->RecallDaftNestedWriteableObjectOrThrow($leaf);
413
    }
414
415
    /**
416
    * @psalm-param T $leaf
417
    *
418
    * @psalm-return T
419
    */
420 24
    private function ModifyDaftNestedObjectTreeInsertLooseIntoTree(
421
        DaftNestedWriteableObject $leaf,
422
        bool $before,
423
        ? bool $above
424
    ) : DaftNestedWriteableObject {
425
        /**
426
        * @var array<int, DaftNestedWriteableObject>
427
        *
428
        * @psalm-var array<int, T>
429
        */
430 24
        $leaves = $this->RecallDaftNestedObjectFullTree(self::RELATIVE_DEPTH_SAME);
431 24
        $leaves = array_filter(
432 24
            $leaves,
433
            /**
434
            * @psalm-param T $e
435
            */
436
            function (DaftNestedWriteableObject $e) use ($leaf) : bool {
437 24
                return $e->GetId() !== $leaf->GetId();
438 24
            }
439
        );
440
441
        /**
442
        * @var false|DaftNestedWriteableObject
443
        *
444
        * @psalm-var false|T
445
        */
446 24
        $reference = $before ? current($leaves) : end($leaves);
447
448 24
        if ( ! ($reference instanceof DaftNestedWriteableObject)) {
449 24
            $leaf->SetIntNestedLeft(0);
450 24
            $leaf->SetIntNestedRight(1);
451 24
            $leaf->SetIntNestedLevel(0);
452 24
            $leaf->AlterDaftNestedObjectParentId($this->GetNestedObjectTreeRootId());
453
454 24
            return $this->StoreThenRetrieveFreshLeaf($leaf);
455
        }
456
457 24
        return $this->ModifyDaftNestedObjectTreeInsert($leaf, $reference, $before, $above);
458
    }
459
460
    /**
461
    * @psalm-param T $rootObject
462
    * @psalm-param T|null $replacementRootObject
463
    */
464 4
    private function MaybeRemoveWithPossibleObject(
465
        DaftNestedWriteableObject $rootObject,
466
        ? DaftObject $replacementRootObject
467
    ) : int {
468 4
        if ( ! ($replacementRootObject instanceof DaftNestedWriteableObject)) {
469 2
            throw new InvalidArgumentException(
470 2
                'Could not locate replacement root, cannot leave orphan objects!'
471
            );
472
        }
473
474 2
        return $this->ModifyDaftNestedObjectTreeRemoveWithObject(
475 2
            $rootObject,
476 2
            $replacementRootObject
477
        );
478
    }
479
480
    /**
481
    * @param scalar|(scalar|array|object|null)[] $replacementRoot
482
    *
483
    * @psalm-param T $rootObject
484
    */
485 6
    private function UpdateRemoveThenRebuild(
486
        DaftNestedWriteableObject $rootObject,
487
        $replacementRoot
488
    ) : void {
489 6
        $this->UpdateRoots($rootObject, $replacementRoot);
490
491 6
        $this->RemoveDaftObject($rootObject);
492
493 6
        $this->RebuildTreeInefficiently();
494 6
    }
495
496
    /**
497
    * @psalm-param T $newLeaf
498
    * @psalm-param T $referenceLeaf
499
    */
500 48
    private function ModifyDaftNestedObjectTreeInsertAbove(
501
        DaftNestedWriteableObject $newLeaf,
502
        DaftNestedWriteableObject $referenceLeaf
503
    ) : void {
504 48
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->ObtainDaftNestedObjectParentId());
505 48
        $referenceLeaf->AlterDaftNestedObjectParentId($newLeaf->GetId());
506
507 48
        $this->StoreThenRetrieveFreshLeaf($newLeaf);
508 48
        $this->StoreThenRetrieveFreshLeaf($referenceLeaf);
509 48
    }
510
511
    /**
512
    * @psalm-param T $newLeaf
513
    * @psalm-param T $referenceLeaf
514
    */
515 6
    private function ModifyDaftNestedObjectTreeInsertBelow(
516
        DaftNestedWriteableObject $newLeaf,
517
        DaftNestedWriteableObject $referenceLeaf
518
    ) : void {
519 6
        $newLeaf->AlterDaftNestedObjectParentId($referenceLeaf->GetId());
520 6
        $this->StoreThenRetrieveFreshLeaf($newLeaf);
521 6
    }
522
523
    /**
524
    * @psalm-param T $newLeaf
525
    * @psalm-param T $referenceLeaf
526
    *
527
    * @psalm-return array<int, T>
528
    */
529 52
    private function SiblingsExceptLeaf(
530
        DaftNestedWriteableObject $newLeaf,
531
        DaftNestedWriteableObject $referenceLeaf
532
    ) : array {
533
        /**
534
        * @var array<int, DaftNestedWriteableObject>
535
        *
536
        * @psalm-var array<int, T>
537
        */
538 52
        $siblings = $this->RecallDaftNestedObjectTreeWithId(
539 52
            $referenceLeaf->ObtainDaftNestedObjectParentId(),
540 52
            self::EXCLUDE_ROOT,
541 52
            self::RELATIVE_DEPTH_SAME
542
        );
543
544 52
        $siblings = array_values(array_filter(
545 52
            $siblings,
546
            /**
547
            * @psalm-param T $leaf
548
            */
549
            function (DaftNestedWriteableObject $leaf) use ($newLeaf) : bool {
550 28
                return $leaf->GetId() !== $newLeaf->GetId();
551 52
            }
552
        ));
553
554 52
        return $siblings;
555
    }
556
557
    /**
558
    * @param scalar|(scalar|array|object|null)[]|null $replacementRoot
559
    *
560
    * @psalm-param T $rootObject
561
    */
562 12
    private function ModifyDaftNestedObjectTreeRemoveWithIdUsingRootObject(
563
        $replacementRoot,
564
        DaftNestedWriteableObject $rootObject
565
    ) : ? int {
566
        if (
567 12
            $this->CountDaftNestedObjectTreeWithObject(
568 12
                $rootObject,
569 12
                false,
570 12
                null
571 12
            ) > AbstractArrayBackedDaftNestedObject::COUNT_EXPECT_NON_EMPTY &&
572 12
            is_null($replacementRoot)
573
        ) {
574 2
            throw new BadMethodCallException('Cannot leave orphan objects in a tree');
575
        } elseif (
576 10
            ! is_null($replacementRoot) &&
577 10
            $replacementRoot !== $this->GetNestedObjectTreeRootId()
578
        ) {
579 4
            $replacementRoot = $this->RecallDaftObject($replacementRoot);
580
581 4
            return $this->MaybeRemoveWithPossibleObject($rootObject, $replacementRoot);
582
        }
583
584
        /**
585
        * @var scalar|(scalar|array|object|null)[]
586
        */
587 6
        $replacementRoot = $replacementRoot;
588
589 6
        $this->UpdateRemoveThenRebuild($rootObject, $replacementRoot);
590
591 6
        return null;
592
    }
593
}
594