Completed
Push — master ( 203a2d...5654c3 )
by Josh
17:17
created

getClosureCompilerBinHash()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
ccs 5
cts 5
cp 1
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2018 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
	* @deprecated 1.3.0 Set a command instead
18
	*/
19
	public $closureCompilerBin;
20
21
	/**
22
	* @var string Command used to invoke the Closure Compiler application
23
	*/
24
	public $command;
25
26
	/**
27
	* @var string Closure Compiler's compilation level
28
	*/
29
	public $compilationLevel = 'ADVANCED_OPTIMIZATIONS';
30
31
	/**
32
	* @var bool Whether to exclude Closure Compiler's default externs
33
	*/
34
	public $excludeDefaultExterns = true;
35
36
	/**
37
	* @var string Path to java interpreter
38
	* @deprecated 1.3.0 Set a command instead
39
	*/
40
	public $javaBin = 'java';
41
42
	/**
43
	* @var string Extra options to be passed to the Closure Compiler application
44
	*/
45
	public $options = '--use_types_for_optimization';
46
47
	/**
48
	* Constructor
49
	*
50
	* @param  string $filepathOrCommand Path to the Closure Compiler .jar or command to execute
51
	*/
52 14
	public function __construct($filepathOrCommand = null)
53
	{
54 14
		if (isset($filepathOrCommand))
55
		{
56 12
			if (file_exists($filepathOrCommand) && substr($filepathOrCommand, -4) === '.jar')
57
			{
58 8
				$this->closureCompilerBin = $filepathOrCommand;
59
			}
60
			else
61
			{
62 4
				$this->command = $filepathOrCommand;
63
			}
64
		}
65 14
	}
66
67
	/**
68
	* {@inheritdoc}
69
	*/
70 6
	public function getCacheDifferentiator()
71
	{
72
		$key = [
73 6
			$this->compilationLevel,
74 6
			$this->excludeDefaultExterns,
75 6
			$this->options
76
		];
77 6
		if (isset($this->closureCompilerBin))
78
		{
79 6
			$key[] = $this->getClosureCompilerBinHash();
80
		}
81
82 6
		if ($this->excludeDefaultExterns)
83
		{
84 6
			$key[] = file_get_contents(__DIR__ . '/../externs.application.js');
85
		}
86
87 6
		return $key;
88
	}
89
90
	/**
91
	* Compile given JavaScript source via the Closure Compiler application
92
	*
93
	* @param  string $src JavaScript source
94
	* @return string      Compiled source
95
	*/
96 8
	public function minify($src)
97
	{
98 8
		$this->testFilepaths();
99 6
		$options = ($this->options) ? ' ' . $this->options : '';
100
101
		// Add our custom externs if default externs are disabled
102 6
		if ($this->excludeDefaultExterns && $this->compilationLevel === 'ADVANCED_OPTIMIZATIONS')
103
		{
104 1
			$options .= ' --externs ' . __DIR__ . '/../externs.application.js --env=CUSTOM';
105
		}
106
107 6
		$crc     = crc32($src);
108 6
		$inFile  = sys_get_temp_dir() . '/' . $crc . '.js';
109 6
		$outFile = sys_get_temp_dir() . '/' . $crc . '.min.js';
110
111 6
		file_put_contents($inFile, $src);
112
113 6
		if (isset($this->command))
114
		{
115 4
			$cmd = $this->command;
116
		}
117
		else
118
		{
119 2
			$cmd = escapeshellcmd($this->javaBin) . ' -jar ' . escapeshellarg($this->closureCompilerBin);
120
		}
121
122 6
		$cmd .= ' --compilation_level ' . escapeshellarg($this->compilationLevel)
123 6
		     . $options
124 6
		     . ' --js ' . escapeshellarg($inFile)
125 6
		     . ' --js_output_file ' . escapeshellarg($outFile);
126
127 6
		exec($cmd . ' 2>&1', $output, $return);
128 6
		unlink($inFile);
129
130 6
		if (file_exists($outFile))
131
		{
132 4
			$src = trim(file_get_contents($outFile));
133 4
			unlink($outFile);
134
		}
135
136 6
		if (!empty($return))
137
		{
138 2
			throw new RuntimeException('An error occured during minification: ' . implode("\n", $output));
139
		}
140
141 4
		return $src;
142
	}
143
144
	/**
145
	* Compute and return the hash for current Closure Compiler binary
146
	*
147
	* @return string
148
	*/
149 6
	protected function getClosureCompilerBinHash()
150
	{
151
		// Caching the value saves time during testing but has little to no real-world impact
152 6
		static $cache = [];
153 6
		if (!isset($cache[$this->closureCompilerBin]))
154
		{
155 2
			$cache[$this->closureCompilerBin] = md5_file($this->closureCompilerBin);
156
		}
157
158 6
		return $cache[$this->closureCompilerBin];
159
	}
160
161
	/**
162
	* Test that the Closure Compiler file exists
163
	*
164
	* @return void
165
	*/
166 8
	protected function testFilepaths()
167
	{
168 8
		if (isset($this->command))
169
		{
170 4
			return;
171
		}
172 4
		if (!isset($this->closureCompilerBin))
173
		{
174 1
			throw new RuntimeException('No path set for Closure Compiler');
175
		}
176 3
		if (!file_exists($this->closureCompilerBin))
177
		{
178 1
			throw new RuntimeException('Cannot find Closure Compiler at ' . $this->closureCompilerBin);
179
		}
180
	}
181
}