Completed
Push — master ( dc2115...2a0218 )
by
unknown
43:53 queued 30:01
created

AssetCollector::addMedia()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 6
rs 10
c 1
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
namespace TYPO3\CMS\Core\Page;
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
use TYPO3\CMS\Core\SingletonInterface;
19
use TYPO3\CMS\Core\Utility\ArrayUtility;
20
21
/**
22
 * The Asset Collector is responsible for keeping track of
23
 * - everything within <script> tags: javascript files and inline javascript code
24
 * - inline CSS and CSS files
25
 *
26
 * The goal of the asset collector is to:
27
 * - utilize a single "runtime-based" store for adding assets of certain kinds that are added to the output
28
 * - allow to deal with assets from non-cacheable plugins and cacheable content in the Frontend
29
 * - reduce the "power" and flexibility (I'd say it's a burden) of the "god class" PageRenderer.
30
 * - reduce the burden of storing everything in PageRenderer
31
 *
32
 * As a side-effect this allows to:
33
 * - Add a single CSS snippet or CSS file per content block, but assure that the CSS is only added once to the output.
34
 *
35
 * Note on the implementation:
36
 * - We use a Singleton to make use of the AssetCollector throughout Frontend process (similar to PageRenderer).
37
 * - Although this is not optimal, I don't see any other way to do so in the current code.
38
 *
39
 * https://developer.wordpress.org/reference/functions/wp_enqueue_style/
40
 */
41
class AssetCollector implements SingletonInterface
42
{
43
    /**
44
     * @var array
45
     */
46
    protected $javaScripts = [];
47
48
    /**
49
     * @var array
50
     */
51
    protected $inlineJavaScripts = [];
52
53
    /**
54
     * @var array
55
     */
56
    protected $styleSheets = [];
57
58
    /**
59
     * @var array
60
     */
61
    protected $inlineStyleSheets = [];
62
63
    /**
64
     * @var array
65
     */
66
    protected $media = [];
67
68
    public function addJavaScript(string $identifier, string $source, array $attributes, array $options = []): self
69
    {
70
        $existingAttributes = $this->javaScripts[$identifier]['attributes'] ?? [];
71
        ArrayUtility::mergeRecursiveWithOverrule($existingAttributes, $attributes);
72
        $existingOptions = $this->javaScripts[$identifier]['options'] ?? [];
73
        ArrayUtility::mergeRecursiveWithOverrule($existingOptions, $options);
74
        $this->javaScripts[$identifier] = [
75
            'source' => $source,
76
            'attributes' => $existingAttributes,
77
            'options' => $existingOptions
78
        ];
79
        return $this;
80
    }
81
82
    public function addInlineJavaScript(string $identifier, string $source, array $attributes, array $options = []): self
83
    {
84
        $existingAttributes = $this->inlineJavaScripts[$identifier]['attributes'] ?? [];
85
        ArrayUtility::mergeRecursiveWithOverrule($existingAttributes, $attributes);
86
        $existingOptions = $this->inlineJavaScripts[$identifier]['options'] ?? [];
87
        ArrayUtility::mergeRecursiveWithOverrule($existingOptions, $options);
88
        $this->inlineJavaScripts[$identifier] = [
89
            'source' => $source,
90
            'attributes' => $existingAttributes,
91
            'options' => $existingOptions
92
        ];
93
        return $this;
94
    }
95
96
    public function addStyleSheet(string $identifier, string $source, array $attributes, array $options = []): self
97
    {
98
        $existingAttributes = $this->styleSheets[$identifier]['attributes'] ?? [];
99
        ArrayUtility::mergeRecursiveWithOverrule($existingAttributes, $attributes);
100
        $existingOptions = $this->styleSheets[$identifier]['options'] ?? [];
101
        ArrayUtility::mergeRecursiveWithOverrule($existingOptions, $options);
102
        $this->styleSheets[$identifier] = [
103
            'source' => $source,
104
            'attributes' => $existingAttributes,
105
            'options' => $existingOptions
106
        ];
107
        return $this;
108
    }
109
110
    public function addInlineStyleSheet(string $identifier, string $source, array $attributes, array $options = []): self
111
    {
112
        $existingAttributes = $this->inlineStyleSheets[$identifier]['attributes'] ?? [];
113
        ArrayUtility::mergeRecursiveWithOverrule($existingAttributes, $attributes);
114
        $existingOptions = $this->inlineStyleSheets[$identifier]['options'] ?? [];
115
        ArrayUtility::mergeRecursiveWithOverrule($existingOptions, $options);
116
        $this->inlineStyleSheets[$identifier] = [
117
            'source' => $source,
118
            'attributes' => $existingAttributes,
119
            'options' => $existingOptions
120
        ];
121
        return $this;
122
    }
123
124
    /**
125
     * @param string $fileName
126
     * @param array $additionalInformation One dimensional hash map (array with non numerical keys) with scalar values
127
     * @return AssetCollector
128
     */
129
    public function addMedia(string $fileName, array $additionalInformation): self
130
    {
131
        $existingAdditionalInformation = $this->media[$fileName] ?? [];
132
        ArrayUtility::mergeRecursiveWithOverrule($existingAdditionalInformation, $this->ensureAllValuesAreSerializable($additionalInformation));
133
        $this->media[$fileName] = $existingAdditionalInformation;
134
        return $this;
135
    }
136
137
    private function ensureAllValuesAreSerializable(array $additionalInformation): array
138
    {
139
        // Currently just filtering all non scalar values
140
        return array_filter($additionalInformation, 'is_scalar');
141
    }
142
143
    public function removeJavaScript(string $identifier): self
144
    {
145
        unset($this->javaScripts[$identifier]);
146
        return $this;
147
    }
148
149
    public function removeInlineJavaScript(string $identifier): self
150
    {
151
        unset($this->inlineJavaScripts[$identifier]);
152
        return $this;
153
    }
154
155
    public function removeStyleSheet(string $identifier): self
156
    {
157
        unset($this->styleSheets[$identifier]);
158
        return $this;
159
    }
160
161
    public function removeInlineStyleSheet(string $identifier): self
162
    {
163
        unset($this->inlineStyleSheets[$identifier]);
164
        return $this;
165
    }
166
167
    public function removeMedia(string $identifier): self
168
    {
169
        unset($this->media[$identifier]);
170
        return $this;
171
    }
172
173
    public function getMedia(): array
174
    {
175
        return $this->media;
176
    }
177
178
    public function getJavaScripts(): array
179
    {
180
        return $this->javaScripts;
181
    }
182
183
    public function getInlineJavaScripts(): array
184
    {
185
        return $this->inlineJavaScripts;
186
    }
187
188
    public function getStyleSheets(): array
189
    {
190
        return $this->styleSheets;
191
    }
192
193
    public function getInlineStyleSheets(): array
194
    {
195
        return $this->inlineStyleSheets;
196
    }
197
}
198