Passed
Push — master ( 781931...9601bc )
by Alexander
02:28
created

AssetLoader::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
c 1
b 0
f 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Assets;
6
7
use Yiisoft\Aliases\Aliases;
8
use Yiisoft\Assets\Exception\InvalidConfigException;
9
use Yiisoft\Files\FileHelper;
10
11
use function array_merge;
12
use function is_file;
13
use function strncmp;
14
15
/**
16
 * AssetLoader is responsible for executing the loading of the assets
17
 * from {@see AssetBundle::$basePath} to {@see AssetBundle::$baseUrl}.
18
 */
19
final class AssetLoader implements AssetLoaderInterface
20
{
21
    private Aliases $aliases;
22
23
    /**
24
     * @var bool Whether to append a timestamp to the URL of every published asset. When this is true, the URL of a
25
     * published asset may look like `/path/to/asset?v=timestamp`, where `timestamp` is the last modification time
26
     * of the published asset file. You normally would want to set this property to true when you have enabled
27
     * HTTP caching for assets, because it allows you to bust caching when the assets are updated.
28
     */
29
    private bool $appendTimestamp = false;
30
31
    /**
32
     * @var array<string, string> Mapping from source asset files (keys) to target asset files (values).
33
     *
34
     * This property is provided to support fixing incorrect asset file paths in some asset bundles. When an asset
35
     * bundle is registered with a view, each relative asset file in its {@see AssetBundle::$css} and
36
     * {@see AssetBundle::$js} arrays will be examined against this map. If any of the keys is found to be the last
37
     * part of an asset file (which is prefixed with {@see AssetBundle::$sourcePath} if available), the corresponding
38
     * value will replace the asset and be registered with the view. For example, an asset file `my/path/to/jquery.js`
39
     * matches a key `jquery.js`.
40
     *
41
     * Note that the target asset files should be absolute URLs, domain relative URLs (starting from '/') or paths
42
     * relative to {@see $baseUrl} and {@see $basePath}.
43
     *
44
     * In the following example, any assets ending with `jquery.min.js` will be replaced with `jquery/dist/jquery.js`
45
     * which is relative to {@see $baseUrl} and {@see $basePath}.
46
     *
47
     * ```php
48
     * [
49
     *     'jquery.min.js' => 'jquery/dist/jquery.js',
50
     * ]
51
     * ```
52
     */
53
    private array $assetMap = [];
54
55
    /**
56
     * @var string|null The root directory storing the asset files.
57
     */
58
    private ?string $basePath = null;
59
60
    /**
61
     * @var string|null The base URL that can be used to access the asset files.
62
     */
63
    private ?string $baseUrl = null;
64
65
    /**
66
     * @var array The options that will be passed to {@see \Yiisoft\View\WebView::registerCssFile()}
67
     * when registering the CSS files all assets bundle.
68
     */
69
    private array $cssDefaultOptions = [];
70
71
    /**
72
     * @var array The options that will be passed to {@see \Yiisoft\View\WebView::registerJsFile()}
73
     * when registering the JS files all assets bundle.
74
     */
75
    private array $jsDefaultOptions = [];
76
77 78
    public function __construct(Aliases $aliases)
78
    {
79 78
        $this->aliases = $aliases;
80 78
    }
81
82 33
    public function getAssetUrl(AssetBundle $bundle, string $assetPath): string
83
    {
84 33
        if (!$bundle->cdn && empty($this->basePath) && empty($bundle->basePath)) {
85 1
            throw new InvalidConfigException(
86
                'basePath must be set in AssetLoader->setBasePath($path) or ' .
87 1
                'AssetBundle property public ?string $basePath = $path'
88
            );
89
        }
90
91 32
        if (!$bundle->cdn && $this->baseUrl === null && $bundle->baseUrl === null) {
92 1
            throw new InvalidConfigException(
93
                'baseUrl must be set in AssetLoader->setBaseUrl($path) or ' .
94 1
                'AssetBundle property public ?string $baseUrl = $path'
95
            );
96
        }
97
98 31
        $asset = AssetUtil::resolveAsset($bundle, $assetPath, $this->assetMap);
99
100 31
        if (!empty($asset)) {
101 1
            $assetPath = $asset;
102
        }
103
104 31
        if ($bundle->cdn) {
105 1
            return $bundle->baseUrl === null
106 1
                ? $assetPath
107 1
                : $bundle->baseUrl . '/' . $assetPath;
108
        }
109
110 30
        if (!AssetUtil::isRelative($assetPath) || strncmp($assetPath, '/', 1) === 0) {
111 2
            return $assetPath;
112
        }
113
114 28
        $path = "{$this->getBundleBasePath($bundle)}/{$assetPath}";
115 28
        $url = "{$this->getBundleBaseUrl($bundle)}/{$assetPath}";
116
117 28
        if (!is_file($path)) {
118 1
            throw new InvalidConfigException("Asset files not found: \"{$path}\".");
119
        }
120
121 27
        if ($this->appendTimestamp && ($timestamp = FileHelper::lastModifiedTime($path)) > 0) {
122 1
            return "{$url}?v={$timestamp}";
123
        }
124
125 26
        return $url;
126
    }
127
128 40
    public function loadBundle(string $name, array $config = []): AssetBundle
129
    {
130 40
        $bundle = AssetUtil::createAsset($name, $config);
131
132 40
        $bundle->basePath = $this->getBundleBasePath($bundle);
133 40
        $bundle->baseUrl = $this->getBundleBaseUrl($bundle);
134
135 40
        $bundle->cssOptions = array_merge($bundle->cssOptions, $this->cssDefaultOptions);
136 40
        $bundle->jsOptions = array_merge($bundle->jsOptions, $this->jsDefaultOptions);
137
138 40
        return $bundle;
139
    }
140
141
    /**
142
     * Sets whether to append a timestamp to the URL of every loaded asset.
143
     *
144
     * @param bool $value
145
     *
146
     * {@see $appendTimestamp}
147
     */
148 78
    public function setAppendTimestamp(bool $value): void
149
    {
150 78
        $this->appendTimestamp = $value;
151 78
    }
152
153
    /**
154
     * Sets the map for mapping from source asset files (keys) to target asset files (values).
155
     *
156
     * @param array<string, string> $value
157
     *
158
     * {@see $assetMap}
159
     */
160 78
    public function setAssetMap(array $value): void
161
    {
162 78
        $this->assetMap = $value;
163 78
    }
164
165
    /**
166
     * Sets the root directory storing the asset files.
167
     *
168
     * @param string|null $value
169
     *
170
     * {@see $basePath}
171
     */
172 78
    public function setBasePath(?string $value): void
173
    {
174 78
        $this->basePath = $value;
175 78
    }
176
177
    /**
178
     * Sets the base URL that can be used to access the asset files.
179
     *
180
     * @param string|null $value
181
     *
182
     * {@see $baseUrl}
183
     */
184 78
    public function setBaseUrl(?string $value): void
185
    {
186 78
        $this->baseUrl = $value;
187 78
    }
188
189
    /**
190
     * Sets the global `$css` default options for all assets bundle.
191
     *
192
     * @param array $value
193
     *
194
     * {@see $cssDefaultOptions}
195
     */
196 1
    public function setCssDefaultOptions(array $value): void
197
    {
198 1
        $this->cssDefaultOptions = $value;
199 1
    }
200
201
    /**
202
     * Sets the global `$js` default options for all assets bundle.
203
     *
204
     * @param array $value
205
     *
206
     * {@see $jsDefaultOptions}
207
     */
208 1
    public function setJsDefaultOptions(array $value): void
209
    {
210 1
        $this->jsDefaultOptions = $value;
211 1
    }
212
213
    /**
214
     * If the asset bundle does not have a {@see AssetBundle::$basePath} set, the {@see $basePath} value is returned.
215
     *
216
     * @param AssetBundle $bundle
217
     *
218
     * @return string|null
219
     */
220 40
    private function getBundleBasePath(AssetBundle $bundle): ?string
221
    {
222 40
        if ($bundle->basePath === null && $this->basePath === null) {
223 3
            return null;
224
        }
225
226 37
        return $this->aliases->get($bundle->basePath ?? (string) $this->basePath);
227
    }
228
229
    /**
230
     * If the asset bundle does not have a {@see AssetBundle::$baseUrl} set, the {@see $baseUrl} value is returned.
231
     *
232
     * @param AssetBundle $bundle
233
     *
234
     * @return string|null
235
     */
236 40
    private function getBundleBaseUrl(AssetBundle $bundle): ?string
237
    {
238 40
        if ($bundle->baseUrl === null && $this->baseUrl === null) {
239 3
            return null;
240
        }
241
242 37
        return $this->aliases->get($bundle->baseUrl ?? (string) $this->baseUrl);
243
    }
244
}
245