Completed
Push — master ( 71f345...e29597 )
by Josh
20:28
created

getCacheDifferentiator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
ccs 8
cts 8
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
	*/
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
	*/
45 14
	public function __construct($filepath = null)
46
	{
47 14
		if (isset($filepath))
48
		{
49 12
			$this->closureCompilerBin = $filepath;
50 12
			$this->testFilepaths();
51
		}
52 13
	}
53
54
	/**
55
	* {@inheritdoc}
56
	*/
57 7
	public function getCacheDifferentiator()
58
	{
59
		$key = [
60 7
			$this->compilationLevel,
61 7
			$this->excludeDefaultExterns,
62 7
			$this->options,
63 7
			$this->getClosureCompilerBinHash()
64
		];
65
66 7
		if ($this->excludeDefaultExterns)
67
		{
68 7
			$key[] = file_get_contents(__DIR__ . '/../externs.application.js');
69
		}
70
71 7
		return $key;
72
	}
73
74
	/**
75
	* Compile given JavaScript source via the Closure Compiler application
76
	*
77
	* @param  string $src JavaScript source
78
	* @return string      Compiled source
79
	*/
80 6
	public function minify($src)
81
	{
82 6
		$this->testFilepaths();
83 4
		$options = ($this->options) ? ' ' . $this->options : '';
84
85
		// Add our custom externs if default externs are disabled
86 4
		if ($this->excludeDefaultExterns && $this->compilationLevel === 'ADVANCED_OPTIMIZATIONS')
87
		{
88 2
			$options .= ' --externs ' . __DIR__ . '/../externs.application.js --env=CUSTOM';
89
		}
90
91 4
		$crc     = crc32($src);
92 4
		$inFile  = sys_get_temp_dir() . '/' . $crc . '.js';
93 4
		$outFile = sys_get_temp_dir() . '/' . $crc . '.min.js';
94
95 4
		file_put_contents($inFile, $src);
96
97 4
		$cmd = escapeshellcmd($this->javaBin)
98 4
		     . ' -jar ' . escapeshellarg($this->closureCompilerBin)
99 4
		     . ' --compilation_level ' . escapeshellarg($this->compilationLevel)
100 4
		     . $options
101 4
		     . ' --js ' . escapeshellarg($inFile)
102 4
		     . ' --js_output_file ' . escapeshellarg($outFile);
103
104 4
		exec($cmd . ' 2>&1', $output, $return);
105 4
		unlink($inFile);
106
107 4
		if (file_exists($outFile))
108
		{
109 2
			$src = trim(file_get_contents($outFile));
110 2
			unlink($outFile);
111
		}
112
113 4
		if (!empty($return))
114
		{
115 2
			throw new RuntimeException('An error occured during minification: ' . implode("\n", $output));
116
		}
117
118 2
		return $src;
119
	}
120
121
	/**
122
	* Compute and return the hash for current Closure Compiler binary
123
	*
124
	* @return string
125
	*/
126 7
	protected function getClosureCompilerBinHash()
127
	{
128
		// Caching the value saves time during testing but has little to no real-world impact
129 7
		static $cache = [];
130 7
		if (!isset($cache[$this->closureCompilerBin]))
131
		{
132 3
			$cache[$this->closureCompilerBin] = md5_file($this->closureCompilerBin);
133
		}
134
135 7
		return $cache[$this->closureCompilerBin];
136
	}
137
138
	/**
139
	* Test that the Closure Compiler file exists
140
	*
141
	* @return void
142
	*/
143 14
	protected function testFilepaths()
144
	{
145 14
		if (!isset($this->closureCompilerBin))
146
		{
147 1
			throw new RuntimeException('No path set for Closure Compiler');
148
		}
149 13
		if (!file_exists($this->closureCompilerBin))
150
		{
151 2
			throw new RuntimeException('Cannot find Closure Compiler at ' . $this->closureCompilerBin);
152
		}
153
	}
154
}