81 public static function minify(
$s, $statementsOnOwnLine =
false, $maxLineLength = 1000 ) {
116 '!' => self::TYPE_UN_OP,
117 '~' => self::TYPE_UN_OP,
118 'delete' => self::TYPE_UN_OP,
119 'new' => self::TYPE_UN_OP,
120 'typeof' => self::TYPE_UN_OP,
121 'void' => self::TYPE_UN_OP,
122 '++' => self::TYPE_INCR_OP,
123 '--' => self::TYPE_INCR_OP,
124 '!=' => self::TYPE_BIN_OP,
125 '!==' => self::TYPE_BIN_OP,
126 '%' => self::TYPE_BIN_OP,
127 '%=' => self::TYPE_BIN_OP,
128 '&' => self::TYPE_BIN_OP,
129 '&&' => self::TYPE_BIN_OP,
130 '&=' => self::TYPE_BIN_OP,
131 '*' => self::TYPE_BIN_OP,
132 '*=' => self::TYPE_BIN_OP,
133 '+=' => self::TYPE_BIN_OP,
134 '-=' => self::TYPE_BIN_OP,
135 '.' => self::TYPE_BIN_OP,
136 '/' => self::TYPE_BIN_OP,
137 '/=' => self::TYPE_BIN_OP,
138 '<' => self::TYPE_BIN_OP,
139 '<<' => self::TYPE_BIN_OP,
140 '<<=' => self::TYPE_BIN_OP,
141 '<=' => self::TYPE_BIN_OP,
142 '=' => self::TYPE_BIN_OP,
143 '==' => self::TYPE_BIN_OP,
144 '===' => self::TYPE_BIN_OP,
145 '>' => self::TYPE_BIN_OP,
146 '>=' => self::TYPE_BIN_OP,
147 '>>' => self::TYPE_BIN_OP,
148 '>>=' => self::TYPE_BIN_OP,
149 '>>>' => self::TYPE_BIN_OP,
150 '>>>=' => self::TYPE_BIN_OP,
151 '^' => self::TYPE_BIN_OP,
152 '^=' => self::TYPE_BIN_OP,
153 '|' => self::TYPE_BIN_OP,
154 '|=' => self::TYPE_BIN_OP,
155 '||' => self::TYPE_BIN_OP,
156 'in' => self::TYPE_BIN_OP,
157 'instanceof' => self::TYPE_BIN_OP,
158 '+' => self::TYPE_ADD_OP,
159 '-' => self::TYPE_ADD_OP,
160 '?' => self::TYPE_HOOK,
161 ':' => self::TYPE_COLON,
162 ',' => self::TYPE_COMMA,
163 ';' => self::TYPE_SEMICOLON,
164 '{' => self::TYPE_BRACE_OPEN,
165 '}' => self::TYPE_BRACE_CLOSE,
166 '(' => self::TYPE_PAREN_OPEN,
167 '[' => self::TYPE_PAREN_OPEN,
168 ')' => self::TYPE_PAREN_CLOSE,
169 ']' => self::TYPE_PAREN_CLOSE,
170 'break' => self::TYPE_RETURN,
171 'continue' => self::TYPE_RETURN,
172 'return' => self::TYPE_RETURN,
173 'throw' => self::TYPE_RETURN,
174 'catch' => self::TYPE_IF,
175 'for' => self::TYPE_IF,
176 'if' => self::TYPE_IF,
177 'switch' => self::TYPE_IF,
178 'while' => self::TYPE_IF,
179 'with' => self::TYPE_IF,
180 'case' => self::TYPE_DO,
181 'do' => self::TYPE_DO,
182 'else' => self::TYPE_DO,
183 'finally' => self::TYPE_DO,
184 'try' => self::TYPE_DO,
185 'var' => self::TYPE_DO,
186 'function' => self::TYPE_FUNC
193 self::STATEMENT =>
array(
194 self::TYPE_UN_OP => self::EXPRESSION,
195 self::TYPE_INCR_OP => self::EXPRESSION,
196 self::TYPE_ADD_OP => self::EXPRESSION,
197 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
198 self::TYPE_RETURN => self::EXPRESSION_NO_NL,
199 self::TYPE_IF => self::CONDITION,
200 self::TYPE_FUNC => self::CONDITION,
201 self::TYPE_LITERAL => self::EXPRESSION_OP
203 self::CONDITION =>
array(
204 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
206 self::PROPERTY_ASSIGNMENT =>
array(
207 self::TYPE_COLON => self::PROPERTY_EXPRESSION,
208 self::TYPE_BRACE_OPEN => self::STATEMENT
210 self::EXPRESSION =>
array(
211 self::TYPE_SEMICOLON => self::STATEMENT,
212 self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
213 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
214 self::TYPE_FUNC => self::EXPRESSION_FUNC,
215 self::TYPE_LITERAL => self::EXPRESSION_OP
217 self::EXPRESSION_NO_NL =>
array(
218 self::TYPE_SEMICOLON => self::STATEMENT,
219 self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
220 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
221 self::TYPE_FUNC => self::EXPRESSION_FUNC,
222 self::TYPE_LITERAL => self::EXPRESSION_OP
224 self::EXPRESSION_OP =>
array(
225 self::TYPE_BIN_OP => self::EXPRESSION,
226 self::TYPE_ADD_OP => self::EXPRESSION,
227 self::TYPE_HOOK => self::EXPRESSION_TERNARY,
228 self::TYPE_COLON => self::STATEMENT,
229 self::TYPE_COMMA => self::EXPRESSION,
230 self::TYPE_SEMICOLON => self::STATEMENT,
231 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
233 self::EXPRESSION_FUNC =>
array(
234 self::TYPE_BRACE_OPEN => self::STATEMENT
236 self::EXPRESSION_TERNARY =>
array(
237 self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
238 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
239 self::TYPE_FUNC => self::EXPRESSION_TERNARY_FUNC,
240 self::TYPE_LITERAL => self::EXPRESSION_TERNARY_OP
242 self::EXPRESSION_TERNARY_OP =>
array(
243 self::TYPE_BIN_OP => self::EXPRESSION_TERNARY,
244 self::TYPE_ADD_OP => self::EXPRESSION_TERNARY,
245 self::TYPE_HOOK => self::EXPRESSION_TERNARY,
246 self::TYPE_COMMA => self::EXPRESSION_TERNARY,
247 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
249 self::EXPRESSION_TERNARY_FUNC =>
array(
250 self::TYPE_BRACE_OPEN => self::STATEMENT
252 self::PAREN_EXPRESSION =>
array(
253 self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
254 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
255 self::TYPE_FUNC => self::PAREN_EXPRESSION_FUNC,
256 self::TYPE_LITERAL => self::PAREN_EXPRESSION_OP
258 self::PAREN_EXPRESSION_OP =>
array(
259 self::TYPE_BIN_OP => self::PAREN_EXPRESSION,
260 self::TYPE_ADD_OP => self::PAREN_EXPRESSION,
261 self::TYPE_HOOK => self::PAREN_EXPRESSION,
262 self::TYPE_COLON => self::PAREN_EXPRESSION,
263 self::TYPE_COMMA => self::PAREN_EXPRESSION,
264 self::TYPE_SEMICOLON => self::PAREN_EXPRESSION,
265 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
267 self::PAREN_EXPRESSION_FUNC =>
array(
268 self::TYPE_BRACE_OPEN => self::STATEMENT
270 self::PROPERTY_EXPRESSION =>
array(
271 self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
272 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
273 self::TYPE_FUNC => self::PROPERTY_EXPRESSION_FUNC,
274 self::TYPE_LITERAL => self::PROPERTY_EXPRESSION_OP
276 self::PROPERTY_EXPRESSION_OP =>
array(
277 self::TYPE_BIN_OP => self::PROPERTY_EXPRESSION,
278 self::TYPE_ADD_OP => self::PROPERTY_EXPRESSION,
279 self::TYPE_HOOK => self::PROPERTY_EXPRESSION,
280 self::TYPE_COMMA => self::PROPERTY_ASSIGNMENT,
281 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
283 self::PROPERTY_EXPRESSION_FUNC =>
array(
284 self::TYPE_BRACE_OPEN => self::STATEMENT
292 self::STATEMENT =>
array(
293 self::TYPE_BRACE_OPEN => self::STATEMENT,
294 self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
296 self::CONDITION =>
array(
297 self::TYPE_PAREN_OPEN => self::STATEMENT
299 self::PROPERTY_ASSIGNMENT =>
array(
300 self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT
302 self::EXPRESSION =>
array(
303 self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
304 self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
306 self::EXPRESSION_NO_NL =>
array(
307 self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
308 self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
310 self::EXPRESSION_OP =>
array(
311 self::TYPE_HOOK => self::EXPRESSION,
312 self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
314 self::EXPRESSION_FUNC =>
array(
315 self::TYPE_BRACE_OPEN => self::EXPRESSION_OP
317 self::EXPRESSION_TERNARY =>
array(
318 self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP,
319 self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
321 self::EXPRESSION_TERNARY_OP =>
array(
322 self::TYPE_HOOK => self::EXPRESSION_TERNARY,
323 self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
325 self::EXPRESSION_TERNARY_FUNC =>
array(
326 self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP
328 self::PAREN_EXPRESSION =>
array(
329 self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP,
330 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
332 self::PAREN_EXPRESSION_OP =>
array(
333 self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
335 self::PAREN_EXPRESSION_FUNC =>
array(
336 self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP
338 self::PROPERTY_EXPRESSION =>
array(
339 self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP,
340 self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
342 self::PROPERTY_EXPRESSION_OP =>
array(
343 self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
345 self::PROPERTY_EXPRESSION_FUNC =>
array(
346 self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP
352 self::STATEMENT =>
array( self::TYPE_BRACE_CLOSE =>
true ),
353 self::PROPERTY_ASSIGNMENT =>
array( self::TYPE_BRACE_CLOSE =>
true ),
354 self::EXPRESSION =>
array( self::TYPE_BRACE_CLOSE =>
true ),
355 self::EXPRESSION_NO_NL =>
array( self::TYPE_BRACE_CLOSE =>
true ),
356 self::EXPRESSION_OP =>
array( self::TYPE_BRACE_CLOSE =>
true ),
357 self::EXPRESSION_TERNARY_OP =>
array( self::TYPE_COLON =>
true ),
358 self::PAREN_EXPRESSION =>
array( self::TYPE_PAREN_CLOSE =>
true ),
359 self::PAREN_EXPRESSION_OP =>
array( self::TYPE_PAREN_CLOSE =>
true ),
360 self::PROPERTY_EXPRESSION =>
array( self::TYPE_BRACE_CLOSE =>
true ),
361 self::PROPERTY_EXPRESSION_OP =>
array( self::TYPE_BRACE_CLOSE =>
true )
366 self::EXPRESSION_NO_NL =>
array(
367 self::TYPE_UN_OP =>
true,
368 self::TYPE_INCR_OP =>
true,
369 self::TYPE_ADD_OP =>
true,
370 self::TYPE_BRACE_OPEN =>
true,
371 self::TYPE_PAREN_OPEN =>
true,
372 self::TYPE_RETURN =>
true,
373 self::TYPE_IF =>
true,
374 self::TYPE_DO =>
true,
375 self::TYPE_FUNC =>
true,
376 self::TYPE_LITERAL =>
true
378 self::EXPRESSION_OP =>
array(
379 self::TYPE_UN_OP =>
true,
380 self::TYPE_INCR_OP =>
true,
381 self::TYPE_BRACE_OPEN =>
true,
382 self::TYPE_RETURN =>
true,
383 self::TYPE_IF =>
true,
384 self::TYPE_DO =>
true,
385 self::TYPE_FUNC =>
true,
386 self::TYPE_LITERAL =>
true
394 $newlineBefore =
array(
395 self::STATEMENT =>
array(
396 self::TYPE_BRACE_CLOSE =>
true,
399 $newlineAfter =
array(
400 self::STATEMENT =>
array(
401 self::TYPE_BRACE_OPEN =>
true,
402 self::TYPE_PAREN_CLOSE =>
true,
403 self::TYPE_SEMICOLON =>
true,
409 self::EXPRESSION_OP =>
true,
410 self::EXPRESSION_TERNARY_OP =>
true,
411 self::PAREN_EXPRESSION_OP =>
true,
412 self::PROPERTY_EXPRESSION_OP =>
true
419 $length = strlen(
$s );
421 $newlineFound =
true;
425 while( $pos < $length ) {
428 $skip = strspn(
$s,
" \t\n\r\xb\xc", $pos );
431 if( $ch ===
'/' && substr(
$s, $pos, 2 ) ===
'/*' ) {
433 $end = strpos(
$s,
'*/', $pos + 2 );
434 $skip = $end ===
false ? $length - $pos : $end - $pos + 2;
440 if( !$newlineFound && strcspn(
$s,
"\r\n", $pos, $skip ) !== $skip ) {
441 $newlineFound =
true;
449 if( ( $ch ===
'/' && substr(
$s, $pos, 2 ) ===
'//' )
450 || ( $ch ===
'<' && substr(
$s, $pos, 4 ) ===
'<!--' )
451 || ( $ch ===
'-' && $newlineFound && substr(
$s, $pos, 3 ) ===
'-->' )
453 $pos += strcspn(
$s,
"\r\n", $pos );
460 if( $ch ===
"'" || $ch ===
'"' ) {
462 $search = $ch .
'\\';
464 $end += strcspn(
$s, $search, $end ) + 2;
465 }
while( $end - 2 < $length &&
$s[$end - 2] ===
'\\' );
469 } elseif( $ch ===
'/' && !isset( $divStates[$state] ) ) {
474 $end += strcspn(
$s,
'/[\\', $end ) + 2;
475 }
while( $end - 2 < $length &&
$s[$end - 2] ===
'\\' );
477 if( $end - 1 >= $length ||
$s[$end - 1] ===
'/' ) {
481 $end += strcspn(
$s,
']\\', $end ) + 2;
482 }
while( $end - 2 < $length &&
$s[$end - 2] ===
'\\' );
486 while( $end < $length && ctype_alpha(
$s[$end] ) ) {
491 && ($pos + 1 < $length) && (
$s[$pos + 1] ===
'x' ||
$s[$pos + 1] ===
'X' )
495 $len = strspn(
$s,
'0123456789ABCDEFabcdef', $end );
497 return self::parseError(
$s, $pos,
'Expected a hexadecimal number but found ' . substr(
$s, $pos, 5 ) .
'...' );
502 || ( $ch ===
'.' && $pos + 1 < $length && ctype_digit(
$s[$pos + 1] ) )
504 $end += strspn(
$s,
'0123456789', $end );
505 $decimal = strspn(
$s,
'.', $end );
507 if ( $decimal > 2 ) {
510 $end += strspn(
$s,
'0123456789', $end + 1 ) + $decimal;
512 $exponent = strspn(
$s,
'eE', $end );
514 if ( $exponent > 1 ) {
520 $end += strspn(
$s,
'-+', $end );
521 $len = strspn(
$s,
'0123456789', $end );
523 return self::parseError(
$s, $pos,
'No decimal digits after e, how many zeroes should be added?' );
527 } elseif( isset( $opChars[$ch] ) ) {
531 && isset( $tokenTypes[substr(
$s, $pos, $end - $pos + 1 )] )
538 $end += strcspn(
$s,
" \t\n.;,=<>+-{}()[]?:*/%'\"!&|^~\xb\xc\r", $end );
542 $token = substr(
$s, $pos, $end - $pos );
545 if( $newlineFound && isset( $semicolon[$state][
$type] ) ) {
551 } elseif( $maxLineLength > 0 && $lineLength + $end - $pos > $maxLineLength &&
552 !isset( $semicolon[$state][
$type] ) &&
$type !== self::TYPE_INCR_OP )
560 } elseif( !isset( $opChars[
$last] ) && !isset( $opChars[$ch] ) ) {
564 } elseif(
$last === $ch && ( $ch ===
'+' || $ch ===
'-' || $ch ===
'/' ) ) {
569 $type === self::TYPE_LITERAL
570 && ( $token ===
'true' || $token ===
'false' )
571 && ( $state === self::EXPRESSION || $state === self::PROPERTY_EXPRESSION )
574 $token = ( $token ===
'true' ) ?
'!0' :
'!1';
578 $lineLength += $end - $pos;
581 $newlineFound =
false;
585 $newlineAdded =
false;
586 if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineBefore[$state][
$type] ) ) {
589 $newlineAdded =
true;
593 if( isset( $push[$state][
$type] ) && count( $stack ) < self::STACK_LIMIT ) {
594 $stack[] = $push[$state][
$type];
596 if( $stack && isset( $pop[$state][
$type] ) ) {
597 $state = array_pop( $stack );
598 } elseif( isset( $goto[$state][
$type] ) ) {
599 $state = $goto[$state][
$type];
603 if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineAfter[$state][
$type] ) ) {