HtmlBuilder::video()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 27
rs 9.1111
c 0
b 0
f 0
cc 6
nc 6
nop 2
1
<?php
2
namespace Cohensive\OEmbed;
3
4
class HtmlBuilder
5
{
6
    const TYPE_RAW = 'raw';
7
    const TYPE_IFRAME = 'iframe';
8
    const TYPE_VIDEO = 'video';
9
10
    public function __construct(
11
        protected string $type,
12
        protected string|array $html,
13
        protected ?string $script = null
14
    ) {
15
    }
16
17
    /**
18
     * Returns current type.
19
     */
20
    public function type(): string
21
    {
22
        return $this->type;
23
    }
24
25
    /**
26
     * Returns HTML code for media provider.
27
     */
28
    public function html(array $options = [], array $globalOptions = [], bool $amp = false): string
29
    {
30
        if (is_array($this->html)) {
31
            $attrs = $this->applyOptions($this->html, $options, $globalOptions);
32
33
            if ($this->type === self::TYPE_IFRAME) {
34
                return $this->iframe($attrs, $amp);
35
            }
36
37
            if ($this->type === self::TYPE_VIDEO) {
38
                return $this->video($attrs, $amp);
39
            }
40
41
            return '';
42
        } else {
43
            return $this->html;
44
        }
45
    }
46
47
    /**
48
     * Return AMP-friendly HTML for media provider.
49
     */
50
    public function ampHtml(array $options = [], array $globalOptions = []): string
51
    {
52
        return $this->html($options, $globalOptions, true);
53
    }
54
55
    /**
56
     * Returns URL for a given media provider embed. Returned url type depends on embed type:
57
     * iframe - string
58
     * video - string[]
59
     * raw - null
60
     */
61
    public function src(array $options = [], array $globalOptions = []): mixed
62
    {
63
        if (is_array($this->html)) {
64
            $attrs = $this->applyOptions($this->html, $options, $globalOptions);
65
66
            if ($this->type === self::TYPE_IFRAME) {
67
                return $attrs['src'] ?? null;
68
            }
69
70
            if ($this->type === self::TYPE_VIDEO) {
71
                return array_map(function ($source) {
72
                    return $source['src'];
73
                }, $attrs['source']);
74
            }
75
        }
76
77
        return null;
78
    }
79
80
    /**
81
     * Constructs <iframe> HTML-element based on array of provider attributes.
82
     */
83
    protected function iframe(array $attrs, bool $amp = false): string
84
    {
85
        $tag = $amp ? 'amp-iframe' : 'iframe';
86
87
        $html = "<$tag";
88
        foreach ($attrs as $attr => $val) {
89
            $html .= sprintf(' %s="%s"', $attr, $val);
90
        }
91
        $html .= "></$tag>";
92
93
        return $html;
94
    }
95
96
    /**
97
     * Constructs <video> HTML-element based on an array of provider attributes.
98
     */
99
    protected function video(array $attrs, bool $amp = false): string
100
    {
101
        $tag = $amp ? 'amp-video' : 'video';
102
103
        $inner = '';
104
105
        $html = "<$tag";
106
        foreach ($attrs as $attr => $val) {
107
            if (is_array($val)) {
108
                foreach ($val as $child) {
109
                    $inner .= "<$attr";
110
                    foreach ($child as $iattr => $ival) {
111
                        $inner .= sprintf(' %s="%s"', $iattr, $ival);
112
                    }
113
                    $inner .= ">";
114
                }
115
            } else {
116
                $html .= sprintf(' %s="%s"', $attr, $val);
117
            }
118
        }
119
        $html .= ">";
120
121
        $html .= $inner;
122
123
        $html .= "</$tag>";
124
125
        return $html;
126
    }
127
128
    /**
129
     * Returns script source if available.
130
     */
131
    public function script(): ?string
132
    {
133
        return $this->script;
134
    }
135
136
    /**
137
     * Converts class to an array.
138
     */
139
    public function toArray(): array
140
    {
141
        return [
142
            'type' => $this->type,
143
            'html' => $this->html,
144
        ];
145
    }
146
147
    /**
148
     * Extracts and returns an array of options for a current HTML element type.
149
     */
150
    protected function getTypeOptions(array $options): array
151
    {
152
        if (isset($options['html'])) {
153
            return $options['html'][$this->type] ?? [];
154
        }
155
156
        return [];
157
    }
158
159
    /**
160
     * Merge and apply local and global options to the provider attributes.
161
     */
162
    protected function applyOptions(array $attrs, array $options, array $globalOptions): array
163
    {
164
        $options = array_merge($globalOptions['attributes'] ?? [], $options);
165
        $width = $options['width'] ?? null;
166
        $height = $options['height'] ?? null;
167
168
        if (isset($attrs['width']) && isset($attrs['height'])) {
169
            $ratio = $attrs['width'] / $attrs['height'];
170
            $attrs['width'] = $width ?: round(($height ?: $attrs['height']) * $ratio);
171
            $attrs['height'] = $height ?: round($attrs['width'] / $ratio);
172
        }
173
174
        $typeOptions = $this->getTypeOptions($globalOptions);
175
176
        if ($options['autoplay'] ?? false) {
177
            $attrs['autoplay'] = $options['autoplay'];
178
179
            // We can remove autoplay option if type is "iframe" after we change "src" attribute.
180
            if ($this->type === self::TYPE_IFRAME) {
181
                $attrs['src'] = $this->addUrlParam($attrs['src'], sprintf('%s=%s', 'autoplay', $attrs['autoplay']));
182
                unset($options['autoplay']);
183
                unset($attrs['autoplay']);
184
            }
185
        }
186
187
        return array_filter(
188
            array_merge($typeOptions, $options, $attrs),
189
            function ($v) {
190
                return $v !== null;
191
            }
192
        );
193
    }
194
195
    /**
196
     * Append custom parameter to the end of the url.
197
     */
198
    protected function addUrlParam(string $url, string $param): string
199
    {
200
        $operator = strpos($url, '?') >= 0 ? '&' : '?';
201
        return $url . $operator . $param;
202
    }
203
}
204