Completed
Push — master ( cca0b5...39139c )
by Josh
17:22
created

ClosureCompilerService   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 13
c 2
b 0
f 0
lcom 1
cbo 1
dl 0
loc 151
rs 10
ccs 55
cts 55
cp 1

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getCacheDifferentiator() 0 11 2
A minify() 0 11 2
B decodeResponse() 0 24 4
A generateRequestBody() 0 21 3
A query() 0 17 1
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 ClosureCompilerService extends Minifier
14
{
15
	/**
16
	* @var string Closure Compiler's compilation level
17
	*/
18
	public $compilationLevel = 'ADVANCED_OPTIMIZATIONS';
19
20
	/**
21
	* @var bool Whether to exclude Closure Compiler's default externs
22
	*/
23
	public $excludeDefaultExterns = true;
24
25
	/**
26
	* @var string Externs used for compilation
27
	*/
28
	public $externs;
29
30
	/**
31
	* @var integer Read timeout in seconds
32
	*/
33
	public $timeout = 10;
34
35
	/**
36
	* @var string Closure Compiler Service's URL
37
	*/
38
	public $url = 'http://closure-compiler.appspot.com/compile';
39
40
	/**
41
	* Constructor
42
	*/
43 12
	public function __construct()
44
	{
45 12
		$this->externs = file_get_contents(__DIR__ . '/../externs.service.js');
46 12
	}
47
48
	/**
49
	* {@inheritdoc}
50
	*/
51 4
	public function getCacheDifferentiator()
52
	{
53 4
		$key = [$this->compilationLevel, $this->excludeDefaultExterns];
54
55 4
		if ($this->excludeDefaultExterns)
56 4
		{
57 4
			$key[] = $this->externs;
58 4
		}
59
60 4
		return $key;
61
	}
62
63
	/**
64
	* Compile given JavaScript source via the Closure Compiler Service
65
	*
66
	* @param  string $src JavaScript source
67
	* @return string      Compiled source
68
	*/
69 8
	public function minify($src)
70
	{
71 8
		$body     = $this->generateRequestBody($src);
72 8
		$response = $this->query($body);
73 8
		if (!$response)
74 8
		{
75 1
			throw new RuntimeException('Could not contact the Closure Compiler service');
76
		}
77
78 7
		return $this->decodeResponse($response);
79
	}
80
81
	/**
82
	* Decode the response returned by the Closure Compiler service
83
	*
84
	* @param  string $response Response body
85
	* @return string           Minified code
86
	*/
87 7
	protected function decodeResponse($response)
88
	{
89 7
		$response = json_decode($response, true);
90 7
		if (is_null($response))
91 7
		{
92 1
			throw new RuntimeException('Closure Compiler service returned invalid JSON: ' . json_last_error_msg());
93
		}
94
95 6
		if (isset($response['serverErrors'][0]))
96 6
		{
97 1
			$error = $response['serverErrors'][0];
98
99 1
			throw new RuntimeException('Server error ' . $error['code'] . ': ' . $error['error']);
100
		}
101
102 5
		if (isset($response['errors'][0]))
103 5
		{
104 1
			$error = $response['errors'][0];
105
106 1
			throw new RuntimeException('Compilation error: ' . $error['error']);
107
		}
108
109 4
		return $response['compiledCode'];
110
	}
111
112
	/**
113
	* Generate the request body for given code
114
	*
115
	* @param  string $src JavaScript source
116
	* @return string      Compiled source
117
	*/
118 8
	protected function generateRequestBody($src)
119
	{
120
		$params = [
121 8
			'compilation_level' => $this->compilationLevel,
122 8
			'js_code'           => $src,
123 8
			'output_format'     => 'json',
124
			'output_info'       => 'compiled_code'
125 8
		];
126
127
		// Add our custom externs if default externs are disabled
128 8
		if ($this->excludeDefaultExterns && $this->compilationLevel === 'ADVANCED_OPTIMIZATIONS')
129 8
		{
130 7
			$params['exclude_default_externs'] = 'true';
131 7
			$params['js_externs'] = $this->externs;
132 7
		}
133
134
		// Add dupe variables by hand
135 8
		$body = http_build_query($params) . '&output_info=errors';
136
137 8
		return $body;
138
	}
139
140
	/**
141
	* Query the Closure Compiler service
142
	*
143
	* @param  string       $body Request body
144
	* @return string|false       Response body, or FALSE
145
	*/
146 8
	protected function query($body)
147
	{
148 8
		return file_get_contents(
149 8
			$this->url,
150 8
			false,
151 8
			stream_context_create([
152
				'http' => [
153 8
					'method'  => 'POST',
154 8
					'timeout' => $this->timeout,
155
					'header'  => "Connection: close\r\n"
156 8
					           . "Content-length: " . strlen($body) . "\r\n"
157 8
					           . "Content-type: application/x-www-form-urlencoded",
158
					'content' => $body
159 8
				]
160 8
			])
161 8
		);
162
	}
163
}