Completed
Push — master ( b40f7e...dd9ee2 )
by Marko
02:52
created

AframeDOMDocument::appendFormatComment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 2
crap 2
1
<?php
2
/** @formatter:off
3
 * ******************************************************************
4
 * Created by   Marko Kungla on Jun 27, 2016 - 9:55:09 PM
5
 * Contact      [email protected]
6
 * @copyright   2016 Marko Kungla - https://github.com/mkungla
7
 * @license     The MIT License (MIT)
8
 * 
9
 * @category       AframeVR
10
 * @package        aframe-php
11
 * 
12
 * Lang         PHP (php version >= 7)
13
 * Encoding     UTF-8
14
 * File         AframeDOMDocument.php
15
 * Code format  PSR-2 and 12
16
 * @link        https://github.com/mkungla/aframe-php
17
 ^ @issues      https://github.com/mkungla/aframe-php/issues
18
 * ********************************************************************
19
 * Contributors:
20
 * @author Marko Kungla <[email protected]>
21
 * ********************************************************************
22
 * Comments:
23
 * @formatter:on */
24
namespace AframeVR\Core\DOM;
25
26
use \AframeVR\Core\Config;
27
use \DOMImplementation;
28
use \DOMDocumentType;
29
use \DOMDocument;
30
use \AframeVR\Core\Entity;
31
use \AframeVR\Interfaces\AssetsInterface;
32
33
final class AframeDOMDocument extends DOMImplementation
34
{
35
36
    /**
37
     * A-Frame DOM Document type
38
     *
39
     * @var \DOMDocumentType
40
     */
41
    protected $doctypeObj;
42
43
    /**
44
     * A-Frame DOM Document
45
     *
46
     * @var \DOMDocument
47
     */
48
    protected $docObj;
49
50
    /**
51
     * Scene meta tile
52
     *
53
     * @var string $scene_title
54
     */
55
    protected $scene_title = 'Untitled';
56
57
    /**
58
     * Scene meta description
59
     *
60
     * @var string $scene_description
61
     */
62
    protected $scene_description = '';
63
64
    /**
65
     * CDN Of aframe.js
66
     *
67
     * @var string
68
     */
69
    protected $aframe_cdn;
70
71
    /**
72
     * Whether to use CDN
73
     *
74
     * @var bool $use_cdn
75
     */
76
    protected $use_cdn = false;
77
78
    /**
79
     * <head>
80
     *
81
     * @var \DOMElement
82
     */
83
    protected $head;
84
85
    /**
86
     * <body>
87
     *
88
     * @var \DOMElement
89
     */
90
    protected $body;
91
92
    /**
93
     * <a-scene>
94
     *
95
     * @var \DOMElement
96
     */
97
    protected $scene;
98
99
    /**
100
     * <a-assets>
101
     *
102
     * @var \DOMElement
103
     */
104
    protected $assets;
105
106
    /**
107
     * Nicely formats output with indentation and extra space.
108
     *
109
     * @var bool
110
     */
111
    protected $formatOutput = false;
112
113
    /**
114
     * A-Frame DOM
115
     *
116
     * @param Config $config            
117
     */
118 67
    public function __construct(Config $config)
119
    {
120
        /* Config */
121 67
        $this->formatOutput = is_bool($config->get('formatOutput')) ? $config->get('formatOutput') : false;
122 67
        $this->use_cdn = is_bool($config->get('useCDN')) ? $config->get('useCDN') : false;
123
        
124
        /* Create HTML5 Document type */
125 67
        $this->createDocType('html');
126
        /* Create A-Frame DOM Document */
127 67
        $this->createAframeDocument();
128
        /* Create <head> element */
129 67
        $this->createHead();
130
        /* Create <body> element */
131 67
        $this->createBody();
132
        /* Create <a-scene> element */
133 67
        $this->createScene();
134
        /* Create <a-assets> element */
135 67
        $this->createAssets();
136
        /* Set CDN of aframe.js */
137 67
        $this->setCDN($config->get('CDN'));
0 ignored issues
show
Bug introduced by
It seems like $config->get('CDN') targeting AframeVR\Core\Config::get() can also be of type boolean or null; however, AframeVR\Core\DOM\AframeDOMDocument::setCDN() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
138 67
    }
139
140
    /**
141
     * Set CDN for aframe.js or min.js
142
     *
143
     * @param string $cdn            
144
     * @return void
145
     */
146 67
    public function setCDN(string $cdn)
147
    {
148 67
        $this->aframe_cdn = $cdn;
149 67
    }
150
151
    /**
152
     * Render scene this DOM Object is attached to
153
     *
154
     * @return string
155
     */
156 5
    public function render(): string
157
    {
158 5
        $this->docObj->formatOutput = $this->formatOutput;
159 5
        $html = $this->docObj->getElementsByTagName('html')->item(0);
160
        /* Make sure we do not add duplicates when render is called multiple times */
161 5
        if (! $html->hasChildNodes()) {
162 5
            $this->renderHead();
163 5
            $html->appendChild($this->head);
164
            
165 5
            $this->renderBody();
166 5
            $html->appendChild($this->body);
167
        }
168 5
        return $this->formatOutput ? $this->correctOutputFormat($this->docObj->saveHTML()) : $this->docObj->saveHTML();
169
    }
170
171
    /**
172
     * Set Scene meta title
173
     *
174
     * @param string $title            
175
     */
176 7
    public function setTitle(string $title)
177
    {
178 7
        $this->scene_title = $title;
179 7
    }
180
181
    /**
182
     * Set Scene meta description
183
     *
184
     * @param string $description            
185
     */
186 7
    public function setDescription(string $description)
187
    {
188 7
        $this->scene_description = $description;
189 7
    }
190
191
    /**
192
     * Append entities
193
     *
194
     * @param array $entities            
195
     * @return void
196
     */
197 7
    public function appendEntities(array $entities)
198
    {
199 7
        if (! empty($entities)) {
200 2
            foreach ($entities as $entity) {
201 2
                $this->appendEntity($entity);
202
            }
203
        }
204 7
    }
205
206
    /**
207
     * Append assets
208
     *
209
     * @param array $assets            
210
     * @return void
211
     */
212 2
    public function appendAssets(array $assets)
213
    {
214 2
        if (! empty($assets)) {
215 2
            if ($this->formatOutput) {
216 2
                $com = $this->docObj->createComment('');
217 2
                $this->scene->appendChild($com);
218
            }
219 2
            foreach ($assets as $asset) {
220 2
                $this->appendAsset($asset);
221
            }
222 2
            $this->scene->appendChild($this->assets);
223
        }
224 2
    }
225
226
    /**
227
     * Append asset
228
     *
229
     * Create asset DOMElement
230
     *
231
     * @param AssetsInterface $asset            
232
     */
233 2
    public function appendAsset(AssetsInterface $asset)
234
    {
235 2
        $this->appendFormatComment('assets', "\n\t");
236 2
        $this->assets->appendChild($asset->DOMElement($this->docObj));
237 2
    }
238
239
    /**
240
     * Create entity DOMElement
241
     *
242
     * Created entity and append it to scene
243
     *
244
     * @param Entity $entity            
245
     * @return void
246
     */
247 2
    public function appendEntity(Entity $entity)
248
    {
249 2
        $this->appendFormatComment('scene', "\n");
250 2
        $this->scene->appendChild($entity->DOMElement($this->docObj));
251 2
    }
252
253
    /**
254
     * Get HTML of Scene only
255
     *
256
     * @return string
257
     */
258 3
    public function renderSceneOnly()
259
    {
260 3
        $html = new DOMDocument();
261 3
        $html->formatOutput = $this->formatOutput;
262 3
        $html_scene = $html->importNode($this->scene, true);
263 3
        $html->appendChild($html_scene);
264 3
        return $this->formatOutput ? $this->correctOutputFormat($html->saveHTML()) : $html->saveHTML();
265
    }
266
267
    /**
268
     * Add document comment for formatting
269
     *
270
     * @param string $element            
271
     * @param string $comment            
272
     */
273 4
    protected function appendFormatComment(string $element, string $comment)
274
    {
275 4
        if ($this->formatOutput) {
276 4
            $com = $this->docObj->createComment($comment);
277 4
            $this->{$element}->appendChild($com);
278
        }
279 4
    }
280
281
    /**
282
     * Correct html format for tags which are not supported by DOMDocument
283
     *
284
     * @param string $html            
285
     * @return string
286
     */
287 4
    protected function correctOutputFormat($html)
288
    {
289
        $tags = array(
290 4
            '<!--',
291
            '-->',
292
            '<a-assets>',
293
            '</a-assets>',
294
            '</a-scene>'
295
        );
296
        $values = array(
297 4
            '',
298
            "\t",
299
            "\n\t<a-assets>",
300
            "\n\t</a-assets>",
301
            "\n</a-scene>"
302
        );
303 4
        return str_ireplace($tags, $values, $html);
304
    }
305
306
    /**
307
     * Prepeare head
308
     *
309
     * @return void
310
     */
311 5
    protected function renderHead()
312
    {
313 5
        $this->appendTitle();
314 5
        $this->appendDefaultMetaTags();
315 5
        $this->appendCDN();
316 5
    }
317
318
    /**
319
     * Append deffault metatags
320
     *
321
     * @return void
322
     */
323 5
    protected function appendDefaultMetaTags()
324
    {
325 5
        foreach ($this->getDefaultMetaTags() as $tag)
326 5
            $this->appendMetaTag($tag);
327 5
    }
328
329
    /**
330
     * Get default meta tags
331
     *
332
     * @return array
333
     */
334 5
    protected function getDefaultMetaTags(): array
335
    {
336
        return array(
337
            array(
338 5
                'name' => 'description',
339 5
                'content' => $this->scene_description
340 5
            ),
341
            array(
342
                'charset' => 'utf-8'
343
            ),
344
            array(
345
                'name' => 'viewport',
346
                'content' => 'width=device-width,initial-scale=1,maximum-scale=1,shrink-to-fit=no,user-scalable=no,minimal-ui'
347
            ),
348
            array(
349
                'name' => 'mobile-web-app-capable',
350
                'content' => 'yes'
351
            ),
352
            array(
353
                'name' => 'theme-color',
354
                'content' => 'black'
355
            )
356
        );
357
    }
358
359
    /**
360
     * If requested by user use aframe CDN
361
     *
362
     * @return void
363
     */
364 5
    protected function appendCDN()
365
    {
366 5
        if ($this->use_cdn) {
367 3
            $cdn_script = $this->docObj->createElement('script');
368 3
            $cdn_script->setAttribute('src', $this->aframe_cdn);
369 3
            $this->head->appendChild($cdn_script);
370
        }
371 5
    }
372
373
    /**
374
     * Prepare body
375
     *
376
     * @return void
377
     */
378 5
    protected function renderBody()
379
    {
380 5
        $this->body->appendChild($this->scene);
381 5
    }
382
383
    /**
384
     * Create meta tags
385
     *
386
     * @param array $attr            
387
     */
388 5
    protected function appendMetaTag(array $attr)
389
    {
390 5
        $metatag = $this->docObj->createElement('meta');
391 5
        foreach ($attr as $key => $val)
392 5
            $metatag->setAttribute($key, $val);
393 5
        $this->head->appendChild($metatag);
394 5
    }
395
396
    /**
397
     * Create title tag
398
     *
399
     * @return void
400
     */
401 5
    protected function appendTitle()
402
    {
403 5
        $title = $this->docObj->createElement('title', $this->scene_title);
404 5
        $this->head->appendChild($title);
405 5
    }
406
407
    /**
408
     * Creates an empty DOMDocumentType object
409
     *
410
     * @param string $doctype            
411
     * @return void
412
     */
413 67
    protected function createDocType(string $doctype)
414
    {
415 67
        $this->doctypeObj = $this->createDocumentType($doctype);
416 67
    }
417
418
    /**
419
     * Creates a DOMDocument object of the specified type with its document element
420
     *
421
     * @return void
422
     */
423 67
    protected function createAframeDocument()
424
    {
425 67
        $this->docObj = $this->createDocument(null, 'html', $this->doctypeObj);
426 67
    }
427
428
    /**
429
     * Create <head> element node
430
     *
431
     * @return void
432
     */
433 67
    protected function createHead()
434
    {
435 67
        $this->head = $this->docObj->createElement('head');
436 67
    }
437
438
    /**
439
     * Create <body> element node
440
     *
441
     * @return void
442
     */
443 67
    protected function createBody()
444
    {
445 67
        $this->body = $this->docObj->createElement('body', $this->formatOutput ? "\n" : '');
446 67
    }
447
448
    /**
449
     * Create <a-scene> element node
450
     *
451
     * @return void
452
     */
453 67
    protected function createScene()
454
    {
455 67
        $this->scene = $this->docObj->createElement('a-scene');
456 67
    }
457
458
    /**
459
     * Create <a-scene> element node
460
     *
461
     * @return void
462
     */
463 67
    protected function createAssets()
464
    {
465 67
        $this->assets = $this->docObj->createElement('a-assets');
466 67
    }
467
}
468