Passed
Push — master ( 506152...7c24f3 )
by Alexander
05:14 queued 01:53
created

AssetUtil::extractFilePathsForExport()   B

Complexity

Conditions 9
Paths 6

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 9

Importance

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