Passed
Push — master ( b9b823...902e2b )
by Kane
11:52
created

Embed   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 357
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 45
eloc 86
c 2
b 0
f 1
dl 0
loc 357
rs 8.8

29 Methods

Rating   Name   Duplication   Size   Complexity  
A setOptions() 0 4 1
A getProviderHtmlOptions() 0 7 2
A setAmp() 0 4 1
A getOptions() 0 3 1
A getProviderOptions() 0 7 2
A initData() 0 19 3
A htmlBuilder() 0 3 1
A __construct() 0 14 1
A width() 0 3 1
A height() 0 3 1
A extractRegexHtml() 0 4 1
A ampHtml() 0 3 1
A url() 0 3 1
A hasThumbnail() 0 3 1
A thumbnailUrl() 0 3 2
A data() 0 5 1
A toJson() 0 3 1
A html() 0 9 2
A script() 0 3 1
A thumbnail() 0 3 1
A src() 0 5 1
A thumbnailHeight() 0 3 2
B extractOEmbedHtml() 0 29 7
A toArray() 0 6 1
A thumbnailWidth() 0 3 2
A mediaType() 0 3 1
A ratio() 0 7 3
A type() 0 3 1
A htmlType() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Embed often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Embed, and based on these observations, apply Extract Interface, too.

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
     * AMP flag.
25
     */
26
    protected bool $amp;
27
28
    /**
29
     * Original media URL.
30
     */
31
    protected string $url;
32
33
    /**
34
     * Embed data extracted via OEmbed or Regex extractors.
35
     */
36
    protected array $data;
37
38
    /**
39
     * Class containing Embed HTML code.
40
     */
41
    protected HtmlBuilder $html;
42
43
    /**
44
     * Thumbnail data if available.
45
     */
46
    protected ?array $thumbnail;
47
48
    /**
49
     * Creates Embed instance.
50
     */
51
    public function __construct(
52
        string $type,
53
        string $url,
54
        array $data,
55
        array $options = [],
56
        bool $amp = false
57
    ) {
58
        $this->type = $type;
59
        $this->url = $url;
60
        $this->data = $data;
61
        $this->options = $options;
62
        $this->amp = $amp;
63
64
        $this->initData();
65
    }
66
67
    /**
68
     * Initializer for the Embed object filling thumbnail, html and type based
69
     * on a given media provider data.
70
     */
71
    public function initData(): self
72
    {
73
        $data = $this->data();
74
75
        if (isset($data['thumbnail_url'])) {
76
            $this->thumbnail = [
77
                'url' => $data['thumbnail_url'],
78
                'width' => $data['thumbnail_width'] ?? null,
79
                'height' => $data['thumbnail_height'] ?? null,
80
            ];
81
        }
82
83
        if ($this->type == self::TYPE_OEMBED) {
84
            $this->html = $this->extractOEmbedHtml($data['html']);
85
        } else {
86
            $this->html = $this->extractRegexHtml($data['html']);
87
        }
88
89
        return $this;
90
    }
91
92
    /**
93
     * Returns mbed options.
94
     */
95
    public function getOptions(): array
96
    {
97
        return $this->options;
98
    }
99
100
    /**
101
     * Sets new embed options. See config file 'options' key.
102
     */
103
    public function setOptions(array $options): self
104
    {
105
        $this->options = $options;
106
        return $this;
107
    }
108
109
    /**
110
     * Returns provider-specific data options.
111
    */
112
    public function getProviderOptions(): array
113
    {
114
        if (isset($this->options['providers'])) {
115
            return $this->options['providers'][$this->data['provider_name']]['data'] ?? [];
116
        }
117
118
        return [];
119
    }
120
121
    /**
122
     * Returns provider-specific HTML options.
123
    */
124
    public function getProviderHtmlOptions(): array
125
    {
126
        if (isset($this->options['providers'])) {
127
            return $this->options['providers'][$this->data['provider_name']]['html'] ?? [];
128
        }
129
130
        return [];
131
    }
132
133
    /**
134
     * Sets AMP mode.
135
     */
136
    public function setAmp(bool $amp): self
137
    {
138
        $this->amp = $amp;
139
        return $this;
140
    }
141
142
    /**
143
     * Returns current HtmlBuilder instance.
144
     */
145
    public function htmlBuilder(): HtmlBuilder
146
    {
147
        return $this->html;
148
    }
149
150
    /**
151
     * Returns string with HTML to embed in application. Will return AMP-friendly
152
     * HTML if global amp mode is enabled and not overwitten.
153
     */
154
    public function html(array $options = null, ?bool $amp = null): string
155
    {
156
        $options = array_merge($this->options, $options ?? []);
157
158
        if (is_null($amp)) {
159
            $amp = $this->amp;
160
        }
161
162
        return $this->html->html($options, $amp);
163
    }
164
165
    /**
166
     * Returns string with AMP-friendly HTML to embed in an application.
167
     */
168
    public function ampHtml(array $options = null): string
169
    {
170
        return $this->html($options, true);
171
    }
172
173
    /**
174
     * Returns URL of a resulting embed object.
175
     */
176
    public function src(array $options = null): string | array | null
