Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
Layout
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
5 / 5
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 normalize
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 array
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 int
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
 clamp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Block;
6
7/**
8 * A block's grid placement: the columns it spans, the rows it spans
9 * and its offset from the row start. Readers clamp stored values so a
10 * field narrowed later or an out-of-range import never breaks a render;
11 * the same bounds are enforced on save by the field shape.
12 */
13final readonly class Layout
14{
15    public const int MAX_ROWSPAN = 6;
16
17    public function __construct(
18        public int $colspan,
19        public int $rowspan,
20        public int $indent,
21    ) {}
22
23    public static function normalize(mixed $layout, int $columns, int $min): self
24    {
25        $layout = is_array($layout) ? $layout : [];
26        $colspan = self::clamp(self::int($layout['colspan'] ?? null, $columns), $min, $columns);
27        $rowspan = self::clamp(self::int($layout['rowspan'] ?? null, 1), 1, self::MAX_ROWSPAN);
28        $indent = self::clamp(self::int($layout['indent'] ?? null, 0), 0, $columns - $colspan);
29
30        return new self($colspan, $rowspan, $indent);
31    }
32
33    /** @return array{colspan: int, rowspan: int, indent: int} */
34    public function array(): array
35    {
36        return ['colspan' => $this->colspan, 'rowspan' => $this->rowspan, 'indent' => $this->indent];
37    }
38
39    private static function int(mixed $value, int $default): int
40    {
41        return is_int($value) || is_numeric($value) ? (int) $value : $default;
42    }
43
44    private static function clamp(int $value, int $min, int $max): int
45    {
46        return max($min, min($max, $value));
47    }
48}