AssetBuilder::disableCompressor()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
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()->getAssetBasePathFromProjectRoot();
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
            $assetSavePath = $this->manifest()->getAssetBasePathFromProjectRoot() . '/' . $assetName;
168
            $assetWebPath  = $this->manifest()->getAssetBasePath() . '/' . $assetName;
169
            
170
            // Если необходимо сжать asset`ы
171
            if ($this->isCompressorEnabled()) {
172
                // Получаем объект для минимфикации
173
                $compressor = $this->getCompressor($assetName);
174
                
175
                // Сообщаем ему что необходимо минимфицировать
176
                $compressor->add($assetContent);
177
                
178
                // Минифицируем ассет
179
                $assetContent = $compressor->minify();
180
            }
181
182
            // Если необходимо заморозить
183
            if ($this->isFreezingEnabled()) {
184
                // Вычисляем путь до файла в который будет сохранен asset
185
                $assetHash = sha1($assetContent);
186
                $assetSavePath = $this->getFreezingAssetFileName($assetName, $assetHash);
187
            }
188
            
189
            // Сохраняем asset
190
            $this->saveAsset($assetSavePath, $assetContent);
191
192
            // Добавляем путь до asset файла в карту asset`ов
193
            $this->resultMap()->addAsset($assetName, $assetWebPath);
194
        }
195
    }
196
    
197
    /**
198
     * Сохранить ассет в файл
199
     * @param string $assetPath путь до файла
200
     * @param string $content контент
201
     */
202
    protected function saveAsset($assetPath, $content)
203
    {
204
        $dirName = dirname($assetPath);
205
        
206
        if (!is_dir($dirName)) {
207
            mkdir($dirName, 0777, true);
208
        }
209
        
210
        file_put_contents($assetPath, $content);
211
    }
212
    
213
    /**
214
     * Получить список asset`ов и файлы которые в них используются
215
     * @param array $assets
216
     * @return array список файлов используемых в ассетах
217
     */
218
    protected function getFilesPaths($assets)
219
    {
220
        $assetFilePaths = [];
221
        foreach ($assets as $assetName => $assetFiles) {
222
            $assetFilePaths[$assetName] = $this->resolveAssetFilesPaths($assets, $assetFiles);
223
        }
224
        
225
        return $assetFilePaths;
226
    }
227
    
228
    /**
229
     * Получить список файлов ассета
230
     * @param array $assets
231
     * @param array $assetFiles
232
     * @return array
233
     */
234
    protected function resolveAssetFilesPaths($assets, $assetFiles)
235
    {
236
        $assetFilePaths = [];
237
        // Обходим все файлы
238
        foreach ($assetFiles as $assetNameOrFile) {
239
            // Если в списке ассетов есть ассет с именем $assetNameOrFile
240
            if (isset($assets[$assetNameOrFile])) {
241
                // это имя ассета файлы которого необходимо использовать
242
                $assetFiles     = $assets[$assetNameOrFile];
243
                $assetFilePaths = array_merge($assetFilePaths, $this->resolveAssetFilesPaths($assets, $assetFiles));
244
            } else {
245
                // это путь до файла который необходимо использовать
246
                $assetFilePaths[] = $assetNameOrFile;
247
            }
248
        }
249
250
        return $assetFilePaths;
251
    }
252
253
    /**
254
     * Получить имя замороженного файла asset`а
255
     * @param string $assetName - Имя asset`а
256
     * @param string $assetHash - Хэш asset`а
257
     * @return string
258
     */
259
    protected function getFreezingAssetFileName($assetName, $assetHash)
260
    {
261
        $fileInfo = pathinfo($assetName);
262
        $fileName = $fileInfo['filename'] . '-' . $assetHash . '.' . $fileInfo['extension'];
263
264
        return $this->manifest()->getAssetBasePathFromProjectRoot() . '/' . $fileInfo['dirname'] . '/' . $fileName;
265
    }
266
267
    /**
268
     * Получить Manifest
269
     * @return Manifest
270
     */
271
    protected function manifest()
272
    {
273
        if (!$this->manifest) {
274
            $this->manifest = new Manifest($this->manifestFilePath);
275
        }
276
277
        return $this->manifest;
278
    }
279
280
    /**
281
     * Получить карту asset`ов
282
     * @return \Dmitrynaum\SAM\Component\AssetMap
283
     */
284
    protected function resultMap()
285
    {
286
        return $this->manifest()->resultMap();
287
    }
288
289
    /**
290
     * Считать файлы и объединить
291
     * @param string[] $filePaths - список файлов
292
     * @return string
293
     */
294
    protected function readFiles($filePaths)
295
    {
296
        $filesContent = [];
297
298
        foreach ($filePaths as $filePath) {
299
            $filesContent[] = file_get_contents($filePath);
300
        }
301
302
        return join(PHP_EOL, $filesContent);
303
    }
304
305
    /**
306
     * Получить компессор asset`ов в зависимости от типа asset`а (css или js)
307
     * @param string $assetPath - имя asset`а
308
     * @return \MatthiasMullie\Minify\Minify
309
     */
310
    protected function getCompressor($assetPath)
311
    {
312
        $fileExtension = pathinfo($assetPath, PATHINFO_EXTENSION);
313
        $compressor    = null;
314
315
        switch ($fileExtension) {
316
            case 'js':
317
                $compressor = new \MatthiasMullie\Minify\JS();
318
                break;
319
            case 'css':
320
                $compressor = new \MatthiasMullie\Minify\CSS();
321
                break;
322
        }
323
324
        return $compressor;
325
    }
326
}
327