0 ignored issues
show
Bug introduced by
The type Cohensive\OEmbed\null was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
177
    {
178
        $options = array_merge($this->options, $options ?? []);
179
180
        return $this->html->src($options);
181
    }
182
183
    /**
184
     * Return script source if available in embed html.
185
     */
186
    public function script(): ?string
187
    {
188
        return $this->html->script();
189
    }
190
191
    /**
192
     * Returns embed provider type.
193
     */
194
    public function type(): int
195
    {
196
        return $this->type;
197
    }
198
199
    /**
200
     * Returns embed html type.
201
     */
202
    public function htmlType(): string
203
    {
204
        return $this->html->type();
205
    }
206
207
    /**
208
     * Returns media provider data.
209
     */
210
    public function url(): string
211
    {
212
        return $this->url;
213
    }
214
215
    /**
216
     * Returns media provider data with applied options if set.
217
     */
218
    public function data(): array
219
    {
220
        $data = array_merge($this->data, $this->getProviderOptions());
221
222
        return $data;
223
    }
224
225
    /**
226
     * Returns width of the embed. 0 if not set.
227
     */
228
    public function width(): int
229
    {
230
        return $this->data()['width'] ?? 0;
231
    }
232
233
    /**
234
     * Returns height of the embed. 0 if not set.
235
     */
236
    public function height(): int
237
    {
238
        return $this->data()['height'] ?? 0;
239
    }
240
241
    /**
242
     * Returns width/height ratio of the embed. 0 if dimensions are not set.
243
     */
244
    public function ratio(): float
245
    {
246
        if (isset($this->data()['width']) && isset($this->data()['height'])) {
247
            return $this->data()['width'] / $this->data()['height'];
248
        }
249
250
        return 0;
251
    }
252
253
    /**
254
     * Returns string describing media type. According to OEmbed spec it could be:
255
     * one of these: photo, video, link, rich
256
     */
257
    public function mediaType(): string
258
    {
259
        return $this->data['type'];
260
    }
261
262
    /**
263
     * Returns boolean flag telling if given embed data has a thumbnail.
264
     */
265
    public function hasThumbnail(): bool
266
    {
267
        return is_array($this->thumbnail);
268
    }
269
270
    /**
271
     * Returns thumbnail data in an array form containing url and its dimensions.
272
     *
273
     */
274
    public function thumbnail(): ?array
275
    {
276
        return $this->thumbnail;
277
    }
278
279
    /**
280
     * Reutrns string for thumbnail or null if it's not set.
281
     */
282
    public function thumbnailUrl(): ?string
283
    {
284
        return $this->hasThumbnail() ? $this->thumbnail['url'] : null;
285
    }
286
287
    /**
288
     * Return thumbnail width or 0 if not set.
289
     */
290
    public function thumbnailWidth(): int
291
    {
292
        return $this->hasThumbnail() ? $this->thumbnail['width'] : 0;
293
    }
294
295
    /**
296
     * Return thumbnail height or 0 if not set.
297
     */
298
    public function thumbnailHeight(): int
299
    {
300
        return $this->hasThumbnail() ? $this->thumbnail['height'] : 0;
301
    }
302
303
    /**
304
     * Converts Embed instance into an array for caching.
305
     */
306
    public function toArray(): array
307
    {
308
        return [
309
            'type' => $this->type,
310
            'url' => $this->url,
311
            'data' => $this->data,
312
        ];
313
    }
314
315
    /**
316
     * Converts Embed instance into json string.
317
     */
318
    public function toJson(): string
319
    {
320
        return json_encode($this->toArray());
321
    }
322
323
    /**
324
     * Returns HtmlBuilder instance of OEmbed media provider HTML string.
325
     */
326
    protected function extractOEmbedHtml(string $html): HtmlBuilder
327
    {
328
        $script = null;
329
        $doc = new DOMDocument();
330
        $doc->loadHTML("<html><body>$html</body></html>", LIBXML_NOERROR);
331
        $body = $doc->documentElement->lastChild;
332
333
        if (!$body || ($body && !$body->firstChild)) {
334
            throw new HtmlParsingException();
335
        }
336
337
        foreach ($body->getElementsByTagName('script') as $node) {
338
            $script = $node->getAttribute('src');
339
            break;
340
        }
341
342
        if ($body->firstChild->nodeName === 'iframe') {
343
            $attrs = [];
344
345
            foreach ($body->firstChild->attributes as $attribute) {
346
                $attrs[$attribute->name] = $attribute->value;
347
            }
348
349
            $attrs = array_merge($attrs, $this->getProviderHtmlOptions());
350
351
            return new HtmlBuilder(HtmlBuilder::TYPE_IFRAME, $attrs, $script);
352
        }
353
354
        return new HtmlBuilder(HtmlBuilder::TYPE_RAW, $html, $script);
355
    }
356
357
    /**
358
     * Returns HtmlBuilder instance of Regex media provider HTML string.
359
     */
360
    protected function extractRegexHtml(array $html): HtmlBuilder
361
    {
362
        $type = array_key_first($html);
363
        return new HtmlBuilder($type, $html[$type]);
364
    }
365
}
366