'javascript URL', 'attr'=>[...]], ...] * @return void */ public static function process(array &$data, ?array $opts = []) { $opts = array_merge([ 'bundle' => false, ], $opts); $host = SITEHOST; if (empty($host)) { return; } $settings = \config::get(static::CONFIG_KEY, ''); $settings = \func::unserialize($settings); $save = false; do { $minPath = \bff::path('min'); $minUrl = \bff::url('min'); foreach ($data as &$v) { $minify = true; # пропускаем URL на сторонние сервера if (empty($v['url']) || mb_stripos($v['url'], $host) === false) { continue; } $path = str_replace($host, '', $v['url']); # пропускаем предположительно упакованные файлы if ( mb_stripos($v['url'], '.min') || mb_stripos($v['url'], '.pack') ) { $minify = false; } # отрезаем query (?x=1) if ($pos = mb_stripos($path, '?')) { $path = mb_substr($path, 0, $pos); } # отрезаем протокол foreach (['//','http://','https://'] as $proto) { if (mb_stripos($path, $proto) === 0) { $path = mb_substr($path, mb_strlen($proto)); break; } } # проверяем наличие файла по указанному пути $path = PATH_PUBLIC . trim($path, DIRECTORY_SEPARATOR); if (! file_exists($path)) { continue; } # проверка допустимых расширений $ext = \bff\utils\Files::getExtension($path); if (! in_array($ext, ['js', 'css'])) { continue; } $key = pathinfo($path, PATHINFO_FILENAME) . '.' . md5($path . $minUrl); # md5 пути к файлу $modified = filemtime($path); # дата и время последнего редактирования файла $minPathFile = $minPath . $key . '.' . $ext; if ( file_exists($minPathFile) && ! empty($settings[$key]['mod']) && $settings[$key]['mod'] == $modified ) { $v['url'] = $settings[$key]['url']; } else { if ($minify) { if ($ext == 'js') { $res = static::js($path, $minPathFile); } else { $res = static::css($path, $minPathFile); } } else { @copy($path, $minPathFile); $res = true; } if ($res) { $hash = substr(md5_file($minPathFile), 0, 6); $settings[$key] = [ 'mod' => $modified, 'url' => $minUrl . $key . '.' . $ext . '?v=' . $hash, ]; $v['url'] = $settings[$key]['url']; $save = true; } } } unset($v); if (! $opts['bundle']) { break; } # соберем все css файлы в один $bundle = []; foreach ($data as $k => $v) { # пропускаем URL на сторонние сервера if (empty($v['url']) || mb_stripos($v['url'], $host) === false) { continue; } $path = str_replace($host, '', $v['url']); # отрезаем query (?x=1) if ($pos = mb_stripos($path, '?')) { $path = mb_substr($path, 0, $pos); } # отрезаем протокол foreach (['//','http://','https://'] as $proto) { if (mb_stripos($path, $proto) === 0) { $path = mb_substr($path, mb_strlen($proto)); break; } } # проверяем наличие файла по указанному пути $path = PATH_PUBLIC . trim($path, DIRECTORY_SEPARATOR); if (! file_exists($path)) { continue; } # проверка допустимых расширений $ext = \bff\utils\Files::getExtension($path); if ($ext != 'css') { continue; } $bundle[$k] = ['path' => $path, 'url' => $v['url']]; } if (empty($bundle) || count($bundle) == 1) { # если только один файл - нечего собирать break; } # название сборного файла $key = 'bundle.' . md5(json_encode($bundle)); $minPathFile = $minPath . $key . '.css'; if (file_exists($minPathFile) && ! empty($settings[$key]['mod']) && $settings[$key]['mod'] == filemtime($minPathFile)) { # есть в кеше и время не изменилось - отдадим файл $url = $settings[$key]['url']; } else { # собираем все файлы в один $fp = fopen($minPathFile, 'w'); if (! $fp) { break; } foreach ($bundle as $v) { $content = file_get_contents($v['path']); fwrite($fp, $content); } fclose($fp); # сохраним время создания $hash = substr(md5_file($minPathFile), 0, 6); $modified = filemtime($minPathFile); $url = $minUrl . $key . '.css?v=' . $hash; $settings[$key] = [ 'mod' => $modified, 'url' => $url, ]; $save = true; } if (! empty($url)) { $fst = true; foreach ($bundle as $k => $v) { if (isset($data[$k])) { if ($fst) { $data[$k] = $url; # подменяем урл первого файла на собранный $fst = false; } else { unset($data[$k]); # остальные удаляем } } } } } while (false); # сохраним время изменения файлов if ($save) { \config::save(static::CONFIG_KEY, serialize($settings)); } } /** * Минимизировать JS файл * @param string $source путь к исходному файлу * @param string $destination путь для сохранения результата * @return bool */ public static function js($source, $destination) { require_once modification(bff('path.core') . 'external/minifier/src/JS.php'); try { $minifier = new Minify\JS($source); $minifier->minify($destination); } catch (\Exception $e) { \bff::log($e->getMessage()); return false; } return true; } /** * Минимизировать CSS файл * @param string $source путь к исходному файлу или данные * @param string|null $destination путь для сохранения результата или null (вернуть результат) * @return bool|string */ public static function css($source, $destination) { require_once modification(bff('path.core') . 'external/minifier/src/Converter.php'); require_once modification(bff('path.core') . 'external/minifier/src/CSS.php'); try { $minifier = new Minify\CSS($source); if (is_string($destination)) { $minifier->minify($destination); } elseif (is_null($destination)) { return $minifier->minify(); } } catch (\Exception $e) { \bff::log($e->getMessage()); return false; } return true; } /** * Сбросить кеш */ public static function reset() { $path = \bff::path('min'); $files = \bff\utils\Files::getFiles($path); foreach ($files as $v) { if (mb_strpos($v, '.gitignore')) { continue; } @unlink($v); } \config::save(static::CONFIG_KEY, ''); } }