[], 'individualStylesheetPaths' => [], 'stylesheet' => [], 'script' => [], ]; protected static $_configLoaded = false; protected static $_configChanged = false; protected static function _getConfigFileName() { return WWW_PATH . '/_lib/packer.config.php'; } public static function loadConfig() { if (Qs_Packer::$_configLoaded) { return false; } $file = Qs_Packer::_getConfigFileName(); if (!file_exists($file)) { $content = " $href, 'attribs' => $options]; continue; } if (!Qs_Packer::isRegisteredStylesheet($options['href'])) { Qs_Packer::registerStylesheet($options['href']); } $path = $_path = dirname($options['href']); $media = Qs_Array::get($options, 'media', 'screen'); $isIndividualPath = false; do { if (in_array($_path, Qs_Packer::$_config['individualStylesheetPaths'])) { $isIndividualPath = true; break; } } while ('.' != ($_path = dirname($_path))); if (!$isIndividualPath) { $path = Qs_Packer::$_defaultStylesheetPath; } $itemIndex = $path . ':' . $media; if (!array_key_exists($itemIndex, $indexes)) { $indexes[$itemIndex] = [ 'path' => $path, 'media' => $media, 'indexes' => [], ]; } $indexes[$itemIndex]['indexes'][] = Qs_Packer::getStylesheetIndex($options['href']); } foreach ($indexes as $options) { $href = $options['path'] . '/' . implode('-', $options['indexes']) . Qs_Packer::_getSuffix() . '.merge.css'; $mergedStylesheets = [ // 1-st argument in doc->addStylesheet function 'href' => $href, // 2-nd argument in doc->addStylesheet function 'attribs' => [ 'media' => $options['media'], ], ]; array_unshift($result, $mergedStylesheets); } Qs_Packer::updateConfig(); return $result; } public static function getMergedScripts($list) { if (empty($list)) { return false; } Qs_Packer::loadConfig(); $result = []; $indexes = []; foreach ($list as $options) { if (!Qs_Packer::_isLocalFile($options['src']) || Qs_Array::get($options, 'skipPacking', false)) { $result[] = $options['src']; continue; } if (!Qs_Packer::isRegisteredScript($options['src'])) { Qs_Packer::registerScript($options['src']); } $path = $_path = dirname($options['src']); $isIndividualPath = false; do { if (in_array($_path, Qs_Packer::$_config['individualScriptPaths'])) { $isIndividualPath = true; break; } } while ('.' != ($_path = dirname($_path))); if (!$isIndividualPath) { $path = Qs_Packer::$_defaultScriptPath; } $indexes[$path][] = Qs_Packer::getScriptIndex($options['src']); } foreach ($indexes as $path => $pathIndexes) { array_unshift($result, $path . '/' . implode('-', $pathIndexes) . Qs_Packer::_getSuffix() . '.merge.js'); } Qs_Packer::updateConfig(); return $result; } protected static function _isLocalFile($file) { if (false === ($fileHost = parse_url($file, PHP_URL_HOST))) { return false; } if ($fileHost && $fileHost != parse_url(constant('BASE_URL'), PHP_URL_HOST)) { return false; } return true; } public static function merge($indexes, $file, $suffix, $extension) { if (empty($indexes)) { return false; } Qs_Packer::loadConfig(); switch ($extension) { case 'css': $files = Qs_Packer::$_config['stylesheet']; $delimiter = "\n"; break; case 'js': $files = Qs_Packer::$_config['script']; $delimiter = ";\n"; break; default: return false; } $list = []; foreach ($indexes as $index) { if (array_key_exists($index, $files)) { $list[$index] = $files[$index]; if ($suffix == 'pack' && false !== ($pos = strrpos($list[$index], '.'))) { $list[$index] = substr($list[$index], 0, $pos) . '.' . $suffix . '.' . $extension; } } } if (!$handle = fopen(WWW_PATH . '/' . $file, 'w+')) { return false; } foreach ($list as $name) { $content = file_get_contents(WWW_PATH . '/' . $name); if ('css' === $extension) { $content = self::prepareCssResourcePaths(dirname($name), dirname($file), $content); } fwrite($handle, $content . $delimiter); } fclose($handle); return true; } public static function createMerge($file) { $basename = basename($file); $parts = explode('.', $basename); $indexes = explode('-', array_shift($parts)); $extension = array_pop($parts); array_pop($parts); $suffix = array_pop($parts); return self::merge($indexes, $file, $suffix, $extension); } /** * Method replace related paths for resources inside css * @param string $srcPath Relative path of source file (e.g.: css/modules) * @param string $dstPath Relative path of destination file (e.g.: css) * @param string $content Source file content * @return string * @throws Exception */ public static function prepareCssResourcePaths($srcPath, $dstPath, $content) { $srcPath = trim($srcPath, DIRECTORY_SEPARATOR); $dstPath = trim($dstPath, DIRECTORY_SEPARATOR); if ($srcPath == $dstPath || parse_url($srcPath, PHP_URL_HOST) || parse_url($dstPath, PHP_URL_HOST)) { return $content; } $newRelativePath = self::getRelativePath($dstPath, $srcPath); $content = preg_replace_callback( '#url\(\s*(([\'"])?([^\s\)]+)?\1?)\s*\)#', function ($match) use ($newRelativePath) { $quote = $match[2]; $path = $match[1]; if (0 === strpos($path, 'data:')) { return $match[0]; } if ($quote) { $path = trim($path, $quote); } if (false !== strpos($path, '://') || '/' === $path[0]) { return $match[0]; } while (0 === strpos($path, '../') && $newRelativePath && $newRelativePath != '.') { $newRelativePath = dirname($newRelativePath); $path = substr($path, 3); } if ($newRelativePath && $newRelativePath != '.') { $path = $newRelativePath . '/' . $path; } return "url({$quote}{$path}{$quote})"; }, $content ); return $content; } public static function getRelativePath($srcPath, $dstPath) { $srcPath = $srcPath ? explode(DIRECTORY_SEPARATOR, trim($srcPath, DIRECTORY_SEPARATOR)) : []; $dstPath = $dstPath ? explode(DIRECTORY_SEPARATOR, trim($dstPath, DIRECTORY_SEPARATOR)) : []; while ($srcPath && $dstPath && $srcPath[0] == $dstPath[0]) { array_shift($srcPath); array_shift($dstPath); } $relativePath = trim(str_repeat('/..', count($srcPath)), DIRECTORY_SEPARATOR); if ($dstPath) { $relativePath .= ($relativePath ? '/' : '') . implode('/', $dstPath); } return $relativePath; } }