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

AframeDOMDocument::useCDN()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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 = $config->get('formatOutput');
0 ignored issues
show
Documentation Bug introduced by
It seems like $config->get('formatOutput') can also be of type string. However, the property $formatOutput is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
122 67
        $this->use_cdn = $config->get('useCDN');
0 ignored issues
show
Documentation Bug introduced by
It seems like $config->get('useCDN') can also be of type string. However, the property $use_cdn is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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'));
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
     * @param AssetsInterface $asset            
230
     */
231 2 View Code Duplication
    public function appendAsset(AssetsInterface $asset)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
232
    {
233
        /* Create asset DOMElement */
234 2
        if ($this->formatOutput) {
235 2
            $com = $this->docObj->createComment("\n\t");
236 2
            $this->assets->appendChild($com);
237
        }
238 2
        $this->assets->appendChild($asset->DOMElement($this->docObj));
239 2
    }
240
241
    /**
242
     * Add entity
243
     *
244
     * @param Entity $entity            
245
     * @return void
246
     */
247 2 View Code Duplication
    public function appendEntity(Entity $entity)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248
    {
249
        /* Create entity DOMElement */
250 2
        if ($this->formatOutput) {
251 2
            $com = $this->docObj->createComment("\n");
252 2
            $this->scene->appendChild($com);
253
        }
254 2
        $this->scene->appendChild($entity->DOMElement($this->docObj));
255 2
    }
256
257
    /**
258
     * Get HTML of Scene only
259
     *
260
     * @return string
261
     */
262 3
    public function renderSceneOnly()
263
    {
264 3
        $html = new DOMDocument();
265 3
        $html->formatOutput = $this->formatOutput;
266 3
        $html_scene = $html->importNode($this->scene, true);
267 3
        $html->appendChild($html_scene);
268 3
        return $this->formatOutput ? $this->correctOutputFormat($html->saveHTML()) : $html->saveHTML();
269
    }
270
271
    /**
272
     * Correct html format for tags which are not supported by DOMDocument
273
     *
274
     * @param string $html            
275
     * @return string
276
     */
277 4
    protected function correctOutputFormat($html)
278
    {
279
        $tags = array(
280 4
            '<!--',
281
            '-->',
282
            '<a-assets>',
283
            '</a-assets>',
284
            '</a-scene>'
285
        );
286
        $values = array(
287 4
            '',
288
            "\t",
289
            "\n\t<a-assets>",
290
            "\n\t</a-assets>",
291
            "\n</a-scene>"
292
        );
293 4
        return str_ireplace($tags, $values, $html);
294
    }
295
296
    /**
297
     * Prepeare head
298
     *
299
     * @return void
300
     */
301 5
    protected function renderHead()
302
    {
303 5
        $this->appendTitle();
304 5
        $this->appendDefaultMetaTags();
305 5
        $this->appendCDN();
306 5
    }
307
308
    /**
309
     * Append deffault metatags
310
     *
311
     * @return void
312
     */
313 5
    protected function appendDefaultMetaTags()
314
    {
315 5
        foreach ($this->getDefaultMetaTags() as $tag)
316 5
            $this->appendMetaTag($tag);
317 5
    }
318
319
    /**
320
     * Get default meta tags
321
     *
322
     * @return array
323
     */
324 5
    protected function getDefaultMetaTags(): array
325
    {
326
        return array(
327
            array(
328 5
                'name' => 'description',
329 5
                'content' => $this->scene_description
330 5
            ),
331
            array(
332
                'charset' => 'utf-8'
333
            ),
334
            array(
335
                'name' => 'viewport',
336
                'content' => 'width=device-width,initial-scale=1,maximum-scale=1,shrink-to-fit=no,user-scalable=no,minimal-ui'
337
            ),
338
            array(
339
                'name' => 'mobile-web-app-capable',
340
                'content' => 'yes'
341
            ),
342
            array(
343
                'name' => 'theme-color',
344
                'content' => 'black'
345
            )
346
        );
347
    }
348
349
    /**
350
     * If requested by user use aframe CDN
351
     *
352
     * @return void
353
     */
354 5
    protected function appendCDN()
355
    {
356 5
        if ($this->use_cdn) {
357 3
            $cdn_script = $this->docObj->createElement('script');
358 3
            $cdn_script->setAttribute('src', $this->aframe_cdn);
359 3
            $this->head->appendChild($cdn_script);
360
        }
361 5
    }
362
363
    /**
364
     * Prepare body
365
     *
366
     * @return void
367
     */
368 5
    protected function renderBody()
369
    {
370 5
        $this->body->appendChild($this->scene);
371 5
    }
372
373
    /**
374
     * Create meta tags
375
     *
376
     * @param array $attr            
377
     */
378 5
    protected function appendMetaTag(array $attr)
379
    {
380 5
        $metatag = $this->docObj->createElement('meta');
381 5
        foreach ($attr as $key => $val)
382 5
            $metatag->setAttribute($key, $val);
383 5
        $this->head->appendChild($metatag);
384 5
    }
385
386
    /**
387
     * Create title tag
388
     *
389
     * @return void
390
     */
391 5
    protected function appendTitle()
392
    {
393 5
        $title = $this->docObj->createElement('title', $this->scene_title);
394 5
        $this->head->appendChild($title);
395 5
    }
396
397
    /**
398
     * Creates an empty DOMDocumentType object
399
     *
400
     * @param string $doctype            
401
     * @return void
402
     */
403 67
    protected function createDocType(string $doctype)
404
    {
405 67
        $this->doctypeObj = $this->createDocumentType($doctype);
406 67
    }
407
408
    /**
409
     * Creates a DOMDocument object of the specified type with its document element
410
     *
411
     * @return void
412
     */
413 67
    protected function createAframeDocument()
414
    {
415 67
        $this->docObj = $this->createDocument(null, 'html', $this->doctypeObj);
416 67
    }
417
418
    /**
419
     * Create <head> element node
420
     *
421
     * @return void
422
     */
423 67
    protected function createHead()
424
    {
425 67
        $this->head = $this->docObj->createElement('head');
426 67
    }
427
428
    /**
429
     * Create <body> element node
430
     *
431
     * @return void
432
     */
433 67
    protected function createBody()
434
    {
435 67
        $this->body = $this->docObj->createElement('body', $this->formatOutput ? "\n" : '');
436 67
    }
437
438
    /**
439
     * Create <a-scene> element node
440
     *
441
     * @return void
442
     */
443 67
    protected function createScene()
444
    {
445 67
        $this->scene = $this->docObj->createElement('a-scene');
446 67
    }
447
448
    /**
449
     * Create <a-scene> element node
450
     *
451
     * @return void
452
     */
453 67
    protected function createAssets()
454
    {
455 67
        $this->assets = $this->docObj->createElement('a-assets');
456 67
    }
457
}
458