Completed
Push — master ( 1eb011...864841 )
by Dmitry
03:56
created

AssetBuilder::resolveAssetFilesPaths()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
rs 9.4285
cc 3
eloc 9
nc 3
nop 2
1
<?php
2
3
namespace Dmitrynaum\SAM;
4
5
use RecursiveDirectoryIterator;
6
use RecursiveIteratorIterator;
7
use Dmitrynaum\SAM\Component\Manifest;
8
9
/**
10
 * Класс для построения (компиляции) asset`ов
11
 *
12
 * @author Naumov Dmitry <[email protected]>
13
 */
14
class AssetBuilder
15
{
16
17
    /**
18
     * Флаг "Заморозить asset`ы"
19
     * @var bool
20
     */
21
    protected $needFreeze;
22
    
23
    /**
24
     * Флаг "Сжать asset`ы"
25
     * @var bool
26
     */
27
    protected $needCompress;
28
    
29
    /**
30
     * Manifest
31
     * @var Manifest
32
     */
33
    protected $manifest;
34
    
35
    /**
36
     * Путь до файла sam.json
37
     * @var string
38
     */
39
    protected $manifestFilePath;
40
41
    /**
42
     * @param string $manifestFilePath - Путь до файла sam.json
43
     */
44
    public function __construct($manifestFilePath)
45
    {
46
        $this->manifestFilePath = $manifestFilePath;
47
        $this->needCompress     = false;
48
        $this->needFreeze       = false;
49
    }
50
51
    /**
52
     * Включить заморозку asset`ов
53
     */
54
    public function enableFreezing()
55
    {
56
        $this->needFreeze = true;
57
    }
58
59
    /**
60
     * Выключить заморозку asset`ов
61
     */
62
    public function disableFreezing()
63
    {
64
        $this->needFreeze = false;
65
    }
66
67
    /**
68
     * Включена ли заморозка asset`ов
69
     */
70
    public function isFreezingEnabled()
71
    {
72
        return $this->needFreeze;
73
    }
74
75
    /**
76
     * Включить сжатие asset`ов
77
     */
78
    public function enableCompressor()
79
    {
80
        $this->needCompress = true;
81
    }
82
83
    /**
84
     * Выключить сжатие asset`ов
85
     */
86
    public function disableCompressor()
87
    {
88
        $this->needCompress = false;
89
    }
90
91
    /**
92
     * Включиено ли сжатие asset`ов
93
     */
94
    public function isCompressorEnabled()
95
    {
96
        return $this->needCompress;
97
    }
98
99
    /**
100
     * Собрать asset`ы (скомпилировать)
101
     */
102
    public function build()
103
    {
104
        $this->clearAssetDirectory();
105
        $this->buildCss();
106
        $this->buildJs();
107
        $this->resultMap()->save();
108
    }
109
110
    /**
111
     * Очистить папку с asset`ами
112
     */
113
    protected function clearAssetDirectory()
114
    {
115
        // Получаем путь до папки с скомпилированными asset`ами
116
        $assetBasePath = $this->manifest()->getAssetBasePath();
117
        
118
        // Получием список всех вложенных папок и объектов
119
        $dirIterator   = new RecursiveDirectoryIterator($assetBasePath, RecursiveDirectoryIterator::SKIP_DOTS);
120
        $files         = new RecursiveIteratorIterator($dirIterator, RecursiveIteratorIterator::CHILD_FIRST);
121
        
122
        // Удаляем все вложенные файлы и объекты
123
        foreach ($files as $file) {
124
            if ($file->isDir()) {
125
                rmdir($file->getPathname());
126
            } else {
127
                unlink($file->getPathname());
128
            }
129
        }
130
    }
131
132
    /**
133
     * Скомпилировать css asset`ы
134
     */
135
    protected function buildCss()
136
    {
137
        $cssAssets = $this->manifest()->getCssAssets();
138
139
        $this->buildAssets($cssAssets);
140
    }
141
142
    /**
143
     * Скомпилировать JavaScript asset`ы
144
     */
145
    protected function buildJs()
146
    {
147
        $jsAssets = $this->manifest()->getJsAssets();
148
149
        $this->buildAssets($jsAssets);
150
    }
151
152
    /**
153
     * Скомпилировать asset`ы
154
     * @param string[] $assets Список asset`ов для компиляции
155
     */
156
    protected function buildAssets($assets)
157
    {
158
        // Получаем карту ассетов [имя_ассета => [список файлов]]
159
        $assetsPaths = $this->getFilesPaths($assets);
160
        
161
        // Обходим все asset`ы
162
        foreach ($assetsPaths as $assetName => $assetFiles) {
163
            // Считываем содержимое файлов которые необходимо объежинить в asset
164
            $assetContent = $this->readFiles($assetFiles);
165
166
            // Вычисляем путь до файла куда будет сохранен asset
167
            $assetPath    = $this->manifest()->getAssetBasePath() . '/' . $assetName;
168
            
169
            // Если необходимо сжать asset`ы
170
            if ($this->isCompressorEnabled()) {
171
                // Получаем объект для минимфикации
172
                $compressor = $this->getCompressor($assetName);
173
                
174
                // Сообщаем ему что необходимо минимфицировать
175
                $compressor->add($assetContent);
176
                
177
                // Минифицируем ассет
178
                $assetContent = $compressor->minify();
179
            }
180
181
            // Если необходимо заморозить
182
            if ($this->isFreezingEnabled()) {
183
                // Вычисляем путь до файла в который будет сохранен asset
184
                $assetHash = sha1($assetContent);
185
                $assetPath = $this->getFreezingAssetFileName($assetName, $assetHash);
186
            }
187
            
188
            // Сохраняем asset
189
            file_put_contents($assetPath, $assetContent);
190
191
            // Добавляем путь до asset файла в карту asset`ов
192
            $this->resultMap()->addAsset($assetName, $assetPath);
193
        }
194
    }
195
    
196
    /**
197
     * Получить список asset`ов и файлы которые в них используются
198
     * @param array $assets
199
     * @return array список файлов используемых в ассетах
200
     */
201
    protected function getFilesPaths($assets)
202
    {
203
        $assetFilePaths = [];
204
        foreach ($assets as $assetName => $assetFiles) {
205
            $assetFilePaths[$assetName] = $this->resolveAssetFilesPaths($assets, $assetFiles);
206
        }
207
        
208
        return $assetFilePaths;
209
    }
210
    
211
    /**
212
     * Получить список файлов ассета
213
     * @param array $assets
214
     * @param array $assetFiles
215
     * @return array
216
     */
217
    protected function resolveAssetFilesPaths($assets, $assetFiles)
218
    {
219
        $assetFilePaths = [];
220
        // Обходим все файлы
221
        foreach ($assetFiles as $assetNameOrFile) {
222
            // Если в списке ассетов есть ассет с именем $assetNameOrFile
223
            if (isset($assets[$assetNameOrFile])) {
224
                // это имя ассета файлы которого необходимо использовать
225
                $assetFiles     = $assets[$assetNameOrFile];
226
                $assetFilePaths = array_merge($assetFilePaths, $this->resolveAssetFilesPaths($assets, $assetFiles));
227
            } else {
228
                // это путь до файла который необходимо использовать
229
                $assetFilePaths[] = $assetNameOrFile;
230
            }
231
        }
232
233
        return $assetFilePaths;
234
    }
235
236
    /**
237
     * Получить имя замороженного файла asset`а
238
     * @param string $assetName - Имя asset`а
239
     * @param string $assetHash - Хэш asset`а
240
     * @return string
241
     */
242
    protected function getFreezingAssetFileName($assetName, $assetHash)
243
    {
244
        $fileInfo = pathinfo($assetName);
245
        $fileName = $fileInfo['filename'] . '-' . $assetHash . '.' . $fileInfo['extension'];
246
247
        return $this->manifest()->getAssetBasePath() . '/' . $fileInfo['dirname'] . '/' . $fileName;
248
    }
249
250
    /**
251
     * Получить Manifest
252
     * @return Manifest
253
     */
254
    protected function manifest()
255
    {
256
        if (!$this->manifest) {
257
            $this->manifest = new Manifest($this->manifestFilePath);
258
        }
259
260
        return $this->manifest;
261
    }
262
263
    /**
264
     * Получить карту asset`ов
265
     * @return \Dmitrynaum\SAM\Component\AssetMap
266
     */
267
    protected function resultMap()
268
    {
269
        return $this->manifest()->resultMap();
270
    }
271
272
    /**
273
     * Считать файлы и объединить
274
     * @param string[] $filePaths - список файлов
275
     * @return string
276
     */
277
    protected function readFiles($filePaths)
278
    {
279
        $filesContent = [];
280
281
        foreach ($filePaths as $filePath) {
282
            $filesContent[] = file_get_contents($filePath);
283
        }
284
285
        return join(PHP_EOL, $filesContent);
286
    }
287
288
    /**
289
     * Получить компессор asset`ов в зависимости от типа asset`а (css или js)
290
     * @param string $assetPath - имя asset`а
291
     * @return \MatthiasMullie\Minify\Minify
292
     */
293
    protected function getCompressor($assetPath)
294
    {
295
        $fileExtension = pathinfo($assetPath, PATHINFO_EXTENSION);
296
        $compressor    = null;
297
298
        switch ($fileExtension) {
299
            case 'js':
300
                $compressor = new \MatthiasMullie\Minify\JS();
301
                break;
302
            case 'css':
303
                $compressor = new \MatthiasMullie\Minify\CSS();
304
                break;
305
        }
306
307
        return $compressor;
308
    }
309
}
310