Completed
Push — master ( fdaddd...d64c68 )
by Josh
15:44
created

ClosureCompilerApplication   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 142
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 97.87%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 1
dl 0
loc 142
ccs 46
cts 47
cp 0.9787
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A getCacheDifferentiator() 0 16 2
A getClosureCompilerBinHash() 0 11 2
A testFilepaths() 0 11 3
B minify() 0 40 6
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 11
	public function __construct($filepath = null)
46
	{
47 11
		if (isset($filepath))
48
		{
49 9
			$this->closureCompilerBin = $filepath;
50 9
			$this->testFilepaths();
51
		}
52 10
	}
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 3
	public function minify($src)
81
	{
82 3
		$this->testFilepaths();
83 1
		$options = ($this->options) ? ' ' . $this->options : '';
84
85
		// Add our custom externs if default externs are disabled
86 1
		if ($this->excludeDefaultExterns && $this->compilationLevel === 'ADVANCED_OPTIMIZATIONS')
87
		{
88 1
			$options .= ' --externs ' . __DIR__ . '/../externs.application.js --env=CUSTOM';
89
		}
90
91 1
		$crc     = crc32($src);
92 1
		$inFile  = sys_get_temp_dir() . '/' . $crc . '.js';
93 1
		$outFile = sys_get_temp_dir() . '/' . $crc . '.min.js';
94
95 1
		file_put_contents($inFile, $src);
96
97 1
		$cmd = escapeshellcmd($this->javaBin)
98 1
		     . ' -jar ' . escapeshellarg($this->closureCompilerBin)
99 1
		     . ' --compilation_level ' . escapeshellarg($this->compilationLevel)
100 1
		     . $options
101 1
		     . ' --js ' . escapeshellarg($inFile)
102 1
		     . ' --js_output_file ' . escapeshellarg($outFile);
103
104 1
		exec($cmd . ' 2>&1', $output, $return);
105 1
		unlink($inFile);
106
107 1
		if (file_exists($outFile))
108
		{
109 1
			$src = trim(file_get_contents($outFile));
110 1
			unlink($outFile);
111
		}
112
113 1
		if (!empty($return))
114
		{
115
			throw new RuntimeException('An error occured during minification: ' . implode("\n", $output));
116
		}
117
118 1
		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 11
	protected function testFilepaths()
144
	{
145 11
		if (!isset($this->closureCompilerBin))
146
		{
147 1
			throw new RuntimeException('No path set for Closure Compiler');
148
		}
149 10
		if (!file_exists($this->closureCompilerBin))
150
		{
151 2
			throw new RuntimeException('Cannot find Closure Compiler at ' . $this->closureCompilerBin);
152
		}
153
	}
154
}