AssetUtil::isRelative()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 1
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 3
rs 10
ccs 2
cts 2
cp 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Assets;
6
7
use RuntimeException;
8
use Yiisoft\Aliases\Aliases;
9
10
use function array_merge;
11
use function array_filter;
12
use function array_unique;
13
use function dirname;
14
use function file_put_contents;
15
use function is_array;
16
use function is_dir;
17
use function is_subclass_of;
18
use function is_writable;
19
use function mb_strlen;
20
use function strncmp;
21
use function substr_compare;
22
23
/**
24
 * `AssetUtil` shared functions.
25
 */
26
final class AssetUtil
27
{
28
    /**
29
     * Creates a new asset bundle instance.
30
     *
31
     * If the name is a class name, an instance of this class will be created,
32
     * otherwise an instance of the {@see AssetBundle} will be created.
33
     *
34
     * @param string $name The asset bundle name. Usually the asset bundle class name (without leading backslash).
35
     * @param array $config The asset bundle instance configuration. If specified, it will be applied to the instance.
36
     *
37
     * @psalm-param array<string,mixed> $config
38
     *
39
     * @return AssetBundle The created asset bundle.
40
     */
41 75
    public static function createAsset(string $name, array $config = []): AssetBundle
42
    {
43
        /** @psalm-suppress UnsafeInstantiation */
44 75
        $bundle = is_subclass_of($name, AssetBundle::class) ? new $name() : new AssetBundle();
45
46 75
        foreach ($config as $property => $value) {
47 28
            $bundle->{$property} = $value;
48
        }
49
50 75
        return $bundle;
51
    }
52
53
    /**
54
     * Resolves the actual URL for the specified asset.
55
     *
56
     * @param AssetBundle $bundle The asset bundle which the asset file belongs to.
57
     * @param string $assetPath The asset path. This should be one of the assets listed
58
     * in {@see AssetBundle::$js} or {@see AssetBundle::$css}.
59
     * @param array $assetMap Mapping from source asset files (keys) to target asset files (values)
60
     * {@see AssetPublisher::$assetMap}.
61
     *
62
     * @psalm-param array<string, string> $assetMap
63
     *
64
     * @return string|null The actual URL for the specified asset, or null if there is no mapping.
65
     */
66 48
    public static function resolveAsset(AssetBundle $bundle, string $assetPath, array $assetMap): ?string
67
    {
68 48
        if (isset($assetMap[$assetPath])) {
69 1
            return $assetMap[$assetPath];
70
        }
71
72 47
        if (!empty($bundle->sourcePath) && self::isRelative($assetPath)) {
73 12
            $assetPath = $bundle->sourcePath . '/' . $assetPath;
74
        }
75
76 47
        $n = mb_strlen($assetPath, 'utf-8');
77
78 47
        foreach ($assetMap as $from => $to) {
79 1
            $n2 = mb_strlen($from, 'utf-8');
80 1
            if ($n2 <= $n && substr_compare($assetPath, $from, $n - $n2, $n2) === 0) {
81 1
                return $to;
82
            }
83
        }
84
85 46
        return null;
86
    }
87
88
    /**
89
     * Resolve path aliases for {@see AssetBundle} properties:
90
     *
91
     * - {@see AssetBundle::$basePath}
92
     * - {@see AssetBundle::$baseUrl}
93
     * - {@see AssetBundle::$sourcePath}
94
     *
95
     * @param AssetBundle $bundle The asset bundle instance to resolving path aliases.
96
     * @param Aliases $aliases The aliases instance to resolving path aliases.
97
     *
98
     * @return AssetBundle The asset bundle instance with resolved paths.
99
     */
100 3
    public static function resolvePathAliases(AssetBundle $bundle, Aliases $aliases): AssetBundle
101
    {
102 3
        if ($bundle->basePath !== null) {
103 3
            $bundle->basePath = $aliases->get($bundle->basePath);
104
        }
105
106 3
        if ($bundle->baseUrl !== null) {
107 3
            $bundle->baseUrl = $aliases->get($bundle->baseUrl);
108
        }
109
110 3
        if ($bundle->sourcePath !== null) {
111 3
            $bundle->sourcePath = $aliases->get($bundle->sourcePath);
112
        }
113
114 3
        return $bundle;
115
    }
116
117
    /**
118
     * Extracts the file paths to export from each asset bundle {@see AssetBundle::$export}.
119
     *
120
     * @param AssetBundle[] $bundles List of asset bundles.
121
     *
122
     * @return string[] Extracted file paths.
123
     */
124 10
    public static function extractFilePathsForExport(array $bundles): array
125
    {
126 10
        $filePaths = [];
127
128 10
        foreach ($bundles as $bundle) {
129 10
            if ($bundle->cdn || empty($bundle->sourcePath)) {
130 7
                continue;
131
            }
132
133 8
            if (!empty($bundle->export)) {
134
                /** @var string $filePath */
135 5
                foreach ($bundle->export as $filePath) {
136 5
                    $filePaths[] = "{$bundle->sourcePath}/{$filePath}";
137
                }
138 5
                continue;
139
            }
140
141
            /** @var array|string $item */
142 7
            foreach (array_merge($bundle->css, $bundle->js) as $item) {
143
                /** @var string */
144 7
                $filePath = is_array($item) ? $item[0] : $item;
145 7
                $filePaths[] = "{$bundle->sourcePath}/{$filePath}";
146
            }
147
        }
148
149 10
        return array_unique(array_filter($filePaths));
150
    }
151
152
    /**
153
     * Writes a string representation of asset bundles to the specified file.
154
     *
155
     * @param string $targetFile The full path to the target file.
156
     * @param string $bundles The string representation of asset bundles.
157
     *
158
     * @throws RuntimeException If an error occurred while writing to the file.
159
     */
160 10
    public static function exportToFile(string $targetFile, string $bundles): void
161
    {
162 10
        $targetDirectory = dirname($targetFile);
163
164 10
        if (!is_dir($targetDirectory) || !is_writable($targetDirectory)) {
165 2
            throw new RuntimeException("Target directory \"{$targetDirectory}\" does not exist or is not writable.");
166
        }
167
168 8
        if (file_put_contents($targetFile, $bundles, LOCK_EX) === false) {
169 1
            throw new RuntimeException("An error occurred while writing to the \"{$targetFile}\" file.");
170
        }
171
    }
172
173
    /**
174
     * Returns a value indicating whether a URL is relative.
175
     *
176
     * A relative URL does not have host info part.
177
     *
178
     * @param string $url The URL to be checked.
179
     *
180
     * @return bool Whether the URL is relative.
181
     */
182 46
    public static function isRelative(string $url): bool
183
    {
184 46
        return strncmp($url, '//', 2) && !str_contains($url, '://');
185
    }
186
}
187