xref: /plugin/combo/ComboStrap/Snippet.php (revision 4cadd4f8c541149bdda95f080e38a6d4e3a640ca)
137748cd8SNickeau<?php
237748cd8SNickeau/**
337748cd8SNickeau * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved.
437748cd8SNickeau *
537748cd8SNickeau * This source code is licensed under the GPL license found in the
637748cd8SNickeau * COPYING  file in the root directory of this source tree.
737748cd8SNickeau *
837748cd8SNickeau * @license  GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
937748cd8SNickeau * @author   ComboStrap <support@combostrap.com>
1037748cd8SNickeau *
1137748cd8SNickeau */
1237748cd8SNickeau
1337748cd8SNickeaunamespace ComboStrap;
1437748cd8SNickeau
1537748cd8SNickeau
16c3437056SNickeauuse JsonSerializable;
17c3437056SNickeau
18*4cadd4f8SNickeau/**
19*4cadd4f8SNickeau * Class Snippet
20*4cadd4f8SNickeau * @package ComboStrap
21*4cadd4f8SNickeau * A HTML tag:
22*4cadd4f8SNickeau *   * CSS: link for href or style with content
23*4cadd4f8SNickeau *   * Javascript: script
24*4cadd4f8SNickeau *
25*4cadd4f8SNickeau * A component to manage the extra HTML that
26*4cadd4f8SNickeau * comes from components and that should come in the head HTML node
27*4cadd4f8SNickeau *
28*4cadd4f8SNickeau */
29c3437056SNickeauclass Snippet implements JsonSerializable
3037748cd8SNickeau{
3137748cd8SNickeau    /**
3237748cd8SNickeau     * The head in css format
3337748cd8SNickeau     * We need to add the style node
3437748cd8SNickeau     */
35*4cadd4f8SNickeau    const EXTENSION_CSS = "css";
3637748cd8SNickeau    /**
3737748cd8SNickeau     * The head in javascript
3837748cd8SNickeau     * We need to wrap it in a script node
3937748cd8SNickeau     */
40*4cadd4f8SNickeau    const EXTENSION_JS = "js";
41*4cadd4f8SNickeau
4237748cd8SNickeau    /**
43*4cadd4f8SNickeau     * Properties of the JSON array
4437748cd8SNickeau     */
45*4cadd4f8SNickeau    const JSON_TYPE_PROPERTY = "type"; // mandatory
46*4cadd4f8SNickeau    const JSON_COMPONENT_PROPERTY = "component"; // mandatory
47*4cadd4f8SNickeau    const JSON_EXTENSION_PROPERTY = "extension"; // mandatory
48*4cadd4f8SNickeau    const JSON_URL_PROPERTY = "url"; // mandatory if external
49c3437056SNickeau    const JSON_CRITICAL_PROPERTY = "critical";
50*4cadd4f8SNickeau    const JSON_ASYNC_PROPERTY = "async";
51c3437056SNickeau    const JSON_CONTENT_PROPERTY = "content";
52*4cadd4f8SNickeau    const JSON_INTEGRITY_PROPERTY = "integrity";
53*4cadd4f8SNickeau    const JSON_HTML_ATTRIBUTES_PROPERTY = "attributes";
54*4cadd4f8SNickeau
55*4cadd4f8SNickeau    /**
56*4cadd4f8SNickeau     * The type identifier for a script snippet
57*4cadd4f8SNickeau     * (ie inline javascript or style)
58*4cadd4f8SNickeau     *
59*4cadd4f8SNickeau     * To make the difference with library
60*4cadd4f8SNickeau     * that have already an identifier with the url value
61*4cadd4f8SNickeau     * (ie external)
62*4cadd4f8SNickeau     */
63*4cadd4f8SNickeau    const INTERNAL_TYPE = "internal";
64*4cadd4f8SNickeau    const EXTERNAL_TYPE = "external";
65*4cadd4f8SNickeau
66*4cadd4f8SNickeau    /**
67*4cadd4f8SNickeau     * When a snippet is scoped to the request
68*4cadd4f8SNickeau     * (ie not saved with a slot)
69*4cadd4f8SNickeau     *
70*4cadd4f8SNickeau     * They are unique on a request scope
71*4cadd4f8SNickeau     *
72*4cadd4f8SNickeau     * TlDR: The snippet does not depends to a slot and cannot therefore be cached along.
73*4cadd4f8SNickeau     *
74*4cadd4f8SNickeau     * The code that adds this snippet is not created by the parsing of content
75*4cadd4f8SNickeau     * or depends on the page.
76*4cadd4f8SNickeau     *
77*4cadd4f8SNickeau     * It's always called and add the snippet whatsoever.
78*4cadd4f8SNickeau     * Generally, this is an action plugin with a `TPL_METAHEADER_OUTPUT` hook
79*4cadd4f8SNickeau     * such as {@link Bootstrap}, {@link HistoricalBreadcrumbMenuItem},
80*4cadd4f8SNickeau     * ,...
81*4cadd4f8SNickeau     */
82*4cadd4f8SNickeau    const REQUEST_SLOT = "request";
83*4cadd4f8SNickeau
84*4cadd4f8SNickeau
85*4cadd4f8SNickeau    protected static $globalSnippets;
8637748cd8SNickeau
8737748cd8SNickeau    private $snippetId;
88*4cadd4f8SNickeau    private $extension;
8937748cd8SNickeau
9037748cd8SNickeau    /**
9137748cd8SNickeau     * @var bool
9237748cd8SNickeau     */
93c3437056SNickeau    private $critical;
9437748cd8SNickeau
9537748cd8SNickeau    /**
9637748cd8SNickeau     * @var string the text script / style (may be null if it's an external resources)
9737748cd8SNickeau     */
98*4cadd4f8SNickeau    private $inlineContent;
99*4cadd4f8SNickeau
10037748cd8SNickeau    /**
101*4cadd4f8SNickeau     * @var string
10237748cd8SNickeau     */
103*4cadd4f8SNickeau    private $url;
104*4cadd4f8SNickeau    /**
105*4cadd4f8SNickeau     * @var string
106*4cadd4f8SNickeau     */
107*4cadd4f8SNickeau    private $integrity;
108*4cadd4f8SNickeau    /**
109*4cadd4f8SNickeau     * @var array Extra html attributes if needed
110*4cadd4f8SNickeau     */
111*4cadd4f8SNickeau    private $htmlAttributes;
112*4cadd4f8SNickeau
113*4cadd4f8SNickeau    /**
114*4cadd4f8SNickeau     * @var string ie internal or external
115*4cadd4f8SNickeau     */
116*4cadd4f8SNickeau    private $type;
117*4cadd4f8SNickeau    /**
118*4cadd4f8SNickeau     * @var string The name of the component (used for internal style sheet to retrieve the file)
119*4cadd4f8SNickeau     */
120*4cadd4f8SNickeau    private $componentName;
121*4cadd4f8SNickeau
122*4cadd4f8SNickeau    /**
123*4cadd4f8SNickeau     * @var array - the slots that needs this snippet (as key to get only one snippet by scope)
124*4cadd4f8SNickeau     * A special slot exists for {@link Snippet::REQUEST_SLOT}
125*4cadd4f8SNickeau     * where a snippet is for the whole requested page
126*4cadd4f8SNickeau     *
127*4cadd4f8SNickeau     * It's also used in the cache because not all bars
128*4cadd4f8SNickeau     * may render at the same time due to the other been cached.
129*4cadd4f8SNickeau     *
130*4cadd4f8SNickeau     * There is two scope:
131*4cadd4f8SNickeau     *   * a slot - cached along the HTML
132*4cadd4f8SNickeau     *   * or  {@link Snippet::REQUEST_SLOT} - never cached
133*4cadd4f8SNickeau     */
134*4cadd4f8SNickeau    private $slots;
135*4cadd4f8SNickeau    /**
136*4cadd4f8SNickeau     * @var bool run as soon as possible
137*4cadd4f8SNickeau     */
138*4cadd4f8SNickeau    private $async;
13937748cd8SNickeau
14037748cd8SNickeau    /**
14137748cd8SNickeau     * Snippet constructor.
14237748cd8SNickeau     */
143*4cadd4f8SNickeau    public function __construct($snippetId, $mime, $type, $url, $componentId)
14437748cd8SNickeau    {
14537748cd8SNickeau        $this->snippetId = $snippetId;
146*4cadd4f8SNickeau        $this->extension = $mime;
147*4cadd4f8SNickeau        $this->type = $type;
148*4cadd4f8SNickeau        $this->url = $url;
149*4cadd4f8SNickeau        $this->componentName = $componentId;
15037748cd8SNickeau    }
15137748cd8SNickeau
152*4cadd4f8SNickeau
153*4cadd4f8SNickeau    public static function createInternalCssSnippet($componentId): Snippet
15437748cd8SNickeau    {
155*4cadd4f8SNickeau        return self::getOrCreateSnippet(self::INTERNAL_TYPE, self::EXTENSION_CSS, $componentId);
15637748cd8SNickeau    }
15737748cd8SNickeau
15837748cd8SNickeau
15937748cd8SNickeau    /**
160*4cadd4f8SNickeau     * @param $componentId
16137748cd8SNickeau     * @return Snippet
162c3437056SNickeau     * @deprecated You should create a snippet with a known type, this constructor was created for refactoring
16337748cd8SNickeau     */
164*4cadd4f8SNickeau    public static function createUnknownSnippet($componentId): Snippet
16537748cd8SNickeau    {
166*4cadd4f8SNickeau        return new Snippet("unknown", "unknwon", "unknwon", "unknwon", $componentId);
167*4cadd4f8SNickeau    }
168*4cadd4f8SNickeau
169*4cadd4f8SNickeau    public static function &getOrCreateSnippet(string $identifier, string $extension, string $componentId): Snippet
170*4cadd4f8SNickeau    {
171*4cadd4f8SNickeau
172*4cadd4f8SNickeau        /**
173*4cadd4f8SNickeau         * The snippet id is the url for external resources (ie external javascript / stylesheet)
174*4cadd4f8SNickeau         * otherwise if it's internal, it's the component id and it's type
175*4cadd4f8SNickeau         * @param string $componentId
176*4cadd4f8SNickeau         * @param string $identifier
177*4cadd4f8SNickeau         * @return string
178*4cadd4f8SNickeau         */
179*4cadd4f8SNickeau        if ($identifier === Snippet::INTERNAL_TYPE) {
180*4cadd4f8SNickeau            $snippetId = $identifier . "-" . $extension . "-" . $componentId;
181*4cadd4f8SNickeau            $type = self::INTERNAL_TYPE;
182*4cadd4f8SNickeau            $url = null;
183*4cadd4f8SNickeau        } else {
184*4cadd4f8SNickeau            $type = self::EXTERNAL_TYPE;
185*4cadd4f8SNickeau            $snippetId = $identifier;
186*4cadd4f8SNickeau            $url = $identifier;
187*4cadd4f8SNickeau        }
188*4cadd4f8SNickeau        $requestedPageId = PluginUtility::getRequestedWikiId();
189*4cadd4f8SNickeau        if ($requestedPageId === null) {
190*4cadd4f8SNickeau            if (PluginUtility::isTest()) {
191*4cadd4f8SNickeau                $requestedPageId = "test-id";
192*4cadd4f8SNickeau            } else {
193*4cadd4f8SNickeau                $requestedPageId = "unknown";
194*4cadd4f8SNickeau                LogUtility::msg("The requested id is unknown. We couldn't scope the snippets.");
195*4cadd4f8SNickeau            }
196*4cadd4f8SNickeau        }
197*4cadd4f8SNickeau        $snippets = &self::$globalSnippets[$requestedPageId];
198*4cadd4f8SNickeau        if ($snippets === null) {
199*4cadd4f8SNickeau            self::$globalSnippets = null;
200*4cadd4f8SNickeau            self::$globalSnippets[$requestedPageId] = [];
201*4cadd4f8SNickeau            $snippets = &self::$globalSnippets[$requestedPageId];
202*4cadd4f8SNickeau        }
203*4cadd4f8SNickeau        $snippet = &$snippets[$snippetId];
204*4cadd4f8SNickeau        if ($snippet === null) {
205*4cadd4f8SNickeau            $snippets[$snippetId] = new Snippet($snippetId, $extension, $type, $url, $componentId);
206*4cadd4f8SNickeau            $snippet = &$snippets[$snippetId];
207*4cadd4f8SNickeau        }
208*4cadd4f8SNickeau        return $snippet;
209*4cadd4f8SNickeau
210*4cadd4f8SNickeau    }
211*4cadd4f8SNickeau
212*4cadd4f8SNickeau    public static function reset()
213*4cadd4f8SNickeau    {
214*4cadd4f8SNickeau        self::$globalSnippets = null;
215*4cadd4f8SNickeau    }
216*4cadd4f8SNickeau
217*4cadd4f8SNickeau    /**
218*4cadd4f8SNickeau     * @return Snippet[]|null
219*4cadd4f8SNickeau     */
220*4cadd4f8SNickeau    public static function getSnippets(): ?array
221*4cadd4f8SNickeau    {
222*4cadd4f8SNickeau        if (self::$globalSnippets === null) {
223*4cadd4f8SNickeau            return null;
224*4cadd4f8SNickeau        }
225*4cadd4f8SNickeau        $keys = array_keys(self::$globalSnippets);
226*4cadd4f8SNickeau        return self::$globalSnippets[$keys[0]];
22737748cd8SNickeau    }
22837748cd8SNickeau
22937748cd8SNickeau
23037748cd8SNickeau    /**
23137748cd8SNickeau     * @param $bool - if the snippet is critical, it would not be deferred or preloaded
23237748cd8SNickeau     * @return Snippet for chaining
23337748cd8SNickeau     * All css that are for animation or background for instance
23437748cd8SNickeau     * should not be set as critical as they are not needed to paint
23537748cd8SNickeau     * exactly the page
236*4cadd4f8SNickeau     *
237*4cadd4f8SNickeau     * If a snippet is critical, it should not be deferred
238*4cadd4f8SNickeau     *
239*4cadd4f8SNickeau     * By default:
240*4cadd4f8SNickeau     *   * all css are critical (except animation or background stylesheet)
241*4cadd4f8SNickeau     *   * all javascript are not critical
242*4cadd4f8SNickeau     *
243*4cadd4f8SNickeau     * This attribute is passed in the dokuwiki array
244*4cadd4f8SNickeau     * The value is stored in the {@link Snippet::getCritical()}
24537748cd8SNickeau     */
246c3437056SNickeau    public function setCritical($bool): Snippet
24737748cd8SNickeau    {
24837748cd8SNickeau        $this->critical = $bool;
24937748cd8SNickeau        return $this;
25037748cd8SNickeau    }
25137748cd8SNickeau
25237748cd8SNickeau    /**
253*4cadd4f8SNickeau     * If the library does not manipulate the DOM,
254*4cadd4f8SNickeau     * it can be ran as soon as possible (ie async)
255*4cadd4f8SNickeau     * @param $bool
256*4cadd4f8SNickeau     * @return $this
257*4cadd4f8SNickeau     */
258*4cadd4f8SNickeau    public function setDoesManipulateTheDomOnRun($bool): Snippet
259*4cadd4f8SNickeau    {
260*4cadd4f8SNickeau        $this->async = !$bool;
261*4cadd4f8SNickeau        return $this;
262*4cadd4f8SNickeau    }
263*4cadd4f8SNickeau
264*4cadd4f8SNickeau    /**
265*4cadd4f8SNickeau     * @param $inlineContent - Set an inline content for a script or stylesheet
26637748cd8SNickeau     * @return Snippet for chaining
26737748cd8SNickeau     */
268*4cadd4f8SNickeau    public function setInlineContent($inlineContent): Snippet
26937748cd8SNickeau    {
270*4cadd4f8SNickeau        $this->inlineContent = $inlineContent;
27137748cd8SNickeau        return $this;
27237748cd8SNickeau    }
27337748cd8SNickeau
27437748cd8SNickeau    /**
27537748cd8SNickeau     * @return string
27637748cd8SNickeau     */
277*4cadd4f8SNickeau    public function getInternalDynamicContent(): ?string
27837748cd8SNickeau    {
279*4cadd4f8SNickeau        return $this->inlineContent;
280*4cadd4f8SNickeau    }
281*4cadd4f8SNickeau
282*4cadd4f8SNickeau    /**
283*4cadd4f8SNickeau     * @return string|null
284*4cadd4f8SNickeau     */
285*4cadd4f8SNickeau    public function getInternalFileContent(): ?string
286*4cadd4f8SNickeau    {
287*4cadd4f8SNickeau        $path = $this->getInternalFile();
288*4cadd4f8SNickeau        if (!FileSystems::exists($path)) {
289*4cadd4f8SNickeau            return null;
290*4cadd4f8SNickeau        }
291*4cadd4f8SNickeau        return FileSystems::getContent($path);
292*4cadd4f8SNickeau    }
293*4cadd4f8SNickeau
294*4cadd4f8SNickeau    public function getInternalFile(): ?LocalPath
295*4cadd4f8SNickeau    {
296*4cadd4f8SNickeau        switch ($this->extension) {
297*4cadd4f8SNickeau            case self::EXTENSION_CSS:
298*4cadd4f8SNickeau                $extension = "css";
299*4cadd4f8SNickeau                $subDirectory = "style";
30037748cd8SNickeau                break;
301*4cadd4f8SNickeau            case self::EXTENSION_JS:
302*4cadd4f8SNickeau                $extension = "js";
303*4cadd4f8SNickeau                $subDirectory = "js";
30437748cd8SNickeau                break;
30537748cd8SNickeau            default:
306*4cadd4f8SNickeau                $message = "Unknown snippet type ($this->extension)";
307*4cadd4f8SNickeau                if (PluginUtility::isDevOrTest()) {
308*4cadd4f8SNickeau                    throw new ExceptionComboRuntime($message);
30937748cd8SNickeau                } else {
310*4cadd4f8SNickeau                    LogUtility::msg($message);
311*4cadd4f8SNickeau                }
312*4cadd4f8SNickeau                return null;
313*4cadd4f8SNickeau        }
314*4cadd4f8SNickeau        return Site::getComboResourceSnippetDirectory()
315*4cadd4f8SNickeau            ->resolve($subDirectory)
316*4cadd4f8SNickeau            ->resolve(strtolower($this->componentName) . ".$extension");
31737748cd8SNickeau    }
31837748cd8SNickeau
319*4cadd4f8SNickeau    public function hasSlot($slot): bool
32037748cd8SNickeau    {
321*4cadd4f8SNickeau        if ($this->slots === null) {
322*4cadd4f8SNickeau            return false;
32337748cd8SNickeau        }
324*4cadd4f8SNickeau        return key_exists($slot, $this->slots);
32537748cd8SNickeau    }
32637748cd8SNickeau
32737748cd8SNickeau    public function __toString()
32837748cd8SNickeau    {
329*4cadd4f8SNickeau        return $this->snippetId . "-" . $this->extension;
33037748cd8SNickeau    }
33137748cd8SNickeau
332c3437056SNickeau    public function getCritical(): bool
33337748cd8SNickeau    {
334c3437056SNickeau        if ($this->critical === null) {
335*4cadd4f8SNickeau            if ($this->extension == self::EXTENSION_CSS) {
336c3437056SNickeau                // All CSS should be loaded first
337c3437056SNickeau                // The CSS animation / background can set this to false
338c3437056SNickeau                return true;
339c3437056SNickeau            }
340c3437056SNickeau            return false;
341c3437056SNickeau        }
34237748cd8SNickeau        return $this->critical;
34337748cd8SNickeau    }
34437748cd8SNickeau
345c3437056SNickeau    public function getClass(): string
34637748cd8SNickeau    {
34737748cd8SNickeau        /**
34837748cd8SNickeau         * The class for the snippet is just to be able to identify them
34937748cd8SNickeau         *
35037748cd8SNickeau         * The `snippet` prefix was added to be sure that the class
35137748cd8SNickeau         * name will not conflict with a css class
35237748cd8SNickeau         * Example: if you set the class to `combo-list`
35337748cd8SNickeau         * and that you use it in a inline `style` tag with
35437748cd8SNickeau         * the same class name, the inline `style` tag is not applied
35537748cd8SNickeau         *
35637748cd8SNickeau         */
357*4cadd4f8SNickeau        return "snippet-" . $this->componentName . "-" . SnippetManager::COMBO_CLASS_SUFFIX;
35837748cd8SNickeau
35937748cd8SNickeau    }
36037748cd8SNickeau
36137748cd8SNickeau    /**
36237748cd8SNickeau     * @return string the HTML of the tag (works for now only with CSS content)
36337748cd8SNickeau     */
364c3437056SNickeau    public function getHtmlStyleTag(): string
36537748cd8SNickeau    {
366*4cadd4f8SNickeau        $content = $this->getInternalInlineAndFileContent();
36737748cd8SNickeau        $class = $this->getClass();
36837748cd8SNickeau        return <<<EOF
36937748cd8SNickeau<style class="$class">
37037748cd8SNickeau$content
37137748cd8SNickeau</style>
37237748cd8SNickeauEOF;
37337748cd8SNickeau
37437748cd8SNickeau    }
37537748cd8SNickeau
37637748cd8SNickeau    public function getId()
37737748cd8SNickeau    {
37837748cd8SNickeau        return $this->snippetId;
37937748cd8SNickeau    }
38037748cd8SNickeau
38137748cd8SNickeau
382*4cadd4f8SNickeau    public function toJsonArray(): array
383c3437056SNickeau    {
384*4cadd4f8SNickeau        return $this->jsonSerialize();
385c3437056SNickeau
386c3437056SNickeau    }
387c3437056SNickeau
388c3437056SNickeau    /**
389c3437056SNickeau     * @throws ExceptionCombo
390c3437056SNickeau     */
391c3437056SNickeau    public static function createFromJson($array): Snippet
392c3437056SNickeau    {
393*4cadd4f8SNickeau        $snippetType = $array[self::JSON_TYPE_PROPERTY];
394*4cadd4f8SNickeau        if ($snippetType === null) {
395c3437056SNickeau            throw new ExceptionCombo("The snippet type property was not found in the json array");
396c3437056SNickeau        }
397*4cadd4f8SNickeau        switch ($snippetType) {
398*4cadd4f8SNickeau            case Snippet::INTERNAL_TYPE:
399*4cadd4f8SNickeau                $identifier = Snippet::INTERNAL_TYPE;
400*4cadd4f8SNickeau                break;
401*4cadd4f8SNickeau            case Snippet::EXTERNAL_TYPE:
402*4cadd4f8SNickeau                $identifier = $array[self::JSON_URL_PROPERTY];
403*4cadd4f8SNickeau                break;
404*4cadd4f8SNickeau            default:
405*4cadd4f8SNickeau                throw new ExceptionCombo("snippet type unknown ($snippetType");
406*4cadd4f8SNickeau        }
407*4cadd4f8SNickeau        $extension = $array[self::JSON_EXTENSION_PROPERTY];
408*4cadd4f8SNickeau        if ($extension === null) {
409*4cadd4f8SNickeau            throw new ExceptionCombo("The snippet extension property was not found in the json array");
410*4cadd4f8SNickeau        }
411*4cadd4f8SNickeau        $componentName = $array[self::JSON_COMPONENT_PROPERTY];
412*4cadd4f8SNickeau        if ($componentName === null) {
413*4cadd4f8SNickeau            throw new ExceptionCombo("The snippet component name property was not found in the json array");
414*4cadd4f8SNickeau        }
415*4cadd4f8SNickeau        $snippet = Snippet::getOrCreateSnippet($identifier, $extension, $componentName);
416*4cadd4f8SNickeau
417*4cadd4f8SNickeau
418c3437056SNickeau        $critical = $array[self::JSON_CRITICAL_PROPERTY];
419c3437056SNickeau        if ($critical !== null) {
420c3437056SNickeau            $snippet->setCritical($critical);
421c3437056SNickeau        }
422c3437056SNickeau
423*4cadd4f8SNickeau        $async = $array[self::JSON_ASYNC_PROPERTY];
424*4cadd4f8SNickeau        if ($async !== null) {
425*4cadd4f8SNickeau            $snippet->setDoesManipulateTheDomOnRun($async);
426*4cadd4f8SNickeau        }
427*4cadd4f8SNickeau
428c3437056SNickeau        $content = $array[self::JSON_CONTENT_PROPERTY];
429c3437056SNickeau        if ($content !== null) {
430*4cadd4f8SNickeau            $snippet->setInlineContent($content);
431c3437056SNickeau        }
432c3437056SNickeau
433*4cadd4f8SNickeau        $attributes = $array[self::JSON_HTML_ATTRIBUTES_PROPERTY];
434*4cadd4f8SNickeau        if ($attributes !== null) {
435*4cadd4f8SNickeau            foreach ($attributes as $name => $value) {
436*4cadd4f8SNickeau                $snippet->addHtmlAttribute($name, $value);
437c3437056SNickeau            }
438*4cadd4f8SNickeau        }
439*4cadd4f8SNickeau
440*4cadd4f8SNickeau        $integrity = $array[self::JSON_INTEGRITY_PROPERTY];
441*4cadd4f8SNickeau        if ($integrity !== null) {
442*4cadd4f8SNickeau            $snippet->setIntegrity($integrity);
443*4cadd4f8SNickeau        }
444*4cadd4f8SNickeau
445c3437056SNickeau        return $snippet;
446c3437056SNickeau
447c3437056SNickeau    }
448*4cadd4f8SNickeau
449*4cadd4f8SNickeau    public function getExtension()
450*4cadd4f8SNickeau    {
451*4cadd4f8SNickeau        return $this->extension;
452*4cadd4f8SNickeau    }
453*4cadd4f8SNickeau
454*4cadd4f8SNickeau    public function setIntegrity(?string $integrity): Snippet
455*4cadd4f8SNickeau    {
456*4cadd4f8SNickeau        $this->integrity = $integrity;
457*4cadd4f8SNickeau        return $this;
458*4cadd4f8SNickeau    }
459*4cadd4f8SNickeau
460*4cadd4f8SNickeau    public function addHtmlAttribute(string $name, string $value): Snippet
461*4cadd4f8SNickeau    {
462*4cadd4f8SNickeau        $this->htmlAttributes[$name] = $value;
463*4cadd4f8SNickeau        return $this;
464*4cadd4f8SNickeau    }
465*4cadd4f8SNickeau
466*4cadd4f8SNickeau    public function addSlot(string $slot): Snippet
467*4cadd4f8SNickeau    {
468*4cadd4f8SNickeau        $this->slots[$slot] = 1;
469*4cadd4f8SNickeau        return $this;
470*4cadd4f8SNickeau    }
471*4cadd4f8SNickeau
472*4cadd4f8SNickeau    public function getType(): string
473*4cadd4f8SNickeau    {
474*4cadd4f8SNickeau        return $this->type;
475*4cadd4f8SNickeau    }
476*4cadd4f8SNickeau
477*4cadd4f8SNickeau    public function getUrl(): string
478*4cadd4f8SNickeau    {
479*4cadd4f8SNickeau        return $this->url;
480*4cadd4f8SNickeau    }
481*4cadd4f8SNickeau
482*4cadd4f8SNickeau    public function getIntegrity(): ?string
483*4cadd4f8SNickeau    {
484*4cadd4f8SNickeau        return $this->integrity;
485*4cadd4f8SNickeau    }
486*4cadd4f8SNickeau
487*4cadd4f8SNickeau    public function getHtmlAttributes(): ?array
488*4cadd4f8SNickeau    {
489*4cadd4f8SNickeau        return $this->htmlAttributes;
490*4cadd4f8SNickeau    }
491*4cadd4f8SNickeau
492*4cadd4f8SNickeau    public function getInternalInlineAndFileContent(): ?string
493*4cadd4f8SNickeau    {
494*4cadd4f8SNickeau        $totalContent = null;
495*4cadd4f8SNickeau        $internalFileContent = $this->getInternalFileContent();
496*4cadd4f8SNickeau        if ($internalFileContent !== null) {
497*4cadd4f8SNickeau            $totalContent = $internalFileContent;
498*4cadd4f8SNickeau        }
499*4cadd4f8SNickeau
500*4cadd4f8SNickeau        $content = $this->getInternalDynamicContent();
501*4cadd4f8SNickeau        if ($content !== null) {
502*4cadd4f8SNickeau            if ($totalContent === null) {
503*4cadd4f8SNickeau                $totalContent = $content;
504*4cadd4f8SNickeau            } else {
505*4cadd4f8SNickeau                $totalContent .= $content;
506*4cadd4f8SNickeau            }
507*4cadd4f8SNickeau        }
508*4cadd4f8SNickeau        return $totalContent;
509*4cadd4f8SNickeau
510*4cadd4f8SNickeau    }
511*4cadd4f8SNickeau
512*4cadd4f8SNickeau
513*4cadd4f8SNickeau    public function jsonSerialize(): array
514*4cadd4f8SNickeau    {
515*4cadd4f8SNickeau        $dataToSerialize = [
516*4cadd4f8SNickeau            self::JSON_COMPONENT_PROPERTY => $this->componentName,
517*4cadd4f8SNickeau            self::JSON_EXTENSION_PROPERTY => $this->extension,
518*4cadd4f8SNickeau            self::JSON_TYPE_PROPERTY => $this->type
519*4cadd4f8SNickeau        ];
520*4cadd4f8SNickeau        if ($this->url !== null) {
521*4cadd4f8SNickeau            $dataToSerialize[self::JSON_URL_PROPERTY] = $this->url;
522*4cadd4f8SNickeau        }
523*4cadd4f8SNickeau        if ($this->integrity !== null) {
524*4cadd4f8SNickeau            $dataToSerialize[self::JSON_INTEGRITY_PROPERTY] = $this->integrity;
525*4cadd4f8SNickeau        }
526*4cadd4f8SNickeau        if ($this->critical !== null) {
527*4cadd4f8SNickeau            $dataToSerialize[self::JSON_CRITICAL_PROPERTY] = $this->critical;
528*4cadd4f8SNickeau        }
529*4cadd4f8SNickeau        if ($this->async !== null) {
530*4cadd4f8SNickeau            $dataToSerialize[self::JSON_ASYNC_PROPERTY] = $this->async;
531*4cadd4f8SNickeau        }
532*4cadd4f8SNickeau        if ($this->inlineContent !== null) {
533*4cadd4f8SNickeau            $dataToSerialize[self::JSON_CONTENT_PROPERTY] = $this->inlineContent;
534*4cadd4f8SNickeau        }
535*4cadd4f8SNickeau        if ($this->htmlAttributes !== null) {
536*4cadd4f8SNickeau            $dataToSerialize[self::JSON_HTML_ATTRIBUTES_PROPERTY] = $this->htmlAttributes;
537*4cadd4f8SNickeau        }
538*4cadd4f8SNickeau        return $dataToSerialize;
539*4cadd4f8SNickeau    }
54037748cd8SNickeau}
541