Passed
Push — master ( b6d9b3...e6422a )
by Kane
02:22
created

Embed::type()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
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();
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(): self
67
    {
68
        $data = $this->data();
69
70
        if (isset($data['thumbnail_url'])) {
71
            $this->thumbnail = [
72
                'url' => $data['thumbnail_url'],
73
                'width' => $data['thumbnail_width'] ?? null,
74
                'height' => $data['thumbnail_height'] ?? null,
75
            ];
76
        }
77
78
        if ($this->type == self::TYPE_OEMBED) {
79
            $this->html = $this->extractOEmbedHtml($data['html']);
80
        } else {
81
            $this->html = $this->extractRegexHtml($data['html']);
82
        }
83
84
        return $this;
85
    }
86
87
    /**
88
     * Returns mbed options.
89
     */
90
    public function getOptions(): array
91
    {
92
        return $this->options;
93
    }
94
95
    /**
96
     * Sets new embed options. See config file 'options' key.
97
     */
98
    public function setOptions(array $options): self
99
    {
100
        $this->options = $options;
101
        return $this;
102
    }
103
104
    /**
105
     * Returns provider-specific data options.
106
    */
107
    public function getProviderOptions(): array
108
    {
109
        if (isset($this->options['providers'])) {
110
            return $this->options['providers'][$this->data['provider_name']]['data'] ?? [];
111
        }
112
113
        return [];
114
    }
115
116
    /**
117
     * Returns provider-specific HTML options.
118
    */
119
    public function getProviderHtmlOptions(): array
120
    {
121
        if (isset($this->options['providers'])) {
122
            return $this->options['providers'][$this->data['provider_name']]['html'] ?? [];
123
        }
124
125
        return [];
126
    }
127
128
    /**
129
     * Sets AMP mode.
130
     */
131
    public function setAmp(bool $amp): self
132
    {
133
        $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...
134
        return $this;
135
    }
136
137
    /**
138
     * Returns current HtmlBuilder instance.
139
     */
140
    public function htmlBuilder(): HtmlBuilder
141
    {
142
        return $this->html;
143
    }
144
145
    /**
146
     * Returns string with HTML to embed in application. Will return AMP-friendly
147
     * HTML if global amp mode is enabled and not overwitten.
148
     */
149
    public function html(array $options = null, ?bool $amp = null): string
150
    {
151
        if (is_null($options)) {
152
            $options = $this->options;
153
        } else {
154
            $options = array_merge($this->options, $options);
155
        }
156
157
        if (is_null($amp)) {
158
            $amp = $this->amp;
159
        }
160
161
        return $this->html->html($options, $amp);
162
    }
163
164
    /**
165
     * Returns string with AMP-friendly HTML to embed in an application.
166
     */
167
    public function ampHtml(array $options = null): string
168
    {
169
        return $this->html($options, true);
170
    }
171
172
    /**
173
     * Return script source if available in embed html.
174
     */
175
    public function script(): ?string
176
    {
177
        return $this->html->script();
178
    }
179
180
    /**
181
     * Returns embed provider type.
182
     */
183
    public function type(): int
184
    {
185
        return $this->type;
186
    }
187
188
    /**
189
     * Returns embed html type.
190
     */
191
    public function htmlType(): string
192
    {
193
        return $this->html->type();
194
    }
195
196
    /**
197
     * Returns media provider data.
198
     */
199
    public function url(): string
200
    {
201
        return $this->url;
202
    }
203
204
    /**
205
     * Returns media provider data with applied options if set.
206
     */
207
    public function data(): array
208
    {
209
        $data = array_merge($this->data, $this->getProviderOptions());
210
211
        return $data;
212
    }
213
214
    /**
215
     * Returns width of the embed. 0 if not set.
216
     */
217
    public function width(): int
218
    {
219
        return $this->data()['width'] ?? 0;
220
    }
221
222
    /**
223
     * Returns height of the embed. 0 if not set.
224
     */
