Completed
Push — master ( b73028...4be9a3 )
by Josh
16:55
created

getClosureCompilerBinHash()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 9.4286
cc 2
eloc 5
nc 2
nop 0
crap 2
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2016 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Configurator\JavaScript\Minifiers;
9
10
use RuntimeException;
11
use s9e\TextFormatter\Configurator\JavaScript\Minifier;
12
13
class ClosureCompilerApplication extends Minifier
14
{
15
	/**
16
	* @var string Path to the Closure Compiler application
17
	*/
18
	public $closureCompilerBin;
19
20
	/**
21
	* @var string Closure Compiler's compilation level
22
	*/
23
	public $compilationLevel = 'ADVANCED_OPTIMIZATIONS';
24
25
	/**
26
	* @var bool Whether to exclude Closure Compiler's default externs
27
	*/
28
	public $excludeDefaultExterns = true;
29
30
	/**
31
	* @var string Path to java interpreter
32
	*/
33
	public $javaBin = 'java';
34
35
	/**
36
	* @var string Extra options to be passed to the Closure Compiler application
37
	*/
38
	public $options = '--use_types_for_optimization';
39
40
	/**
41
	* Constructor
42
	*
43
	* @param  string $filepath Path to the Closure Compiler .jar
44
	* @return void
45
	*/
46 13
	public function __construct($filepath = null)
47
	{
48 13
		if (isset($filepath))
49 13
		{
50 11
			$this->closureCompilerBin = $filepath;
51 11
			$this->testFilepaths();
52 10
		}
53 12
	}
54
55
	/**
56
	* {@inheritdoc}
57
	*/
58 7
	public function getCacheDifferentiator()
59
	{
60
		$key = [
61 7
			$this->compilationLevel,
62 7
			$this->excludeDefaultExterns,
63 7
			$this->options,
64 7
			$this->getClosureCompilerBinHash()
65 7
		];
66
67 7
		if ($this->excludeDefaultExterns)
68 7
		{
69 7
			$key[] = file_get_contents(__DIR__ . '/../externs.application.js');
70 7
		}
71
72 7
		return $key;
73
	}
74
75
	/**
76
	* Compile given JavaScript source via the Closure Compiler application
77
	*
78
	* @param  string $src JavaScript source
79
	* @return string      Compiled source
80
	*/
81 5
	public function minify($src)
82
	{
83 5
		$this->testFilepaths();
84 3
		$options = ($this->options) ? ' ' . $this->options : '';
85
86
		// Add our custom externs if default externs are disabled
87 3
		if ($this->excludeDefaultExterns && $this->compilationLevel === 'ADVANCED_OPTIMIZATIONS')
88 3
		{
89 1
			$options .= ' --externs ' . __DIR__ . '/../externs.application.js --env=CUSTOM';
90 1
		}
91
92 3
		$crc     = crc32($src);
93 3
		$inFile  = sys_get_temp_dir() . '/' . $crc . '.js';
94 3
		$outFile = sys_get_temp_dir() . '/' . $crc . '.min.js';
95
96 3
		file_put_contents($inFile, $src);
97
98 3
		$cmd = escapeshellcmd($this->javaBin)
99 3
		     . ' -jar ' . escapeshellarg($this->closureCompilerBin)
100 3
		     . ' --compilation_level ' . escapeshellarg($this->compilationLevel)
101 3
		     . $options
102 3
		     . ' --js ' . escapeshellarg($inFile)
103 3
		     . ' --js_output_file ' . escapeshellarg($outFile);
104
105 3
		exec($cmd . ' 2>/dev/null', $output, $return);
106 3
		unlink($inFile);
107
108 3
		if (file_exists($outFile))
109 3
		{
110 2
			$src = trim(file_get_contents($outFile));
111 2
			unlink($outFile);
112 2
		}
113
114 3
		if (!empty($return))
115 3
		{
116 1
			throw new RuntimeException('An error occured during minification');
117
		}
118
119 2
		return $src;
120
	}
121
122
	/**
123
	* Compute and return the hash for current Closure Compiler binary
124
	*
125
	* @return string
126
	*/
127 7
	protected function getClosureCompilerBinHash()
128
	{
129
		// Caching the value saves time during testing but has little to no real-world impact
130 7
		static $cache = [];
131 7
		if (!isset($cache[$this->closureCompilerBin]))
132 7
		{
133 3
			$cache[$this->closureCompilerBin] = md5_file($this->closureCompilerBin);
134 3
		}
135
136 7
		return $cache[$this->closureCompilerBin];
137
	}
138
139
	/**
140
	* Test that the Closure Compiler file exists
141
	*
142
	* @return void
143
	*/
144 13
	protected function testFilepaths()
145
	{
146 13
		if (!isset($this->closureCompilerBin))
147 13
		{
148 1
			throw new RuntimeException('No path set for Closure Compiler');
149
		}
150 12
		if (!file_exists($this->closureCompilerBin))
151 12
		{
152 2
			throw new RuntimeException('Cannot find Closure Compiler at ' . $this->closureCompilerBin);
153
		}
154
	}
155
}