Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
jsmin.php
1 <?php
48 class JSMin {
49  const ORD_LF = 10;
50  const ORD_SPACE = 32;
51 
52  protected $a = '';
53  protected $b = '';
54  protected $input = '';
55  protected $inputIndex = 0;
56  protected $inputLength = 0;
57  protected $lookAhead = null;
58  protected $output = '';
59 
60  // -- Public Static Methods --------------------------------------------------
61 
62  public static function minify($js) {
63  $jsmin = new JSMin($js);
64  return $jsmin->min();
65  }
66 
67  // -- Public Instance Methods ------------------------------------------------
68 
69  public function __construct($input) {
70  $this->input = str_replace("\r\n", "\n", $input);
71  $this->inputLength = strlen($this->input);
72  }
73 
74  // -- Protected Instance Methods ---------------------------------------------
75 
76  protected function action($d) {
77  switch($d) {
78  case 1:
79  $this->output .= $this->a;
80 
81  case 2:
82  $this->a = $this->b;
83 
84  if ($this->a === "'" || $this->a === '"') {
85  for (;;) {
86  $this->output .= $this->a;
87  $this->a = $this->get();
88 
89  if ($this->a === $this->b) {
90  break;
91  }
92 
93  if (ord($this->a) <= self::ORD_LF) {
94  throw new JSMinException('Unterminated string literal.');
95  }
96 
97  if ($this->a === '\\') {
98  $this->output .= $this->a;
99  $this->a = $this->get();
100  }
101  }
102  }
103 
104  case 3:
105  $this->b = $this->next();
106 
107  if ($this->b === '/' && (
108  $this->a === '(' || $this->a === ',' || $this->a === '=' ||
109  $this->a === ':' || $this->a === '[' || $this->a === '!' ||
110  $this->a === '&' || $this->a === '|' || $this->a === '?')) {
111 
112  $this->output .= $this->a . $this->b;
113 
114  for (;;) {
115  $this->a = $this->get();
116 
117  if ($this->a === '/') {
118  break;
119  } elseif ($this->a === '\\') {
120  $this->output .= $this->a;
121  $this->a = $this->get();
122  } elseif (ord($this->a) <= self::ORD_LF) {
123  throw new JSMinException('Unterminated regular expression '.
124  'literal.');
125  }
126 
127  $this->output .= $this->a;
128  }
129 
130  $this->b = $this->next();
131  }
132  }
133  }
134 
135  protected function get() {
136  $c = $this->lookAhead;
137  $this->lookAhead = null;
138 
139  if ($c === null) {
140  if ($this->inputIndex < $this->inputLength) {
141  $c = substr($this->input, $this->inputIndex, 1);
142  $this->inputIndex += 1;
143  } else {
144  $c = null;
145  }
146  }
147 
148  if ($c === "\r") {
149  return "\n";
150  }
151 
152  if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
153  return $c;
154  }
155 
156  return ' ';
157  }
158 
159  protected function isAlphaNum($c) {
160  return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
161  }
162 
163  protected function min() {
164  $this->a = "\n";
165  $this->action(3);
166 
167  while ($this->a !== null) {
168  switch ($this->a) {
169  case ' ':
170  if ($this->isAlphaNum($this->b)) {
171  $this->action(1);
172  } else {
173  $this->action(2);
174  }
175  break;
176 
177  case "\n":
178  switch ($this->b) {
179  case '{':
180  case '[':
181  case '(':
182  case '+':
183  case '-':
184  $this->action(1);
185  break;
186 
187  case ' ':
188  $this->action(3);
189  break;
190 
191  default:
192  if ($this->isAlphaNum($this->b)) {
193  $this->action(1);
194  }
195  else {
196  $this->action(2);
197  }
198  }
199  break;
200 
201  default:
202  switch ($this->b) {
203  case ' ':
204  if ($this->isAlphaNum($this->a)) {
205  $this->action(1);
206  break;
207  }
208 
209  $this->action(3);
210  break;
211 
212  case "\n":
213  switch ($this->a) {
214  case '}':
215  case ']':
216  case ')':
217  case '+':
218  case '-':
219  case '"':
220  case "'":
221  $this->action(1);
222  break;
223 
224  default:
225  if ($this->isAlphaNum($this->a)) {
226  $this->action(1);
227  }
228  else {
229  $this->action(3);
230  }
231  }
232  break;
233 
234  default:
235  $this->action(1);
236  break;
237  }
238  }
239  }
240 
241  return $this->output;
242  }
243 
244  protected function next() {
245  $c = $this->get();
246 
247  if ($c === '/') {
248  switch($this->peek()) {
249  case '/':
250  for (;;) {
251  $c = $this->get();
252 
253  if (ord($c) <= self::ORD_LF) {
254  return $c;
255  }
256  }
257 
258  case '*':
259  $this->get();
260 
261  for (;;) {
262  switch($this->get()) {
263  case '*':
264  if ($this->peek() === '/') {
265  $this->get();
266  return ' ';
267  }
268  break;
269 
270  case null:
271  throw new JSMinException('Unterminated comment.');
272  }
273  }
274 
275  default:
276  return $c;
277  }
278  }
279 
280  return $c;
281  }
282 
283  protected function peek() {
284  $this->lookAhead = $this->get();
285  return $this->lookAhead;
286  }
287 }
288 
289 // -- Exceptions ---------------------------------------------------------------
290 class JSMinException extends Exception {}
291 ?>
292