Navigation::getPostfix()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Framework\Navigation;
6
7
use AbterPhp\Framework\Constant\Html5;
8
use AbterPhp\Framework\Html\Attribute;
9
use AbterPhp\Framework\Html\Contentless;
10
use AbterPhp\Framework\Html\INode;
11
use AbterPhp\Framework\Html\IStringer;
12
use AbterPhp\Framework\Html\ITag;
13
use AbterPhp\Framework\Html\Node;
14
use InvalidArgumentException;
15
use LogicException;
16
17
class Navigation extends Contentless
18
{
19
    public const ROLE_NAVIGATION = 'navigation';
20
21
    public const INTENT_NAVBAR    = 'navbar';
22
    public const INTENT_FOOTER    = 'footer';
23
    public const INTENT_PRIMARY   = 'primary';
24
    public const INTENT_SECONDARY = 'secondary';
25
26
    protected const DEFAULT_TAG = Html5::TAG_UL;
27
    protected const CONTENT_TYPE = Item::class;
28
    protected const SEPARATOR = "\n";
29
30
    protected const ERROR_INVALID_TAG_FOR_ITEM_CREATION = 'item creation is not allowed for navigation type: %s';
31
    protected const ERROR_NAVIGATION_OFFSET_NOT_ALLOWED = 'navigation offsets are not allowed';
32
    protected const ERROR_UNEXPECTED_OFFSET_VALUE_COMBINATION = 'Unexpected offset-value combination: %s %s';
33
34
    protected INode $prefix;
35
    protected INode $postfix;
36
37
    protected ?ITag $wrapper = null;
38
39
    /** @var array<int,Item[]> */
40
    protected array $itemsByWeight = [];
41
42
    /** @var int highest key of $itemsByWeight */
43
    protected int $highestWeight = 0;
44
45
    /** @var Item[] */
46
    protected array $content = [];
47
48
    /**
49
     * Navigation constructor.
50
     *
51
     * @param string[]                     $intents
52
     * @param array<string,Attribute>|null $attributes
53
     * @param string|null                  $tag
54
     */
55
    public function __construct(array $intents = [], ?array $attributes = null, ?string $tag = null)
56
    {
57
        parent::__construct($intents, $attributes, $tag);
58
59
        $this->prefix  = new Node();
60
        $this->postfix = new Node();
61
    }
62
63
    /**
64
     * @param int  $weight
65
     * @param Item ...$items
66
     *
67
     * @return $this
68
     */
69
    public function addWithWeight(int $weight, Item ...$items): self
70
    {
71
        foreach ($items as $item) {
72
            $this->itemsByWeight[$weight][] = $item;
73
        }
74
75
        $this->highestWeight = $this->highestWeight > $weight ? $this->highestWeight : $weight;
76
77
        return $this;
78
    }
79
80
    /**
81
     * @param INode ...$items
82
     *
83
     * @return $this
84
     */
85
    public function add(INode ...$items): self
86
    {
87
        foreach ($items as $item) {
88
            assert($item instanceof Item);
89
            $this->itemsByWeight[$this->highestWeight][] = $item;
90
        }
91
92
        return $this;
93
    }
94
95
    protected function resort(): void
96
    {
97
        ksort($this->itemsByWeight);
98
99
        $content = [];
100
        foreach ($this->itemsByWeight as $items) {
101
            $content = array_merge($content, $items);
102
        }
103
104
        $this->content = $content;
105
    }
106
107
    /**
108
     * @return INode
109
     */
110
    public function getPrefix(): INode
111
    {
112
        return $this->prefix;
113
    }
114
115
    /**
116
     * @param INode $prefix
117
     *
118
     * @return $this
119
     */
120
    public function setPrefix(INode $prefix): self
121
    {
122
        $this->prefix = $prefix;
123
124
        return $this;
125
    }
126
127
    /**
128
     * @return INode
129
     */
130
    public function getPostfix(): INode
131
    {
132
        return $this->postfix;
133
    }
134
135
    /**
136
     * @param INode $postfix
137
     *
138
     * @return $this
139
     */
140
    public function setPostfix(INode $postfix): self
141
    {
142
        $this->postfix = $postfix;
143
144
        return $this;
145
    }
146
147
    /**
148
     * @return ITag|null
149
     */
150
    public function getWrapper(): ?ITag
151
    {
152
        return $this->wrapper;
153
    }
154
155
    /**
156
     * @param ITag|null $wrapper
157
     *
158
     * @return $this
159
     */
160
    public function setWrapper(?ITag $wrapper): self
161
    {
162
        $this->wrapper = $wrapper;
163
164
        return $this;
165
    }
166
167
    /**
168
     * @return INode[]
169
     */
170
    public function getNodes(): array
171
    {
172
        $this->resort();
173
174
        return $this->content;
175
    }
176
177
    /**
178
     * @return INode[]
179
     */
180
    public function getExtendedNodes(): array
181
    {
182
        $nodes = array_merge([$this->prefix, $this->postfix], $this->getNodes());
183
184
        if ($this->wrapper) {
185
            $nodes[] = $this->wrapper;
186
        }
187
188
        return $nodes;
189
    }
190
191
    /**
192
     * @return string
193
     */
194
    public function __toString(): string
195
    {
196
        $this->resort();
197
198
        $content = parent::__toString();
199
200
        if ($this->wrapper) {
201
            $content = (string)$this->wrapper->setContent($content);
202
        }
203
204
        $prefix  = $this->prefix ? (string)$this->prefix : '';
205
        $postfix = $this->postfix ? (string)$this->postfix : '';
206
207
        return $prefix . $content . $postfix;
208
    }
209
210
    /**
211
     * @param int $offset
212
     *
213
     * @return INode|null
214
     */
215
    public function offsetGet($offset): ?INode
216
    {
217
        $this->resort();
218
219
        return $this->content[$offset] ?? null;
220
    }
221
222
    /**
223
     * @return int
224
     */
225
    public function count(): int
226
    {
227
        $this->resort();
228
229
        return count($this->content);
230
    }
231
232
    public function rewind(): void
233
    {
234
        $this->resort();
235
236
        $this->position = 0;
237
    }
238
239
    /**
240
     * @param int|null $offset
241
     * @param INode    $value
242
     */
243
    public function offsetSet($offset, $value): void
244
    {
245
        assert($value instanceof Item);
246
247
        if (is_null($offset)) {
248
            $this->addWithWeight($this->highestWeight, $value);
249
            return;
250
        } elseif ($offset < 0 || $offset > $this->count()) {
251
            throw new InvalidArgumentException(static::ERROR_INVALID_OFFSET);
252
        }
253
254
        $count = 0;
255
        foreach ($this->itemsByWeight as $weight => $values) {
256
            $diff = $offset - $count;
257
            if (count($values) + $count >= $offset) {
258
                array_splice($this->itemsByWeight[$weight], $diff, 1, [$value]);
259
260
                break;
261
            } else {
262
                $count += count($values);
263
            }
264
        }
265
    }
266
267
    /**
268
     * @param int $offset
269
     */
270
    public function offsetUnset($offset): void
271
    {
272
        $count = 0;
273
        foreach ($this->itemsByWeight as $weight => $values) {
274
            $diff = $offset - count($values);
275
            if (count($values) + $count >= $offset) {
276
                array_splice($this->itemsByWeight[$weight], $diff, 1);
277
278
                break;
279
            } else {
280
                $count += count($values);
281
            }
282
        }
283
    }
284
285
    /**
286
     * @param array<string|IStringer>|string|IStringer|null $content
287
     *
288
     * @return $this
289
     */
290
    public function setContent($content): self
291
    {
292
        if (null === $content) {
293
            return $this;
294
        }
295
296
        throw new LogicException(self::ERROR_NO_CONTENT);
297
    }
298
}
299