<?php
/**
 * Project: 
 * ConLite Content Management System
 * 
 * Description: 
 * Class for creating and outputing only one compressed file
 * 
 * Requirements: 
 * @con_php_req 5.0
 * 
 *
 * @package    ConLite Content Types
 * @version    1.0.0
 * @author     Rene Mansveld
 * @copyright  ConLite Team <www.conlite.org>
 * @license    http://www.conlite.org/license/LIZENZ.txt
 * @link       http://www.conlite.org
 * @since      file available since ConLite release 2.0.0
 * 
 * {@internal 
 *   created 2012-12-15
 *
 *   $Id$:
 * }}
 * 
 */

/**
 * Class for creating and outputing only one compressed file
 */
abstract class Output_Compressor {
    
    /**
     * Worker-Function generate()
     *
     * @param string $sCachePath - Path to the cache folder
     * @param array string $asFiles - Files to include in the compressed output file
     * @return string - Returns the name of the compressed file in the cache folder
     * @access public
     */
    public function generate($sCachePath, $asFiles, $sFileExt, $sFrontendPath) {
        
        # Check for input files
        if ((!is_array($asFiles)) || (!count($asFiles))) {
            return false;
        }
        
        $lastCacheTime = 0;
        $lastChangeTime = 0;
        
        # Generate a unique filename for the compressed file
        $md5 = md5(implode('', $asFiles));
        
        # Check if a file with this name exists in the cache folder
        $found = self::findMatchingFilenames($sCachePath, strtolower($sFileExt), $md5);
        if ($found !== false) {
            # Matching files were found, check which one ist the latest one
            for ($i = 0, $n = count($found); $i < $n; $i ++) {
                $time = substr($found[$i], strlen($md5), 14);
                if ($time > $lastCacheTime) {
                    $lastCacheTime = $time;
                }
            }
        }
        
        # Get the date and time of the last modified input file
        for ($i = 0, $n = count($asFiles); $i < $n; $i ++) {
            if (file_exists($asFiles[$i])) {
                $time = date('YmdHis', filemtime($asFiles[$i]));
                if ($time > $lastChangeTime) {
                    $lastChangeTime = $time;
                }
            }
        }
        
        if ($lastCacheTime == $lastChangeTime) {
            # If the cache file has the date and time of the last changed file, just return it's name
            return $md5 . $lastCacheTime . '.' . $sFileExt;
        } else {
            # Create new cache files
            $content = '';
            
            # Read the input files
            for ($i = 0, $n = count($asFiles); $i < $n; $i ++) {
                if (file_exists($asFiles[$i])) {
                    $cnt = file_get_contents($asFiles[$i]);
                    # Remove file include commands (those files were added to the array or HTML head section)
                    if (strrpos($asFiles[$i], '/') > 0) {
                        $path = substr($asFiles[$i], 0, (strrpos($asFiles[$i], '/') + 1));
                    } else {
                        $path = '';
                    }
                    $p1 = strpos(strtolower($cnt), '@import url(');
                    while ($p1 !== false) {
                        $p2 = strpos($cnt, ');', $p1);
                        $cnt = substr($cnt, 0, $p1) . substr($cnt, ($p2 + 2));
                        $p1 = strpos(strtolower($cnt), '@import url(');
                    }
                    
                    $content .= $cnt . "\n";
                }
            }
            
            # Compress the content
            switch ($sFileExt) {
                case 'css':
                    # Remove '/* ... */' comments
                    $content = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $content);
                    
                    # Convert line breaks and tabs to a single space
                    $content = str_replace(array("\r\n", "\r", "\n", "\t"), ' ', $content);
                    
                    # Convert multiple spaces to a single space
                    while (strpos($content, '  ') !== false) {
                        $content = str_replace('  ', ' ', $content);
                    }
                    
                    # Remove preceding and trailing spaces
                    $content = trim($content);
                    
                    # Correct paths starting with '../' to start with the client HTML path
                    $content = str_replace(array('../../', '../'), $sFrontendPath, $content);
                    
                    break;
                case 'js':
                    # Convert tabs to a single space
                    $content = str_replace("\t", ' ', $content);
                    
                    # Convert multiple spaces to a single space
                    while (strpos($content, '  ') !== false) {
                        $content = str_replace('  ', ' ', $content);
                    }
                    
                    # Remove blank lines
                    $content = str_replace(array("\r\n\r\n", "\r\r", "\n\n"), "\n", $content);
                    
                    # Remove preceding and trailing spaces from each line and
                    $aCnt = explode("\n", $content);
                    for ($i = 0, $n = count($aCnt); $i < $n; $i ++) {
                        $aCnt[$i] = trim($aCnt[$i]);
                    }
                    $content = implode("\n", $aCnt);
                    
                    # Remove remaining blank lines
                    while (strpos($content, "\n\n") !== false) {
                        $content = str_replace("\n\n", "\n", $content);
                    }
                    
                    break;
            }
            
            # Save a new uncompressed cache file, use date and time of the last modified file in the filename
            $fp = fopen($sCachePath . $md5 . $lastChangeTime . '.' . $sFileExt, 'w');
            fwrite($fp, $content);
            fclose($fp);
            
            if (extension_loaded('zlib')) {
                # Save a new compressed cache file, use date and time of the last modified file in the filename
                $fp = fopen($sCachePath . $md5 . $lastChangeTime . '.' . $sFileExt . '.gz', 'w');
                fwrite($fp, gzencode($content, 6, FORCE_GZIP));
                fclose($fp);
            }
            
            # Delete all older versions of the cache file
            for ($i = 0, $n = count($found); $i < $n; $i ++) {
                @unlink($sCachePath . $found[$i]);
                @unlink($sCachePath . $found[$i] . '.gz');
            }
            
            # Return the name of the new cache file
            return $md5 . $lastChangeTime . '.' . $sFileExt;
        }
    }
    
    /**
     * Worker-Function output()
     *
     * @param string $sCachePath - Path to the cache folder
     * @param string $sFilename - Name of the file to deliver
     * @param string $sContentType - Content-Type of the file (eg 'css', 'html' or 'javascript')
     */
    public function output($sCachePath, $sFilename, $sContentType) {
        header('Content-Type: text/' . $sContentType);
        if ((strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) && (extension_loaded('zlib')) && (is_file($sCachePath . $sFilename . '.gz')) && (!ini_get('zlib.output_compression'))) {
            header('Content-Length: ' . filesize($sCachePath . $sFilename . '.gz'));
            header('Content-Encoding: gzip');
            readfile($sCachePath . $sFilename . '.gz');
        } else {
            header('Content-Length: ' . filesize($sCachePath . $sFilename));
            readfile($sCachePath . $sFilename);
        }
    }
    
    /**
     * private helper function findMatchingFilenames()
     *
     * @param string $sCachePath - Path to the cache folder
     * @param string $sFileExt - filename extension (eg css)
     * @param string $sFilenamePart - Beginning part of the filename
     * @return - Returns an array of the matching files found or false
     * @access private
     */
    private function findMatchingFilenames($sCachePath, $sFileExt, $sFilenamePart) {
        $pattern = '[0123456789]';
        # Find only files with 14 numerical characters in the filename (yyyymmddHHiiss)
        $files = glob($sCachePath . $sFilenamePart . str_repeat($pattern, 14) . '.' . $sFileExt);
        if (empty($files)) {
            # No suiting filename was found
            return false;
        } else {
            # Return the filenames of the found files
            return array_map('basename', $files);
        }
    }
}
?>