225
    public function height(): int
226
    {
227
        return $this->data()['height'] ?? 0;
228
    }
229
230
    /**
231
     * Returns width/height ratio of the embed. 0 if dimensions are not set.
232
     */
233
    public function ratio(): float
234
    {
235
        if (isset($this->data()['width']) && isset($this->data()['height'])) {
236
            return $this->data()['width'] / $this->data()['height'];
237
        }
238
239
        return 0;
240
    }
241
242
    /**
243
     * Returns string describing media type. According to OEmbed spec it could be:
244
     * one of these: photo, video, link, rich
245
     */
246
    public function mediaType(): string
247
    {
248
        return $this->data['type'];
249
    }
250
251
    /**
252
     * Returns boolean flag telling if given embed data has a thumbnail.
253
     */
254
    public function hasThumbnail(): bool
255
    {
256
        return is_array($this->thumbnail);
257
    }
258
259
    /**
260
     * Returns thumbnail data in an array form containing url and its dimensions.
261
     *
262
     */
263
    public function thumbnail(): ?array
264
    {
265
        return $this->thumbnail;
266
    }
267
268
    /**
269
     * Reutrns string for thumbnail or null if it's not set.
270
     */
271
    public function thumbnailUrl(): ?string
272
    {
273
        return $this->hasThumbnail() ? $this->thumbnail['url'] : null;
274
    }
275
276
    /**
277
     * Return thumbnail width or 0 if not set.
278
     */
279
    public function thumbnailWidth(): int
280
    {
281
        return $this->hasThumbnail() ? $this->thumbnail['width'] : 0;
282
    }
283
284
    /**
285
     * Return thumbnail height or 0 if not set.
286
     */
287
    public function thumbnailHeight(): int
288
    {
289
        return $this->hasThumbnail() ? $this->thumbnail['height'] : 0;
290
    }
291
292
    /**
293
     * Converts Embed instance into an array for caching.
294
     */
295
    public function toArray(): array
296
    {
297
        return [
298
            'type' => $this->type,
299
            'url' => $this->url,
300
            'data' => $this->data,
301
        ];
302
    }
303
304
    /**
305
     * Converts Embed instance into json string.
306
     */
307
    public function toJson(): string
308
    {
309
        return json_encode($this->toArray());
310
    }
311
312
    /**
313
     * Returns HtmlBuilder instance of OEmbed media provider HTML string.
314
     */
315
    protected function extractOEmbedHtml(string $html): HtmlBuilder
316
    {
317
        $script = null;
318
        $doc = new DOMDocument();
319
        $doc->loadHTML("<html><body>$html</body></html>", LIBXML_NOERROR);
320
        $body = $doc->documentElement->lastChild;
321
322
        $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

322
        /** @scrutinizer ignore-call */ 
323
        $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...
323
        foreach ($scripts as $node) {
324
            $script = $node->getAttribute('src');
325
            break;
326
        }
327
328
        if (!$body->firstChild) {
329
            throw new HtmlParsingException();
330
        }
331
332
        if ($body->firstChild && $body->firstChild->nodeName === 'iframe') {
333
            $attrs = [];
334
335
            foreach ($body->firstChild->attributes as $attribute) {
336
                $attrs[$attribute->name] = $attribute->value;
337
            }
338
339
            $attrs = array_merge($attrs, $this->getProviderHtmlOptions());
340
341
            return new HtmlBuilder(HtmlBuilder::TYPE_IFRAME, $attrs, $script);
342
        }
343
344
        return new HtmlBuilder(HtmlBuilder::TYPE_RAW, $html, $script);
345
    }
346
347
    /**
348
     * Returns HtmlBuilder instance of Regex media provider HTML string.
349
     */
350
    protected function extractRegexHtml(array $html): HtmlBuilder
351
    {
352
        $type = array_key_first($html);
353
        return new HtmlBuilder($type, $html[$type]);
354
    }
355
}
356