Skip to main content

A small PHP CLI script to compress (or minify) a CSS style sheet. It does require that you include the CommandLine parser class.

#!/usr/bin/php -q

<?php

/**
 * Compress CSS
 * 
 * A small PHP CLI script to compress (or minify) a CSS style sheet. 
 * It does require that you include the CommandLine parser class 
 * (http://jonlabelle.com/snippets/view/php/php-cli-parser).
 *
 * @author Jon LaBelle, http://jonlabelle.com
 * Date: Sun Sep 09 2012 16:25:10 GMT-05:00 (CDT)
 */

include 'CommandLine.php';

function show_help($display = true) {
  $script = basename($_SERVER['argv'][0]);
  $help = <<<ENDHELP
    Usage: {$script} [OPTIONS]

Compress a CSS style-sheet. Removes comments and extra spaces in code.

-s, --source=FILE    The source CSS file that will be compressed.
-w, --overwrite      Overwrite the source CSS file with the compressed output.
-o, --output=FILE    Specify the output of the compressed CSS file.
-h, --help           Display help and exit.

ENDHELP;
  if ($display) {
    echo $help . "\n";
  } else {
    return $help . "\n";
  }
}

/**
 * Remove spaces around curly brackets, colons, semi-colons, parenthesis and
 * commas.
 *
 * @param  string $input The CSS to process.
 * @return string
 */
function remove_extra_spaces($input) {
  return preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!', '$1', $input);
}

/**
 * Remove CSS comments
 *
 * @param  string $input The CSS to process.
 * @return string
 */
function remove_css_comments($input) {
  return preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $input);
}

/**
 * Delete directory recursively
 *
 * This PHP function scans a given directory and deletes all files and
 * subdirectories it finds and has permission to delete.
 *
 * Per default it removes the given directory totally, but you can
 * specify to just "empty" it instead. In this case it deletes
 * everything inside the given directory and keeps the directory itself.
 *
 * @author http://lixlpixel.org/recursive_function/php/recursive_directory_delete/
 * @param $directory string The path to directory.
 * @param $empty bool Set to true to leave the directory in tact, but empty all it's contents.
 * @return boolean
 */
function remove_dir($directory, $empty = false) {

  if (substr($directory, -1) == '/') { // remove appending slashes
    $directory = substr($directory, 0, -1);
  }

  if (!file_exists($directory) || !is_dir($directory)) {
    return false;

  } elseif (!is_readable($directory)) {
    return false;

  } else {

    $handle = opendir($directory);

    while (false !== ($item = readdir($handle))) {

      if ($item != '.' && $item != '..') {
        $path = $directory . '/' . $item;

        if (is_dir($path)) {
          remove_dir($path);
        } else {
          unlink($path);
        }
      }
    }

    closedir($handle);

    // if the option to empty is not set to true
    if ($empty === false) {
      if (!rmdir($directory)) {
        return false;
      }
    }

    return true;
  }
}

/**
 * Delete a file.
 *
 * @param string $file The complete path to file.
 */
function delete_file($file) {
  if (file_exists($file)) {
    unlink($file);
  }
}

/**
 * Reads the content of the file into a string.
 *
 * @param  string $file The complete file path.
 * @return string
 */
function read_file_contents($file) {
  $contents = '';

  if (file_exists($file)) {
    $fh = fopen($file, "r");
    $contents = fread($fh, filesize($file));
    fclose($fh);
  }

  return $contents;
}

/**
 * Write contents to a file.
 * NOTE: Will overwrite file if exists.
 *
 * @param string $file     The complete file path.
 * @param string $content  The content to write.
 * @return void
 */
function write_file_contents($file, $content) {
  $fh = fopen($file, 'w') or die("\nCannot open $file for writing.");
  fwrite($fh, $content);
  fclose($fh);
}

/**
 * Gets the file extensions.
 *
 * @param string $file
 * @return string  The file extension.
 */
function get_file_extension($file) {
  return strtolower(substr(strrchr($file, "."), 1));
}

// ---------------------------------------------------------------
// MAIN
// ---------------------------------------------------------------

// no arguments provided
if ($_SERVER['argc'] == 1) {
  fwrite(STDERR, show_help(false));
  exit(1);

} else {

  // Retrieve command-line arguments
  $allowed = array(
    's', 'source',
    'w', 'overwrite',
    'o', 'output',
    'h', 'help'
  );

  try {
    $options = CommandLine::parseOptions($_SERVER['argv'], $allowed);

  } catch (CommandLineOptionException $e) {
    fwrite(STDERR, $e->getMessage() . "\n" . show_help(false));
    exit(1);
  }
}

// show help if requested
if (isset($options['h']) || isset($options['help'])) {
  show_help();
  exit();
}

if (!isset($options['s']) && !isset($options['source'])) {
  fwrite(STDERR, 'ERROR: Source file option is not set. Use -s ' . 'or --source.' . "\n");
  exit(1);
}

$source_file      = (isset($options['s'])) ? $options['s'] : $options['source'];
$output_file      = false;
$overwrite_source = false;

if (isset($options['w']) && !isset($options['o']) || !isset($options['output'])) {
  $overwrite_source = true;

} elseif (isset($options['overwrite'])) {
  $overwrite_source = true;

} else {
  if (!isset($options['o']) && !isset($options['output'])) {
    fwrite(STDERR, 'ERROR: Output file not set, and you did not set the overwrite option (-w)' . "\n");
    exit(1);
  } else {
    $output_file = (isset($options['o'])) ? $options['o'] : $options['output'];
  }
}

if (!file_exists($source_file) || filesize($source_file) <= 0) {
  fwrite(STDERR, 'ERROR: Source (' . $source_file. ') file does not exist. Option -s ' . 'or --source.' . "\n");
  exit(1);
}

// finally, the meat and potatoes...
$source_file_contents = remove_css_comments(read_file_contents($source_file));
$source_file_contents = remove_extra_spaces($source_file_contents);

if ($overwrite_source === true) { // overwrite source file with the removed comments content?

  delete_file($source_file);
  write_file_contents($source_file, $source_file_contents);

} else {
  write_file_contents($output_file, $source_file_contents);
}

?>