Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
69.09% covered (warning)
69.09%
38 / 55
44.44% covered (danger)
44.44%
8 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
File
69.09% covered (warning)
69.09%
38 / 55
44.44% covered (danger)
44.44%
8 / 18
97.60
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __toString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 title
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 url
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 publicPath
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 filename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 mimetype
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 unwrap
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 json
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 count
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 files
57.14% covered (warning)
57.14%
8 / 14
0.00% covered (danger)
0.00%
0 / 1
15.38
 fileItem
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 effectiveFiles
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 hasFile
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
5
 getFileName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 textValue
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
9.08
 getFile
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Value;
6
7use Cosray\Assets;
8use Cosray\Exception\RuntimeException;
9use Cosray\Field\Capability\Translatable;
10use Cosray\Field\Field;
11use Cosray\Field\Owner;
12use Cosray\Schema\TranslateMode;
13
14/**
15 * @property-read Field&Translatable $field
16 */
17class File extends Value
18{
19    public function __construct(
20        Owner $owner,
21        Field&Translatable $field,
22        ValueContext $context,
23        protected int $index = 0,
24    ) {
25        parent::__construct($owner, $field, $context);
26    }
27
28    public function __toString(): string
29    {
30        return $this->publicPath(false);
31    }
32
33    public function title(): string
34    {
35        return $this->textValue('title', $this->index);
36    }
37
38    public function url(bool $bust = false): string
39    {
40        if ($url = filter_var($this->getFile($this->index)->url($bust), FILTER_VALIDATE_URL)) {
41            return $url;
42        }
43
44        throw new RuntimeException('Invalid file url');
45    }
46
47    public function publicPath(bool $bust = false): string
48    {
49        return filter_var($this->getFile($this->index)->publicPath($bust), FILTER_SANITIZE_URL);
50    }
51
52    public function filename(): string
53    {
54        return $this->getFileName($this->index);
55    }
56
57    public function mimetype(): string
58    {
59        return mime_content_type($this->getFile($this->index)->path());
60    }
61
62    public function unwrap(): ?array
63    {
64        return $this->fileItem(0);
65    }
66
67    public function json(): mixed
68    {
69        return $this->data;
70    }
71
72    public function count(): int
73    {
74        return count($this->files());
75    }
76
77    public function isset(): bool
78    {
79        return $this->fileItem(0) !== null;
80    }
81
82    protected function files(): array
83    {
84        $value = $this->data['value'] ?? [];
85
86        if (
87            !isset($this->data['type'])
88            && isset($this->data['files'])
89            && is_array($this->data['files'])
90        ) {
91            if (
92                $this->field->translateMode() === TranslateMode::Asymmetric
93                && !array_is_list($this->data['files'])
94            ) {
95                return $this->effectiveFiles($this->data['files']);
96            }
97
98            return $this->data['files'];
99        }
100
101        if (!is_array($value)) {
102            return [];
103        }
104
105        if ($this->field->translateMode() === TranslateMode::Asymmetric) {
106            return $this->effectiveFiles($value);
107        }
108
109        $files = $value[Field::NEUTRAL_LOCALE] ?? [];
110
111        return is_array($files) ? $files : [];
112    }
113
114    protected function fileItem(int $index): ?array
115    {
116        $item = $this->files()[$index] ?? null;
117
118        return is_array($item) ? $item : null;
119    }
120
121    /** @param array<string, mixed> $map */
122    protected function effectiveFiles(array $map): array
123    {
124        $locale = $this->locale;
125
126        while ($locale) {
127            $files = $map[$locale->id] ?? null;
128
129            if ($this->hasFile($files)) {
130                return $files;
131            }
132
133            $locale = $locale->fallback();
134        }
135
136        $files = $map[Field::NEUTRAL_LOCALE] ?? null;
137
138        return $this->hasFile($files) ? $files : [];
139    }
140
141    protected function hasFile(mixed $files): bool
142    {
143        if (!is_array($files)) {
144            return false;
145        }
146
147        foreach ($files as $file) {
148            if (is_array($file) && ($file['file'] ?? null)) {
149                return true;
150            }
151        }
152
153        return false;
154    }
155
156    protected function getFileName(int $index): string
157    {
158        return (string) ($this->fileItem($index)['file'] ?? '');
159    }
160
161    protected function textValue(string $key, int $index): string
162    {
163        $item = $this->fileItem($index) ?? [];
164        $value = $item['meta'][$key] ?? null;
165
166        if (!is_array($value) && !isset($this->data['type']) && array_key_exists($key, $item)) {
167            $value = $item[$key];
168        }
169
170        if (!is_array($value)) {
171            return is_string($value) || is_numeric($value) ? (string) $value : '';
172        }
173
174        $value = $this->effective($value);
175
176        if (is_string($value) || is_numeric($value)) {
177            return (string) $value;
178        }
179
180        return '';
181    }
182
183    protected function getFile(int $index): Assets\File
184    {
185        return $this->getAssets()->file($this->assetsPath() . $this->getFileName($index));
186    }
187}