240 lines
6 KiB
PHP
240 lines
6 KiB
PHP
<?php
|
|
/**
|
|
* Class Minify_ClosureCompiler
|
|
* @package Minify
|
|
*/
|
|
|
|
/**
|
|
* Compress Javascript using the Closure Compiler
|
|
*
|
|
* You must set $jarFile and $tempDir before calling the minify functions.
|
|
* Also, depending on your shell's environment, you may need to specify
|
|
* the full path to java in $javaExecutable or use putenv() to setup the
|
|
* Java environment.
|
|
*
|
|
* <code>
|
|
* Minify_ClosureCompiler::$jarFile = '/path/to/closure-compiler-20120123.jar';
|
|
* Minify_ClosureCompiler::$tempDir = '/tmp';
|
|
* $code = Minify_ClosureCompiler::minify(
|
|
* $code,
|
|
* array('compilation_level' => 'SIMPLE_OPTIMIZATIONS')
|
|
* );
|
|
*
|
|
* --compilation_level WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS
|
|
*
|
|
* </code>
|
|
*
|
|
* @package Minify
|
|
* @author Stephen Clay <steve@mrclay.org>
|
|
* @author Elan Ruusamäe <glen@delfi.ee>
|
|
*/
|
|
class Minify_ClosureCompiler
|
|
{
|
|
public static $isDebug = false;
|
|
|
|
/**
|
|
* Filepath of the Closure Compiler jar file. This must be set before
|
|
* calling minifyJs().
|
|
*
|
|
* @var string
|
|
*/
|
|
public static $jarFile;
|
|
|
|
/**
|
|
* Writable temp directory. This must be set before calling minifyJs().
|
|
*
|
|
* @var string
|
|
*/
|
|
public static $tempDir;
|
|
|
|
/**
|
|
* Filepath of "java" executable (may be needed if not in shell's PATH)
|
|
*
|
|
* @var string
|
|
*/
|
|
public static $javaExecutable = 'java';
|
|
|
|
/**
|
|
* Default command line options passed to closure-compiler
|
|
*
|
|
* @var array
|
|
*/
|
|
public static $defaultOptions = array(
|
|
'charset' => 'utf-8',
|
|
'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
|
|
'warning_level' => 'QUIET',
|
|
);
|
|
|
|
/**
|
|
* Minify a Javascript string
|
|
*
|
|
* @param string $js
|
|
* @param array $options (verbose is ignored)
|
|
* @see https://code.google.com/p/closure-compiler/source/browse/trunk/README
|
|
* @return string
|
|
* @throws Minify_ClosureCompiler_Exception
|
|
*/
|
|
public static function minify($js, $options = array())
|
|
{
|
|
$min = new static();
|
|
|
|
return $min->process($js, $options);
|
|
}
|
|
|
|
/**
|
|
* Process $js using $options.
|
|
*
|
|
* @param string $js
|
|
* @param array $options
|
|
* @return string
|
|
* @throws Exception
|
|
* @throws Minify_ClosureCompiler_Exception
|
|
*/
|
|
public function process($js, $options)
|
|
{
|
|
$tmpFile = $this->dumpFile(self::$tempDir, $js);
|
|
try {
|
|
$result = $this->compile($tmpFile, $options);
|
|
} catch (Exception $e) {
|
|
unlink($tmpFile);
|
|
throw $e;
|
|
}
|
|
unlink($tmpFile);
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param string $tmpFile
|
|
* @param array $options
|
|
* @return string
|
|
* @throws Minify_ClosureCompiler_Exception
|
|
*/
|
|
protected function compile($tmpFile, $options)
|
|
{
|
|
$command = $this->getCommand($options, $tmpFile);
|
|
|
|
return implode("\n", $this->shell($command));
|
|
}
|
|
|
|
/**
|
|
* @param array $userOptions
|
|
* @param string $tmpFile
|
|
* @return string
|
|
*/
|
|
protected function getCommand($userOptions, $tmpFile)
|
|
{
|
|
$args = array_merge(
|
|
$this->getCompilerCommandLine(),
|
|
$this->getOptionsCommandLine($userOptions)
|
|
);
|
|
|
|
return implode(' ', $args) . ' ' . escapeshellarg($tmpFile);
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
* @throws Minify_ClosureCompiler_Exception
|
|
*/
|
|
protected function getCompilerCommandLine()
|
|
{
|
|
$this->checkJar(self::$jarFile);
|
|
$server = array(
|
|
self::$javaExecutable,
|
|
'-jar',
|
|
escapeshellarg(self::$jarFile)
|
|
);
|
|
|
|
return $server;
|
|
}
|
|
|
|
/**
|
|
* @param array $userOptions
|
|
* @return array
|
|
*/
|
|
protected function getOptionsCommandLine($userOptions)
|
|
{
|
|
$args = array();
|
|
|
|
$options = array_merge(
|
|
static::$defaultOptions,
|
|
$userOptions
|
|
);
|
|
|
|
foreach ($options as $key => $value) {
|
|
$args[] = "--{$key} " . escapeshellarg($value);
|
|
}
|
|
|
|
return $args;
|
|
}
|
|
|
|
/**
|
|
* @param string $jarFile
|
|
* @throws Minify_ClosureCompiler_Exception
|
|
*/
|
|
protected function checkJar($jarFile)
|
|
{
|
|
if (!is_file($jarFile)) {
|
|
throw new Minify_ClosureCompiler_Exception('$jarFile(' . $jarFile . ') is not a valid file.');
|
|
}
|
|
if (!is_readable($jarFile)) {
|
|
throw new Minify_ClosureCompiler_Exception('$jarFile(' . $jarFile . ') is not readable.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $tempDir
|
|
* @throws Minify_ClosureCompiler_Exception
|
|
*/
|
|
protected function checkTempdir($tempDir)
|
|
{
|
|
if (!is_dir($tempDir)) {
|
|
throw new Minify_ClosureCompiler_Exception('$tempDir(' . $tempDir . ') is not a valid direcotry.');
|
|
}
|
|
if (!is_writable($tempDir)) {
|
|
throw new Minify_ClosureCompiler_Exception('$tempDir(' . $tempDir . ') is not writable.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write $content to a temporary file residing in $dir.
|
|
*
|
|
* @param string $dir
|
|
* @param string $content
|
|
* @return string
|
|
* @throws Minify_ClosureCompiler_Exception
|
|
*/
|
|
protected function dumpFile($dir, $content)
|
|
{
|
|
$this->checkTempdir($dir);
|
|
$tmpFile = tempnam($dir, 'cc_');
|
|
if (!$tmpFile) {
|
|
throw new Minify_ClosureCompiler_Exception('Could not create temp file in "' . $dir . '".');
|
|
}
|
|
file_put_contents($tmpFile, $content);
|
|
|
|
return $tmpFile;
|
|
}
|
|
|
|
/**
|
|
* Execute command, throw if exit code is not in $expectedCodes array
|
|
*
|
|
* @param string $command
|
|
* @param array $expectedCodes
|
|
* @return mixed
|
|
* @throws Minify_ClosureCompiler_Exception
|
|
*/
|
|
protected function shell($command, $expectedCodes = array(0))
|
|
{
|
|
exec($command, $output, $result_code);
|
|
if (!in_array($result_code, $expectedCodes)) {
|
|
throw new Minify_ClosureCompiler_Exception("Unpexpected return code: $result_code");
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
class Minify_ClosureCompiler_Exception extends Exception
|
|
{
|
|
}
|