Passed
Pull Request — master (#180)
by Aleksei
02:23
created

Heap::eraseIndexes()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 10
nc 8
nop 3
dl 0
loc 14
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Cycle DataMapper ORM
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\ORM\Heap;
13
14
use IteratorAggregate;
15
use SplObjectStorage;
16
use UnexpectedValueException;
17
18
final class Heap implements HeapInterface, IteratorAggregate
19
{
20
    /** @var SplObjectStorage */
21
    private $storage;
22
23
    /** @var array */
24
    private $paths = [];
25
26
    /**
27
     * Heap constructor.
28
     */
29
    public function __construct()
30
    {
31
        $this->clean();
32
    }
33
34
    /**
35
     * Heap destructor.
36
     */
37
    public function __destruct()
38
    {
39
        $this->clean();
40
    }
41
42
    public function getIterator(): SplObjectStorage
43
    {
44
        return clone $this->storage;
45
    }
46
47
    /**
48
     * @inheritdoc
49
     */
50
    public function has($entity): bool
51
    {
52
        return $this->storage->offsetExists($entity);
53
    }
54
55
    /**
56
     * @inheritdoc
57
     */
58
    public function get($entity): ?Node
59
    {
60
        try {
61
            return $this->storage->offsetGet($entity);
62
        } catch (UnexpectedValueException $e) {
63
            return null;
64
        }
65
    }
66
67
    /**
68
     * @inheritdoc
69
     */
70
    public function find(string $role, array $scope)
71
    {
72
        if (count($scope) === 1) {
73
            $key = key($scope);
74
            if (is_object($scope[$key])) {
75
                $scope[$key] = (string)$scope[$key];
76
            }
77
            return $this->paths[$role][$key][$scope[$key]] ?? null;
78
        }
79
80
        $key = $value = '';
81
        foreach ($scope as $k => $v) {
82
            $key .= $k;
83
            $value .= $v . '/';
84
        }
85
86
        return $this->paths[$role][$key][$value] ?? null;
87
    }
88
89
    /**
90
     * @inheritdoc
91
     */
92
    public function attach($entity, Node $node, array $index = []): void
93
    {
94
        $this->storage->offsetSet($entity, $node);
95
        $role = $node->getRole();
96
97
        if ($node->hasState()) {
98
            $this->eraseIndexes($role, $node->getInitialData(), $entity);
99
        }
100
101
        $data = $node->getData();
102
        foreach ($index as $key) {
103
            if (is_array($key)) {
104
                $keyName = $value = '';
105
                foreach ($key as $k) {
106
                    $keyName .= $k; // chance of collision?
107
                    $value .= (string)$data[$k] . '/';
108
                }
109
                $key = $keyName;
110
            } else {
111
                if (!isset($data[$key])) {
112
                    continue;
113
                }
114
115
                $value = (string)$data[$key];
116
            }
117
118
            $this->paths[$role][$key][$value] = $entity;
119
        }
120
    }
121
122
    /**
123
     * @inheritdoc
124
     */
125
    public function detach($entity): void
126
    {
127
        $node = $this->get($entity);
128
        if ($node === null) {
129
            return;
130
        }
131
132
        $role = $node->getRole();
133
134
        $this->eraseIndexes($role, $node->getData(), $entity);
135
        if ($node->hasState()) {
136
            $this->eraseIndexes($role, $node->getInitialData(), $entity);
137
        }
138
139
        $this->storage->offsetUnset($entity);
140
    }
141
142
    /**
143
     * @inheritdoc
144
     */
145
    public function clean(): void
146
    {
147
        $this->paths = [];
148
        $this->storage = new \SplObjectStorage();
149
    }
150
151
    private function eraseIndexes(string $role, array $data, object $entity): void
152
    {
153
        if (!isset($this->paths[$role])) {
154
            return;
155
        }
156
        $keys = array_keys($this->paths[$role]);
157
        foreach ($keys as $key) {
158
            $value = isset($data[$key]) ? (string)$data[$key] : null;
159
            if ($value === null) {
160
                continue;
161
            }
162
            $current = &$this->paths[$role][$key];
163
            if (isset($current[$value]) && $current[$value] === $entity) {
164
                unset($current[$value]);
165
            }
166
        }
167
    }
168
}
169