Passed
Branch master (d27d35)
by Kane
13:10
created

Embed::htmlBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

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

262
        /** @scrutinizer ignore-unhandled */ @$doc->loadHTML("<html><body>$html</body></html>");

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
263
        $body = $doc->documentElement->lastChild;
264
265
        $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

265
        /** @scrutinizer ignore-call */ 
266
        $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...
266
        foreach ($scripts as $node) {
267
            $script = $node->getAttribute('src');
268
            break;
269
        }
270
271
        if ($body->firstChild->nodeName === 'iframe') {
272
            $attrs = [];
273
274
            foreach ($body->firstChild->attributes as $attribute) {
275
                $attrs[$attribute->name] = $attribute->value;
276
            }
277
278
            return new HtmlBuilder(HtmlBuilder::TYPE_IFRAME, $attrs, $script);
279
        }
280
281
        return new HtmlBuilder(HtmlBuilder::TYPE_RAW, $html, $script);
282
    }
283
284
    /**
285
     * Returns HtmlBuilder instance of Regex media provider HTML string.
286
     */
287
    protected function extractRegexHtml(array $html): HtmlBuilder
288
    {
289
        $type = array_key_first($html);
290
        return new HtmlBuilder($type, $html[$type]);
291
    }
292
}
293