Passed
Push — master ( 951441...b6d9b3 )
by Kane
02:14
created

Embed::height()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 1
c 1
b 0
f 1
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
namespace Cohensive\OEmbed;
3
4
use Cohensive\OEmbed\Exceptions\HtmlParsingException;
5
use DOMDocument;
6
7
class Embed
8
{
9
    const TYPE_OEMBED = 0;
10
11
    const TYPE_REGEX = 1;
12
13
    /**
14
     * Type of an Embed object source data - OEmbed or Regex-based.
15
     */
16
    protected int $type;
17
18
    /**
19
     * Array of global options applied to embed objects.
20
     */
21
    protected array $options;
22
23
    /**
24
     * Original media URL.
25
     */
26
    protected string $url;
27
28
    /**
29
     * Embed data extracted via OEmbed or Regex extractors.
30
     */
31
    protected array $data;
32
33
    /**
34
     * Class containing Embed HTML code.
35
     */
36
    protected HtmlBuilder $html;
37
38
    /**
39
     * Thumbnail data if available.
40
     */
41
    protected ?array $thumbnail;
42
43
    /**
44
     * Creates Embed instance.
45
     */
46
    public function __construct(
47
        string $type,
48
        string $url,
49
        array $data,
50
        array $options = [],
51
        bool $amp = false
52
    ) {
53
        $this->type = $type;
54
        $this->url = $url;
55
        $this->data = $data;
56
        $this->options = $options;
57
        $this->amp = $amp;
0 ignored issues
show
Bug Best Practice introduced by
The property amp does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
58
59
        $this->initData($data);
60
    }
61
62
    /**
63
     * Initializer for the Embed object filling thumbnail, html and type based
64
     * on a given media provider data.
65
     */
66
    public function initData(array $data): self
67
    {
68
        if (isset($data['thumbnail_url'])) {
69
            $this->thumbnail = [
70
                'url' => $data['thumbnail_url'],
71
                'width' => $data['thumbnail_width'] ?? null,
72
                'height' => $data['thumbnail_height'] ?? null,
73
            ];
74
        }
75
76
        if ($this->type == self::TYPE_OEMBED) {
77
            $this->html = $this->extractOEmbedHtml($data['html']);
78
        } else {
79
            $this->html = $this->extractRegexHtml($data['html']);
80
        }
81
82
        return $this;
83
    }
84
85
    /**
86
     * Returns mbed options.
87
     */
88
    public function getOptions(): array
89
    {
90
        return $this->options;
91
    }
92
93
    /**
94
     * Sets new embed options. See config file 'options' key.
95
     */
96
    public function setOptions(array $options): self
97
    {
98
        $this->options = $options;
99
        return $this;
100
    }
101
102
    /**
103
     * Sets AMP mode.
104
     */
105
    public function setAmp(bool $amp): self
106
    {
107
        $this->amp = $amp;
0 ignored issues
show
Bug Best Practice introduced by
The property amp does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
108
        return $this;
109
    }
110
111
    /**
112
     * Returns current HtmlBuilder instance.
113
     */
114
    public function htmlBuilder(): HtmlBuilder
115
    {
116
        return $this->html;
117
    }
118
119
    /**
120
     * Returns string with HTML to embed in application. Will return AMP-friendly
121
     * HTML if global amp mode is enabled and not overwitten.
122
     */
123
    public function html(array $options = null, ?bool $amp = null): string
124
    {
125
        if (is_null($options)) {
126
            $options = $this->options;
127
        } else {
128
            $options = array_merge($this->options, $options);
129
        }
130
131
        if (is_null($amp)) {
132
            $amp = $this->amp;
133
        }
134
135
        return $this->html->html($options, $amp);
136
    }
137
138
    /**
139
     * Returns string with AMP-friendly HTML to embed in an application.
140
     */
141
    public function ampHtml(array $options = null): string
142
    {
143
        return $this->html($options, true);
144
    }
145
146
    /**
147
     * Return script source if available in embed html.
148
     */
149
    public function script(): ?string
150
    {
151
        return $this->html->script();
152
    }
153
154
    /**
155
     * Returns embed provider type.
156
     */
157
    public function type(): int
158
    {
159
        return $this->type;
160
    }
161
162
    /**
163
     * Returns embed html type.
164
     */
165
    public function htmlType(): string
166
    {
167
        return $this->html->type();
168
    }
169
170
    /**
171
     * Returns media provider data.
172
     */
173
    public function url(): string
174
    {
175
        return $this->url;
176
    }
177
178
    /**
179
     * Returns media provider data.
180
     */
181
    public function data(): array
182
    {
183
        return $this->data;
184
    }
185
186
    /**
187
     * Returns width of the embed. 0 if not set.
188
     */
189
    public function width(): int
190
    {
191
        return $this->data['width'] ?? 0;
192
    }
193
194
    /**
195
     * Returns height of the embed. 0 if not set.
196
     */
197
    public function height(): int
198
    {
199
        return $this->data['height'] ?? 0;
200
    }
201
202
    /**
203
     * Returns width/height ratio of the embed. 0 if dimensions are not set.
204
     */
205
    public function ratio(): float
206
    {
207
        if (isset($this->data['width']) && isset($this->data['height'])) {
208
            return $this->data['width'] / $this->data['height'];
209
        }
210
211
        return 0;
212
    }
213
214
    /**
215
     * Returns string describing media type. According to OEmbed spec it could be:
216
     * one of these: photo, video, link, rich
217
     */
218
    public function mediaType(): string
219
    {
220
        return $this->data['type'];
221
    }
222
223
    /**
224
     * Returns boolean flag telling if given embed data has a thumbnail.
225
     */
226
    public function hasThumbnail(): bool
227
    {
228
        return is_array($this->thumbnail);
229
    }
230
231
    /**
232
     * Returns thumbnail data in an array form containing url and its dimensions.
233
     *
234
     */
235
    public function thumbnail(): ?array
236
    {
237
        return $this->thumbnail;
238
    }
239
240
    /**
241
     * Reutrns string for thumbnail or null if it's not set.
242
     */
243
    public function thumbnailUrl(): ?string
244
    {
245
        return $this->hasThumbnail() ? $this->thumbnail['url'] : null;
246
    }
247
248
    /**
249
     * Return thumbnail width or 0 if not set.
250
     */
251
    public function thumbnailWidth(): int
252
    {
253
        return $this->hasThumbnail() ? $this->thumbnail['width'] : 0;
254
    }
255
256
    /**
257
     * Return thumbnail height or 0 if not set.
258
     */
259
    public function thumbnailHeight(): int
260
    {
261
        return $this->hasThumbnail() ? $this->thumbnail['height'] : 0;
262
    }
263
264
    /**
265
     * Converts Embed instance into an array for caching.
266
     */
267
    public function toArray(): array
268
    {
269
        return [
270
            'type' => $this->type,
271
            'url' => $this->url,
272
            'data' => $this->data,
273
        ];
274
    }
275
276
    /**
277
     * Converts Embed instance into json string.
278
     */
279
    public function toJson(): string
280
    {
281
        return json_encode($this->toArray());
282
    }
283
284
    /**
285
     * Returns HtmlBuilder instance of OEmbed media provider HTML string.
286
     */
287
    protected function extractOEmbedHtml(string $html): HtmlBuilder
288
    {
289
        $script = null;
290
        $doc = new DOMDocument();
291
        $doc->loadHTML("<html><body>$html</body></html>", LIBXML_NOERROR);
292
        $body = $doc->documentElement->lastChild;
293
294
        $scripts = $body->getElementsByTagName('script');
0 ignored issues
show
Bug introduced by
The method getElementsByTagName() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

294
        /** @scrutinizer ignore-call */ 
295
        $scripts = $body->getElementsByTagName('script');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
295
        foreach ($scripts as $node) {
296
            $script = $node->getAttribute('src');
297
            break;
298
        }
299
300
        if (!$body->firstChild) {
301
            throw new HtmlParsingException();
302
        }
303
304
        if ($body->firstChild && $body->firstChild->nodeName === 'iframe') {
305
            $attrs = [];
306
307
            foreach ($body->firstChild->attributes as $attribute) {
308
                $attrs[$attribute->name] = $attribute->value;
309
            }
310
311
            return new HtmlBuilder(HtmlBuilder::TYPE_IFRAME, $attrs, $script);
312
        }
313
314
        return new HtmlBuilder(HtmlBuilder::TYPE_RAW, $html, $script);
315
    }
316
317
    /**
318
     * Returns HtmlBuilder instance of Regex media provider HTML string.
319
     */
320
    protected function extractRegexHtml(array $html): HtmlBuilder
321
    {
322
        $type = array_key_first($html);
323
        return new HtmlBuilder($type, $html[$type]);
324
    }
325
}
326