ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
JSMin_lib.php
Go to the documentation of this file.
1<?php
85define('JSMIN_VERSION', '0.2');
86
92define('EOF', false);
93
99define('ORD_NL', ord("\n"));
100define('ORD_space', ord(' '));
101define('ORD_cA', ord('A'));
102define('ORD_cZ', ord('Z'));
103define('ORD_a', ord('a'));
104define('ORD_z', ord('z'));
105define('ORD_0', ord('0'));
106define('ORD_9', ord('9'));
107
111/*
112class JSMinException extends Exception {
113}
114*/
116{
117}
118
124{
125}
126
132{
133}
134
140{
141}
142
148{
149}
150
155define('JSMIN_ACT_FULL', 1);
156
161define('JSMIN_ACT_BUF', 2);
162
167define('JSMIN_ACT_IMM', 3);
168
180class JSMin
181{
182
188 public $in;
189
195 public $out;
196
201 public $theA;
202
207 public $theB;
208
210 public $inLength = 0;
211 public $inPos = 0;
212 public $isString = false;
213
220 public function isAlphaNum($c)
221 {
222
223 // Get ASCII value of character for C-like comparisons
224
225 $a = ord($c);
226
227 // Compare using defined character ordinals, or between PHP strings
228 // Note : === is micro-faster than == when types are known to be the same
229
230 return
231 ($a >= ORD_a && $a <= ORD_z) ||
232 ($a >= ORD_0 && $a <= ORD_9) ||
233 ($a >= ORD_cA && $a <= ORD_cZ) ||
234 $c === '_' || $c === '$' || $c === '\\' || $a > 126
235 ;
236 }
237
247 public function get()
248 {
249
250 // Get next input character and advance position in file
251
252 if ($this->isString) {
253 if ($this->inPos < $this->inLength) {
254 $c = $this->in[$this->inPos];
255 ++$this->inPos;
256 } else {
257 return EOF;
258 }
259 } else {
260 $c = $this->in->fgetc();
261 }
262
263 // Test for non-problematic characters
264
265 if ($c === "\n" || $c === EOF || ord($c) >= ORD_space) {
266 return $c;
267 }
268
269 // else
270 // Make linefeeds into newlines
271
272 if ($c === "\r") {
273 return "\n";
274 }
275
276 // else
277 // Consider space
278
279 return ' ';
280 }
281
290 public function peek()
291 {
292 if ($this->isString) {
293 if ($this->inPos < $this->inLength) {
294 $c = $this->in[$this->inPos];
295 } else {
296 return EOF;
297 }
298 } else {
299 // Get next input character
300
301 $c = $this->in->fgetc();
302
303 // Regress position in file
304
305 $this->in->fseek(-1, SEEK_CUR);
306
307 // Return character obtained
308 }
309
310 return $c;
311 }
312
317 public function put($c)
318 {
319 if ($this->isString) {
320 $this->out .= $c;
321 } else {
322 $this->out->fwrite($c);
323 }
324 }
325
335 public function next()
336 {
337
338 // Get next char from input, translated if necessary
339
340 $c = $this->get();
341
342 // Check comment possibility
343
344 if ($c == '/') {
345
346 // Look ahead : a comment is two slashes or slashes followed by asterisk (to be closed)
347
348 switch ($this->peek()) {
349
350 case '/':
351
352 // Comment is up to the end of the line
353 // TOTEST : simple $this->in->fgets()
354
355 while (true) {
356 $c = $this->get();
357
358 if (ord($c) <= ORD_NL) {
359 return $c;
360 }
361 }
362
363 // no break
364 case '*':
365
366 // Comment is up to comment close.
367 // Might not be terminated, if we hit the end of file.
368
369 while (true) {
370
371 // N.B. not using switch() because of having to test EOF with ===
372
373 $c = $this->get();
374
375 if ($c == '*') {
376
377 // Comment termination if the char ahead is a slash
378
379 if ($this->peek() == '/') {
380
381 // Advance again and make into a single space
382
383 $this->get();
384 return ' ';
385 }
386 } elseif ($c === EOF) {
387
388 // Whoopsie
389
390 //throw new UnterminatedCommentJSMinException();
391 trigger_error('UnterminatedComment', E_USER_ERROR);
392 }
393 }
394
395 // no break
396 default:
397
398 // Not a comment after all
399
400 return $c;
401 }
402 }
403
404 // No risk of a comment
405
406 return $c;
407 }
408
423 public function action($action)
424 {
425
426 // Choice of possible actions
427 // Note the frequent fallthroughs : the actions are decrementally "long"
428
429 switch ($action) {
430
431 case JSMIN_ACT_FULL:
432
433 // Write A to output, then fall through
434
435 $this->put($this->theA);
436
437 // no break
438 case JSMIN_ACT_BUF: // N.B. possible fallthrough from above
439
440 // Copy B to A
441
442 $tmpA = $this->theA = $this->theB;
443
444 // Treating a string as a single char : outputting it whole
445 // Note that the string-opening char (" or ') is memorized in B
446
447 if ($tmpA == '\'' || $tmpA == '"') {
448 while (true) {
449
450 // Output string contents
451
452 $this->put($tmpA);
453
454 // Get next character, watching out for termination of the current string,
455 // new line & co (then the string is not terminated !), or a backslash
456 // (upon which the following char is directly output to serve the escape mechanism)
457
458 $tmpA = $this->theA = $this->get();
459
460 if ($tmpA == $this->theB) {
461
462 // String terminated
463
464 break; // from while(true)
465 }
466
467 // else
468
469 if (ord($tmpA) <= ORD_NL) {
470
471 // Whoopsie
472
473 //throw new UnterminatedStringLiteralJSMinException();
474 trigger_error('UnterminatedStringLiteral', E_USER_ERROR);
475 }
476
477 // else
478
479 if ($tmpA == '\\') {
480
481 // Escape next char immediately
482
483 $this->put($tmpA);
484 $tmpA = $this->theA = $this->get();
485 }
486 }
487 }
488
489 // no break
490 case JSMIN_ACT_IMM: // N.B. possible fallthrough from above
491
492 // Get the next B
493
494 $this->theB = $this->next();
495
496 // Special case of recognising regular expressions (beginning with /) that are
497 // preceded by '(', ',' or '='
498
499 $tmpA = $this->theA;
500
501 if ($this->theB == '/' && ($tmpA == '(' || $tmpA == ',' || $tmpA == '=')) {
502
503 // Output the two successive chars
504
505 $this->put($tmpA);
506 $this->put($this->theB);
507
508 // Look for the end of the RE literal, watching out for escaped chars or a control /
509 // end of line char (the RE literal then being unterminated !)
510
511 while (true) {
512 $tmpA = $this->theA = $this->get();
513
514 if ($tmpA == '/') {
515
516 // RE literal terminated
517
518 break; // from while(true)
519 }
520
521 // else
522
523 if ($tmpA == '\\') {
524
525 // Escape next char immediately
526
527 $this->put($tmpA);
528 $tmpA = $this->theA = $this->get();
529 } elseif (ord($tmpA) <= ORD_NL) {
530
531 // Whoopsie
532
533 //throw new UnterminatedRegExpLiteralJSMinException();
534 trigger_error('UnterminatedRegExpLiteral', E_USER_ERROR);
535 }
536
537 // Output RE characters
538
539 $this->put($tmpA);
540 }
541
542 // Move forward after the RE literal
543
544 $this->theB = $this->next();
545 }
546
547 break;
548 default:
549 //throw new JSMinException('Expected a JSMin::ACT_* constant in action().');
550 trigger_error('Expected a JSMin::ACT_* constant in action()', E_USER_ERROR);
551 }
552 }
553
569 public function minify()
570 {
571
572 // Initialize A and run the first (minimal) action
573
574 $this->theA = "\n";
575 $this->action(JSMIN_ACT_IMM);
576
577 // Proceed all the way to the end of the input file
578
579 while ($this->theA !== EOF) {
580 switch ($this->theA) {
581
582 case ' ':
583
584 if (JSMin::isAlphaNum($this->theB)) {
585 $this->action(JSMIN_ACT_FULL);
586 } else {
587 $this->action(JSMIN_ACT_BUF);
588 }
589
590 break;
591 case "\n":
592
593 switch ($this->theB) {
594
595 case '{': case '[': case '(':
596 case '+': case '-':
597
598 $this->action(JSMIN_ACT_FULL);
599
600 break;
601 case ' ':
602
603 $this->action(JSMIN_ACT_IMM);
604
605 break;
606 default:
607
608 if (JSMin::isAlphaNum($this->theB)) {
609 $this->action(JSMIN_ACT_FULL);
610 } else {
611 $this->action(JSMIN_ACT_BUF);
612 }
613
614 break;
615 }
616
617 break;
618 default:
619
620 switch ($this->theB) {
621
622 case ' ':
623
624 if (JSMin::isAlphaNum($this->theA)) {
625 $this->action(JSMIN_ACT_FULL);
626 break;
627 }
628
629 // else
630
631 $this->action(JSMIN_ACT_IMM);
632
633 break;
634 case "\n":
635
636 switch ($this->theA) {
637
638 case '}': case ']': case ')': case '+':
639 case '-': case '"': case '\'':
640
641 $this->action(JSMIN_ACT_FULL);
642
643 break;
644 default:
645
646 if (JSMin::isAlphaNum($this->theA)) {
647 $this->action(JSMIN_ACT_FULL);
648 } else {
649 $this->action(JSMIN_ACT_IMM);
650 }
651
652 break;
653 }
654
655 break;
656 default:
657
658 $this->action(JSMIN_ACT_FULL);
659
660 break;
661 }
662
663 break;
664 }
665 }
666
667 if ($this->isString) {
668 return $this->out;
669 }
670 }
671
682 public function __construct($inFileName = '-', $outFileName = '-', $comments = null)
683 {
684 if ($outFileName === false) {
685 $this->JSMin_String($inFileName, $comments);
686 } else {
687 $this->JSMin_File($inFileName, $outFileName, $comments);
688 }
689 }
690
691 public function JSMin_File($inFileName = '-', $outFileName = '-', $comments = null)
692 {
693
694 // Recuperate input and output streams.
695 // Use STDIN and STDOUT by default, if they are defined (CLI mode) and no file names are provided
696
697 if ($inFileName == '-') {
698 $inFileName = 'php://stdin';
699 }
700 if ($outFileName == '-') {
701 $outFileName = 'php://stdout';
702 }
703
704 /*try {
705
706 $this->in = new SplFileObject($inFileName, 'rb', TRUE);
707 }
708 catch (Exception $e) {
709
710 throw new FileOpenFailedJSMinException(
711 'Failed to open "'.$inFileName.'" for reading only.'
712 );
713 }
714
715 try {
716
717 $this->out = new SplFileObject($outFileName, 'wb', TRUE);
718 }
719 catch (Exception $e) {
720
721 throw new FileOpenFailedJSMinException(
722 'Failed to open "'.$outFileName.'" for writing only.'
723 );
724 }
725 */
726 $this->in = fopen($inFileName, 'rb');
727 if (!$this->in) {
728 trigger_error('Failed to open "' . $inFileName, E_USER_ERROR);
729 }
730 $this->out = fopen($outFileName, 'wb');
731 if (!$this->out) {
732 trigger_error('Failed to open "' . $outFileName, E_USER_ERROR);
733 }
734
735 // Present possible initial comments
736
737 if ($comments !== null) {
738 foreach ($comments as $comm) {
739 $this->out->fwrite('// ' . str_replace("\n", " ", $comm) . "\n");
740 }
741 }
742 }
743
744 public function JSMin_String($inString, $comments = null)
745 {
746 $this->in = $inString;
747 $this->out = '';
748 $this->inLength = strlen($inString);
749 $this->inPos = 0;
750 $this->isString = true;
751
752 if ($comments !== null) {
753 foreach ($comments as $comm) {
754 $this->out .= '// ' . str_replace("\n", " ", $comm) . "\n";
755 }
756 }
757 }
758}
759
const ORD_NL
Some ASCII character ordinals.
Definition: JSMin_lib.php:99
const ORD_z
Definition: JSMin_lib.php:104
const JSMIN_ACT_BUF
Constant describing an action() : Copy B to A.
Definition: JSMin_lib.php:161
const ORD_cA
Definition: JSMin_lib.php:101
const ORD_0
Definition: JSMin_lib.php:105
const ORD_9
Definition: JSMin_lib.php:106
const ORD_a
Definition: JSMin_lib.php:103
const EOF
How fgetc() reports an End Of File.
Definition: JSMin_lib.php:92
const JSMIN_ACT_IMM
Constant describing an action() : Get the next B.
Definition: JSMin_lib.php:167
const JSMIN_ACT_FULL
Constant describing an action() : Output A.
Definition: JSMin_lib.php:155
const ORD_space
Definition: JSMin_lib.php:100
const ORD_cZ
Definition: JSMin_lib.php:102
An exception for terminatinating execution or to throw for unit testing.
A JSMin exception indicating that a file provided for input or output could not be properly opened.
Definition: JSMin_lib.php:124
Generic exception class related to JSMin.
Definition: JSMin_lib.php:116
Main JSMin application class.
Definition: JSMin_lib.php:181
JSMin_File($inFileName='-', $outFileName='-', $comments=null)
Definition: JSMin_lib.php:691
$inLength
variables used for string-based parsing
Definition: JSMin_lib.php:210
next()
Get the next character from the input stream, excluding comments.
Definition: JSMin_lib.php:335
put($c)
Adds a char to the output steram / string.
Definition: JSMin_lib.php:317
peek()
Get the next character from the input stream, without gettng it.
Definition: JSMin_lib.php:290
minify()
Run the JSMin application : minify some JS code.
Definition: JSMin_lib.php:569
action($action)
Do something !
Definition: JSMin_lib.php:423
JSMin_String($inString, $comments=null)
Definition: JSMin_lib.php:744
__construct($inFileName='-', $outFileName='-', $comments=null)
Prepare a new JSMin application.
Definition: JSMin_lib.php:682
isAlphaNum($c)
Indicates whether a character is alphanumeric or _, $, \ or non-ASCII.
Definition: JSMin_lib.php:220
A JSMin exception indicating that an unterminated comment was encountered in input.
Definition: JSMin_lib.php:132
A JSMin exception indicatig that an unterminated regular expression lieteral was encountered in input...
Definition: JSMin_lib.php:148
A JSMin exception indicatig that an unterminated string literal was encountered in input.
Definition: JSMin_lib.php:140
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples