1
0
mirror of https://github.com/mrclay/minify.git synced 2025-03-13 08:59:38 +01:00
minify/lib/Minify/ClosureCompiler.php
2015-11-19 17:56:05 +02:00

239 lines
6.3 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>
*
* @todo unit tests, $options docs
* @todo more options support (or should just passthru them all?)
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author Elan Ruusamäe <glen@delfi.ee>
*/
class Minify_ClosureCompiler
{
const OPTION_CHARSET = 'charset';
const OPTION_COMPILATION_LEVEL = 'compilation_level';
public static $isDebug = false;
/**
* Filepath of the Closure Compiler jar file. This must be set before
* calling minifyJs().
*
* @var string
*/
public static $jarFile = null;
/**
* Writable temp directory. This must be set before calling minifyJs().
*
* @var string
*/
public static $tempDir = null;
/**
* Filepath of "java" executable (may be needed if not in shell's PATH)
*
* @var string
*/
public static $javaExecutable = 'java';
/**
* 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 join(' ', $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();
$o = array_merge(
array(
self::OPTION_CHARSET => 'utf-8',
self::OPTION_COMPILATION_LEVEL => 'SIMPLE_OPTIMIZATIONS',
),
$userOptions
);
$charsetOption = $o[self::OPTION_CHARSET];
if (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $charsetOption)) {
$args[] = "--charset {$charsetOption}";
}
foreach (array(self::OPTION_COMPILATION_LEVEL) as $opt) {
if ($o[$opt]) {
$args[] = "--{$opt} " . escapeshellarg($o[$opt]);
}
}
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
{
}