Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.44% covered (success)
94.44%
34 / 36
71.43% covered (warning)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Image
94.44% covered (success)
94.44%
34 / 36
71.43% covered (warning)
71.43%
5 / 7
17.05
0.00% covered (danger)
0.00%
0 / 1
 __toString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 tag
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 size
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 publicPath
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
4.07
 json
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
6
 link
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 alt
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\Value;
6
7use Cosray\Assets\SizeSpec;
8use Cosray\Markup;
9use Override;
10
11use function Cosray\escape;
12
13class Image extends File
14{
15    protected ?SizeSpec $spec = null;
16
17    #[Override]
18    public function __toString(): string
19    {
20        return (string) $this->tag();
21    }
22
23    public function tag(?string $class = null): Markup
24    {
25        return new Markup(sprintf(
26            '<img %ssrc="%s" alt="%s" data-path-original="%s">',
27            $class ? sprintf('class="%s" ', escape($class)) : '',
28            $this->url(),
29            escape($this->alt() ?: strip_tags($this->title())),
30            $this->asset($this->index)?->path() ?? '',
31        ));
32    }
33
34    /**
35     * Use a named rendition from the `media.sizes` config. Unknown names
36     * throw immediately; assets the resize pipeline cannot process (SVG,
37     * video posters) keep their original URL.
38     */
39    public function size(string $name): static
40    {
41        $new = clone $this;
42        $new->spec = $this->owner->config()->media->sizes->get($name);
43
44        return $new;
45    }
46
47    #[Override]
48    public function publicPath(): string
49    {
50        $asset = $this->asset($this->index);
51
52        if ($asset === null) {
53            return '';
54        }
55
56        if ($this->spec !== null && $asset->resizable()) {
57            return $asset->sizePath($this->spec->name);
58        }
59
60        return $asset->path();
61    }
62
63    /**
64     * Adds the image facts and a URL for every named size; an asset the
65     * resize pipeline cannot process gives its original URL for each.
66     *
67     * @return ?array<string, mixed>
68     */
69    #[Override]
70    public function json(): ?array
71    {
72        $file = parent::json();
73        $asset = $this->asset($this->index);
74
75        if ($file === null || $asset === null) {
76            return null;
77        }
78
79        $sizes = [];
80
81        foreach ($this->owner->config()->media->sizes->names() as $name) {
82            $sizes[$name] = $asset->resizable() ? $asset->sizePath($name) : $asset->path();
83        }
84
85        $focal = $asset->meta['focal'] ?? null;
86
87        return [
88            ...$file,
89            'alt' => $this->alt(),
90            'link' => $this->link(),
91            'width' => $asset->width,
92            'height' => $asset->height,
93            'focal' => is_array($focal) ? $focal : null,
94            'sizes' => $sizes,
95        ];
96    }
97
98    public function link(): string
99    {
100        return $this->textValue('link', $this->index);
101    }
102
103    public function alt(): string
104    {
105        return $this->textValue('alt', $this->index);
106    }
107}