Passed
Push — master ( 8b9472...797003 )
by Evgeniy
02:42
created

AssetUtil::resolvePathAliases()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

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