Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.49% covered (warning)
86.49%
32 / 37
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Value
86.49% covered (warning)
86.49%
32 / 37
77.78% covered (warning)
77.78%
7 / 9
29.93
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 __get
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 __toString
n/a
0 / 0
n/a
0 / 0
0
 isset
n/a
0 / 0
n/a
0 / 0
0
 json
n/a
0 / 0
n/a
0 / 0
0
 raw
n/a
0 / 0
n/a
0 / 0
0
 styleClass
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 elementId
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 value
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 zxx
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 meta
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
5
 effective
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 filled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Value;
6
7use Cosray\Exception\NoSuchProperty;
8use Cosray\Field\Field;
9use Cosray\Field\Field as FieldBase;
10use Cosray\Field\Owner;
11use Cosray\Locale;
12use Override;
13
14abstract class Value
15{
16    public readonly string $fieldType;
17    protected readonly Locale $locale;
18    protected readonly Locale $defaultLocale;
19    protected readonly string $fieldName;
20
21    /** @var array<string, mixed> */
22    protected readonly array $data;
23
24    public function __construct(
25        protected readonly Owner $owner,
26        protected readonly Field $field,
27        protected readonly ValueContext $context,
28    ) {
29        $this->locale = $owner->locale();
30        $this->defaultLocale = $owner->defaultLocale();
31        $this->data = $context->data;
32        $this->fieldName = $context->fieldName;
33        $this->fieldType = $field->type;
34    }
35
36    public function __get(string $name): mixed
37    {
38        if (array_key_exists($name, $this->data)) {
39            return $this->data[$name];
40        }
41
42        if (array_key_exists($name, $this->data['meta'] ?? [])) {
43            return $this->meta($name);
44        }
45
46        throw new NoSuchProperty("The field '{$this->fieldName}' doesn't have the property '{$name}'");
47    }
48
49    /** Safe HTML: Cosray's Boiler renderers print it without escaping. */
50    #[Override]
51    abstract public function __toString(): string;
52
53    abstract public function isset(): bool;
54
55    /**
56     * The value as the API delivers it: resolved for the owner's locale,
57     * plain JSON data, null when a non-text value is empty. docs/api.md
58     * documents the shape of every built-in value.
59     */
60    abstract public function json(): mixed;
61
62    abstract public function raw(): mixed;
63
64    public function styleClass(): ?string
65    {
66        $value = $this->meta('class');
67
68        return is_string($value) && $value !== '' ? $value : null;
69    }
70
71    public function elementId(): ?string
72    {
73        $value = $this->meta('id');
74
75        return is_string($value) && $value !== '' ? $value : null;
76    }
77
78    protected function value(): mixed
79    {
80        $value = $this->data['value'] ?? [];
81
82        if (!is_array($value) && !isset($this->data['type'])) {
83            return $value;
84        }
85
86        return is_array($value) ? $this->effective($value) : null;
87    }
88
89    protected function zxx(): mixed
90    {
91        $value = $this->data['value'] ?? [];
92
93        if (!is_array($value)) {
94            return null;
95        }
96
97        return $value[FieldBase::NEUTRAL_LOCALE] ?? null;
98    }
99
100    protected function meta(string $key, mixed $default = null): mixed
101    {
102        $meta = $this->data['meta'][$key] ?? null;
103
104        if (!is_array($meta) && !isset($this->data['type']) && array_key_exists($key, $this->data)) {
105            return $this->data[$key];
106        }
107
108        if (!is_array($meta)) {
109            return $default;
110        }
111
112        return $this->effective($meta) ?? $default;
113    }
114
115    /** @param array<mixed> $map */
116    protected function effective(array $map): mixed
117    {
118        $locale = $this->locale;
119
120        while ($locale) {
121            if ($this->filled($map[$locale->id] ?? null)) {
122                return $map[$locale->id];
123            }
124
125            $locale = $locale->fallback();
126        }
127
128        if ($this->filled($map[FieldBase::NEUTRAL_LOCALE] ?? null)) {
129            return $map[FieldBase::NEUTRAL_LOCALE];
130        }
131
132        return null;
133    }
134
135    protected function filled(mixed $value): bool
136    {
137        return $value !== null && $value !== '' && $value !== [];
138    }
139}