debugMode = $this->Application->isDebugMode(false); $this->resourceFolder = WRITEABLE . '/cache'; } /** * When used as non-block tag, then compress given files and return url to result * * @param Array $params * @return string * @access public */ public function CompressScriptTag($params) { // put to queue if ( array_key_exists('to', $params) ) { $files = $this->Application->GetVar($params['to'], ''); $this->Application->SetVar($params['to'], $files . '|' . $params['files']); return ''; } if ( array_key_exists('from', $params) ) { // get from queue $files = $this->Application->GetVar($params['from']); } else { // get from tag $files = $params['files']; } $files = $this->_getTemplatePaths( array_map('trim', explode('|', $files)) ); if ( !$files ) { trigger_error('No files specified for compression.', E_USER_NOTICE); return ''; } $extension = pathinfo($files[0], PATHINFO_EXTENSION); $save_as = isset($params['save_as']) ? $params['save_as'] : false; $dst_file = $this->resourceFolder . DIRECTORY_SEPARATOR . ($this->debugMode ? 'd' : 'c') . '_'; /** @var FileHelper $file_helper */ $file_helper = $this->Application->recallObject('FileHelper'); if ( $save_as ) { $dst_file .= $save_as . ( strpos($save_as, '.') === false ? '.' . $extension : '' ); } else { $dst_file .= $this->_getHash($file_helper->makeRelative($files)) . '.' . $extension; } $was_compressed = file_exists($dst_file); if ( !$was_compressed || ($this->debugMode && filemtime($dst_file) < $this->_getMaxFileDate($files)) ) { $string = ''; $path_length = strlen(FULL_PATH) + 1; foreach ($files as $file) { if ( !file_exists($file) ) { continue; } // add filename before for easier debugging if ( $this->debugMode ) { $string .= '/* === File: ' . substr($file, $path_length) . ' === */' . "\n"; $string .= '/* ' . str_repeat('=', strlen(substr($file, $path_length)) + 14) . ' */' . "\n\n"; } // add file content $string .= file_get_contents($file) . "\n\n"; } // replace templates base if ( isset($params['templates_base']) ) { $templates_base = $params['templates_base']; } else { $templates_base = $this->Application->ProcessParsedTag('m', 'TemplatesBase', Array ()); } $templates_base = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', BASE_PATH . '/', $templates_base); $string = str_replace('@templates_base@', rtrim($templates_base, '/'), $string); if ( !$this->debugMode ) { // don't compress merged js/css file in debug mode to allow js/css debugging $this->compressString($string, $extension); } // save compressed file file_put_contents($dst_file, $string); } return $file_helper->pathToUrl($dst_file) . '?ts=' . adodb_date('Y-m-d_H:i:s', filemtime($dst_file)); } /** * Returns maximal modification date across given files * * @param Array $files * @return int * @access protected */ protected function _getMaxFileDate($files) { $ret = 0; foreach ($files as $file) { if ( file_exists($file) ) { $ret = max($ret, filemtime($file)); } } return $ret; } /** * Returns hash string based on given files * * @param Array $files * @return int * @access protected */ protected function _getHash($files) { $hash = $files; if ($this->Application->isAdmin) { array_unshift($hash, 'A:1'); } else { array_unshift($hash, 'A:0;T:' . $this->Application->GetVar('m_theme')); } return kUtil::crc32(implode('|', $hash)); } /** * Deletes compression info file * * @todo also delete all listed there files * @access public */ public function delete() { $iterator = new DirectoryIterator($this->resourceFolder); /** @var DirectoryIterator $file_info */ foreach ($iterator as $file_info) { if ( !$file_info->isDir() && preg_match('/^(c|d)_.*.(css|js)$/', $file_info->getFilename()) ) { unlink( $file_info->getPathname() ); } } } /** * Dumps the assets. * * @return void */ public function dump() { /** @var kCurlHelper $curl_helper */ $curl_helper = $this->Application->recallObject('CurlHelper'); $curl_helper->setOptions(array( CURLOPT_COOKIE => 'debug_off=1', )); $curl_helper->Send($this->Application->BaseURL()); } /** * Compress $string based on $extension * * @param string $string * @param string $extension * @return void * @access protected */ public function compressString(&$string, $extension) { $compression_engine = kUtil::getSystemConfig()->get('CompressionEngine'); if ( !$compression_engine ) { // compression method not specified - use none return; } switch ( $compression_engine ) { case 'yui': $this->compressViaJava($string, $extension); break; case 'php': $this->compressViaPHP($string, $extension); break; } } /** * Compresses string using YUI compressor (uses Java) * * @param string $string * @param string $extension * @return void * @access protected */ protected function compressViaJava(&$string, $extension) { $tmp_file = tempnam('/tmp', 'to_compress_'); file_put_contents($tmp_file, $string); $command = 'java -jar ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'yuicompressor-2.4.8.jar --type ' . $extension . ' --charset utf-8 ' . $tmp_file; $string = shell_exec($command); unlink($tmp_file); } /** * Compresses string using PHP compressor * * @param string $string * @param string $extension * @return void * @access protected */ protected function compressViaPHP(&$string, $extension) { /** @var JsMinifyHelper $minifier */ $minifier = $this->Application->makeClass($extension == 'js' ? 'JsMinifyHelper' : 'CssMinifyHelper'); $string = $minifier->minify($string); } /** * Get full paths on disk for each of given templates * * @param Array $templates * @return Array * @access protected */ protected function _getTemplatePaths($templates) { $ret = Array (); $reg_exp = '/^' . preg_quote($this->Application->BaseURL(), '/') . '(.*)/'; foreach ($templates as $template) { if ( !$template ) { continue; } if ( preg_match($reg_exp, $template, $regs) ) { // full url (from current domain) to a file $ret[] = FULL_PATH . '/' . $regs[1]; } elseif ( strpos($template, '{module_path}') !== false ) { $ret = array_merge($ret, $this->_moduleInclude($template)); } else { $ret[] = $this->Application->TemplatesCache->GetRealFilename($template); } } return $ret; } /** * * @param string $template * @return Array * @access protected */ protected function _moduleInclude($template) { $ret = $included = Array (); foreach ($this->Application->ModuleInfo as $module_name => $module_data) { if ( $module_name == 'In-Portal' ) { continue; } $module_prefix = $this->Application->isAdmin ? mb_strtolower($module_name) . '/' : $module_data['TemplatePath']; if ( in_array($module_prefix, $included) ) { continue; } $ret[] = $this->Application->TemplatesCache->GetRealFilename(str_replace('{module_path}', $module_prefix, $template)); $included[] = $module_prefix; } return $ret; } }