225 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			225 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
namespace W3TCL\YuiCssMin;
 | 
						|
 | 
						|
class Command
 | 
						|
{
 | 
						|
	const SUCCESS_EXIT = 0;
 | 
						|
	const FAILURE_EXIT = 1;
 | 
						|
 | 
						|
	protected $stats = array();
 | 
						|
 | 
						|
	public static function main()
 | 
						|
	{
 | 
						|
		$command = new self;
 | 
						|
		$command->run();
 | 
						|
	}
 | 
						|
 | 
						|
	public function run()
 | 
						|
	{
 | 
						|
		$opts = getopt(
 | 
						|
			'hi:o:',
 | 
						|
			array(
 | 
						|
				'help',
 | 
						|
				'input:',
 | 
						|
				'output:',
 | 
						|
				'dry-run',
 | 
						|
				'keep-sourcemap',
 | 
						|
				'keep-sourcemap-comment',
 | 
						|
				'linebreak-position:',
 | 
						|
				'memory-limit:',
 | 
						|
				'pcre-backtrack-limit:',
 | 
						|
				'pcre-recursion-limit:',
 | 
						|
				'remove-important-comments'
 | 
						|
			)
 | 
						|
		);
 | 
						|
 | 
						|
		$help = $this->getOpt(array('h', 'help'), $opts);
 | 
						|
		$input = $this->getOpt(array('i', 'input'), $opts);
 | 
						|
		$output = $this->getOpt(array('o', 'output'), $opts);
 | 
						|
		$dryrun = $this->getOpt('dry-run', $opts);
 | 
						|
		$keepSourceMapComment = $this->getOpt(array('keep-sourcemap', 'keep-sourcemap-comment'), $opts);
 | 
						|
		$linebreakPosition = $this->getOpt('linebreak-position', $opts);
 | 
						|
		$memoryLimit = $this->getOpt('memory-limit', $opts);
 | 
						|
		$backtrackLimit = $this->getOpt('pcre-backtrack-limit', $opts);
 | 
						|
		$recursionLimit = $this->getOpt('pcre-recursion-limit', $opts);
 | 
						|
		$removeImportantComments = $this->getOpt('remove-important-comments', $opts);
 | 
						|
 | 
						|
		if (!is_null($help)) {
 | 
						|
			$this->showHelp();
 | 
						|
			die( esc_html( self::SUCCESS_EXIT ) );
 | 
						|
		}
 | 
						|
 | 
						|
		if (is_null($input)) {
 | 
						|
			fwrite(STDERR, '-i <file> argument is missing' . PHP_EOL);
 | 
						|
			$this->showHelp();
 | 
						|
			die( esc_html( self::FAILURE_EXIT ) );
 | 
						|
		}
 | 
						|
 | 
						|
		if (!is_readable($input)) {
 | 
						|
			fwrite(STDERR, 'Input file is not readable' . PHP_EOL);
 | 
						|
			die( esc_html( self::FAILURE_EXIT ) );
 | 
						|
		}
 | 
						|
 | 
						|
		$css = file_get_contents($input);
 | 
						|
 | 
						|
		if ($css === false) {
 | 
						|
			fwrite(STDERR, 'Input CSS code could not be retrieved from input file' . PHP_EOL);
 | 
						|
			die( esc_html( self::FAILURE_EXIT ) );
 | 
						|
		}
 | 
						|
 | 
						|
		$this->setStat('original-size', strlen($css));
 | 
						|
 | 
						|
		$cssmin = new Minifier;
 | 
						|
 | 
						|
		if (!is_null($keepSourceMapComment)) {
 | 
						|
			$cssmin->keepSourceMapComment();
 | 
						|
		}
 | 
						|
 | 
						|
		if (!is_null($removeImportantComments)) {
 | 
						|
			$cssmin->removeImportantComments();
 | 
						|
		}
 | 
						|
 | 
						|
		if (!is_null($linebreakPosition)) {
 | 
						|
			$cssmin->setLineBreakPosition($linebreakPosition);
 | 
						|
		}
 | 
						|
 | 
						|
		if (!is_null($memoryLimit)) {
 | 
						|
			$cssmin->setMemoryLimit($memoryLimit);
 | 
						|
		}
 | 
						|
 | 
						|
		if (!is_null($backtrackLimit)) {
 | 
						|
			$cssmin->setPcreBacktrackLimit($backtrackLimit);
 | 
						|
		}
 | 
						|
 | 
						|
		if (!is_null($recursionLimit)) {
 | 
						|
			$cssmin->setPcreRecursionLimit($recursionLimit);
 | 
						|
		}
 | 
						|
 | 
						|
		$this->setStat('compression-time-start', microtime(true));
 | 
						|
 | 
						|
		$css = $cssmin->run($css);
 | 
						|
 | 
						|
		$this->setStat('compression-time-end', microtime(true));
 | 
						|
		$this->setStat('peak-memory-usage', memory_get_peak_usage(true));
 | 
						|
		$this->setStat('compressed-size', strlen($css));
 | 
						|
 | 
						|
		if (!is_null($dryrun)) {
 | 
						|
			$this->showStats();
 | 
						|
			die( esc_html( self::SUCCESS_EXIT ) );
 | 
						|
		}
 | 
						|
 | 
						|
		if (is_null($output)) {
 | 
						|
			fwrite(STDOUT, $css . PHP_EOL);
 | 
						|
			$this->showStats();
 | 
						|
			die( esc_html( self::SUCCESS_EXIT ) );
 | 
						|
		}
 | 
						|
 | 
						|
		if (!is_writable(dirname($output))) {
 | 
						|
			fwrite(STDERR, 'Output file is not writable' . PHP_EOL);
 | 
						|
			die( esc_html( self::FAILURE_EXIT ) );
 | 
						|
		}
 | 
						|
 | 
						|
		if (file_put_contents($output, $css) === false) {
 | 
						|
			fwrite(STDERR, 'Compressed CSS code could not be saved to output file' . PHP_EOL);
 | 
						|
			die( esc_html( self::FAILURE_EXIT ) );
 | 
						|
		}
 | 
						|
 | 
						|
		$this->showStats();
 | 
						|
 | 
						|
		die( esc_html( self::SUCCESS_EXIT ) );
 | 
						|
	}
 | 
						|
 | 
						|
	protected function getOpt($opts, $options)
 | 
						|
	{
 | 
						|
		$value = null;
 | 
						|
 | 
						|
		if (is_string($opts)) {
 | 
						|
			$opts = array($opts);
 | 
						|
		}
 | 
						|
 | 
						|
		foreach ($opts as $opt) {
 | 
						|
			if (array_key_exists($opt, $options)) {
 | 
						|
				$value = $options[$opt];
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return $value;
 | 
						|
	}
 | 
						|
 | 
						|
	protected function setStat($statName, $statValue)
 | 
						|
	{
 | 
						|
		$this->stats[$statName] = $statValue;
 | 
						|
	}
 | 
						|
 | 
						|
	protected function formatBytes($size, $precision = 2)
 | 
						|
	{
 | 
						|
		$base = log($size, 1024);
 | 
						|
		$suffixes = array('B', 'K', 'M', 'G', 'T');
 | 
						|
		return round(pow(1024, $base - floor($base)), $precision) .' '. $suffixes[floor($base)];
 | 
						|
	}
 | 
						|
 | 
						|
	protected function formatMicroSeconds($microSecs, $precision = 2)
 | 
						|
	{
 | 
						|
		// ms
 | 
						|
		$time = round($microSecs * 1000, $precision);
 | 
						|
 | 
						|
		if ($time >= 60 * 1000) {
 | 
						|
			$time = round($time / 60 * 1000, $precision) .' m'; // m
 | 
						|
		} elseif ($time >= 1000) {
 | 
						|
			$time = round($time / 1000, $precision) .' s'; // s
 | 
						|
		} else {
 | 
						|
			$time .= ' ms';
 | 
						|
		}
 | 
						|
 | 
						|
		return $time;
 | 
						|
	}
 | 
						|
 | 
						|
	protected function showStats()
 | 
						|
	{
 | 
						|
		$spaceSavings = round((1 - ($this->stats['compressed-size'] / $this->stats['original-size'])) * 100, 2);
 | 
						|
		$compressionRatio = round($this->stats['original-size'] / $this->stats['compressed-size'], 2);
 | 
						|
		$compressionTime = $this->formatMicroSeconds(
 | 
						|
			$this->stats['compression-time-end'] - $this->stats['compression-time-start']
 | 
						|
		);
 | 
						|
		$peakMemoryUsage = $this->formatBytes($this->stats['peak-memory-usage']);
 | 
						|
 | 
						|
		print esc_html(
 | 
						|
			<<<EOT
 | 
						|
 | 
						|
------------------------------
 | 
						|
CSSMIN STATS
 | 
						|
------------------------------
 | 
						|
Space savings:       {$spaceSavings} %
 | 
						|
Compression ratio:   {$compressionRatio}:1
 | 
						|
Compression time:    $compressionTime
 | 
						|
Peak memory usage:   $peakMemoryUsage
 | 
						|
 | 
						|
 | 
						|
EOT
 | 
						|
		);
 | 
						|
	}
 | 
						|
 | 
						|
	protected function showHelp()
 | 
						|
	{
 | 
						|
		print <<<'EOT'
 | 
						|
Usage: cssmin [options] -i <file> [-o <file>]
 | 
						|
 | 
						|
  -i|--input <file>              File containing uncompressed CSS code.
 | 
						|
  -o|--output <file>             File to use to save compressed CSS code.
 | 
						|
 | 
						|
Options:
 | 
						|
 | 
						|
  -h|--help                      Prints this usage information.
 | 
						|
  --dry-run                      Performs a dry run displaying statistics.
 | 
						|
  --keep-sourcemap[-comment]     Keeps the sourcemap special comment in the output.
 | 
						|
  --linebreak-position <pos>     Splits long lines after a specific column in the output.
 | 
						|
  --memory-limit <limit>         Sets the memory limit for this script.
 | 
						|
  --pcre-backtrack-limit <limit> Sets the PCRE backtrack limit for this script.
 | 
						|
  --pcre-recursion-limit <limit> Sets the PCRE recursion limit for this script.
 | 
						|
  --remove-important-comments    Removes !important comments from output.
 | 
						|
 | 
						|
EOT;
 | 
						|
	}
 | 
						|
}
 |