1
0
mirror of https://github.com/mrclay/minify.git synced 2025-08-09 15:46:34 +02:00

proper fix for Issue 144: + ++a

This commit is contained in:
Steve Clay
2011-06-26 02:37:00 -04:00
parent a57f0fe51a
commit 5b6469467e
4 changed files with 51 additions and 25 deletions

View File

@@ -1,20 +1,20 @@
<?php <?php
/** /**
* jsmin.php - extended PHP implementation of Douglas Crockford's JSMin. * JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
* *
* <code> * <code>
* $minifiedJs = JSMin::minify($js); * $minifiedJs = JSMin::minify($js);
* </code> * </code>
* *
* This is a direct port of jsmin.c to PHP with a few PHP performance tweaks and * This is a modified port of jsmin.c. Improvements:
* modifications to preserve some comments (see below). Also, rather than using *
* stdin/stdout, JSMin::minify() accepts a string as input and returns another * Does not choke on some regexp literals containing quote characters. E.g. /'/
* string as output. *
* * Spaces are preserved after some add/sub operators, so they are not mistakenly
* Comments containing IE conditional compilation are preserved, as are multi-line * converted to post-inc/dec. E.g. a + ++b -> a+ ++b
* comments that begin with "/*!" (for documentation purposes). In the latter case
* newlines are inserted around the comment to enhance readability.
* *
* Preserves multi-line comments that begin with /*!
*
* PHP 5 or higher is required. * PHP 5 or higher is required.
* *
* Permission is hereby granted to use this version of the library under the * Permission is hereby granted to use this version of the library under the
@@ -68,6 +68,7 @@ class JSMin {
protected $inputLength = 0; protected $inputLength = 0;
protected $lookAhead = null; protected $lookAhead = null;
protected $output = ''; protected $output = '';
protected $lastByteOut = '';
/** /**
* Minify Javascript. * Minify Javascript.
@@ -87,13 +88,6 @@ class JSMin {
public function __construct($input) public function __construct($input)
{ {
$this->input = $input; $this->input = $input;
// look out for syntax like "++ +" and "- ++"
$p = '\\+';
$m = '\\-';
if (preg_match("/([$p$m])(?:\\1 [$p$m]| (?:$p$p|$m$m))/", $input)) {
// likely pre-minified and would be broken by JSMin
$this->output = $input;
}
} }
/** /**
@@ -119,7 +113,11 @@ class JSMin {
// determine next command // determine next command
$command = self::ACTION_KEEP_A; // default $command = self::ACTION_KEEP_A; // default
if ($this->a === ' ') { if ($this->a === ' ') {
if (! $this->isAlphaNum($this->b)) { if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
&& ($this->b === $this->lastByteOut)) {
// Don't delete this space. If we do, the addition/subtraction
// could be parsed as a post-increment
} elseif (! $this->isAlphaNum($this->b)) {
$command = self::ACTION_DELETE_A; $command = self::ACTION_DELETE_A;
} }
} elseif ($this->a === "\n") { } elseif ($this->a === "\n") {
@@ -134,7 +132,7 @@ class JSMin {
} }
} elseif (! $this->isAlphaNum($this->a)) { } elseif (! $this->isAlphaNum($this->a)) {
if ($this->b === ' ' if ($this->b === ' '
|| ($this->b === "\n" || ($this->b === "\n"
&& (false === strpos('}])+-"\'', $this->a)))) { && (false === strpos('}])+-"\'', $this->a)))) {
$command = self::ACTION_DELETE_A_B; $command = self::ACTION_DELETE_A_B;
} }
@@ -156,9 +154,21 @@ class JSMin {
*/ */
protected function action($command) protected function action($command)
{ {
if ($command === self::ACTION_DELETE_A_B
&& $this->b === ' '
&& ($this->a === '+' || $this->a === '-')) {
// Note: we're at an addition/substraction operator; the inputIndex
// will certainly be a valid index
if ($this->input[$this->inputIndex] === $this->a) {
// This is "+ +" or "- -". Don't delete the space.
$command = self::ACTION_KEEP_A;
}
}
switch ($command) { switch ($command) {
case self::ACTION_KEEP_A: case self::ACTION_KEEP_A:
$this->output .= $this->a; $this->output .= $this->a;
$this->lastByteOut = $this->a;
// fallthrough // fallthrough
case self::ACTION_DELETE_A: case self::ACTION_DELETE_A:
$this->a = $this->b; $this->a = $this->b;
@@ -166,6 +176,8 @@ class JSMin {
$str = $this->a; // in case needed for exception $str = $this->a; // in case needed for exception
while (true) { while (true) {
$this->output .= $this->a; $this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get(); $this->a = $this->get();
if ($this->a === $this->b) { // end quote if ($this->a === $this->b) { // end quote
break; break;
@@ -178,6 +190,8 @@ class JSMin {
$str .= $this->a; $str .= $this->a;
if ($this->a === '\\') { if ($this->a === '\\') {
$this->output .= $this->a; $this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get(); $this->a = $this->get();
$str .= $this->a; $str .= $this->a;
} }
@@ -204,6 +218,7 @@ class JSMin {
. $this->inputIndex .": {$pattern}"); . $this->inputIndex .": {$pattern}");
} }
$this->output .= $this->a; $this->output .= $this->a;
$this->lastByteOut = $this->a;
} }
$this->b = $this->next(); $this->b = $this->next();
} }

View File

@@ -1,2 +1,9 @@
// JSMin should not alter this file a / ++b;
if(!a.id)a.id="dp"+ ++this.uuid; a * --b;
a++ - b;
a + --b;
a - ++b;
a + -b;
a + ++b;
a + --b;
a - --b;

View File

@@ -1,2 +1 @@
// JSMin should not alter this file a/++b;a*--b;a++-b;a+--b;a-++b;a+-b;a+ ++b;a+--b;a- --b;
if(!a.id)a.id="dp"+ ++this.uuid;

View File

@@ -6,7 +6,7 @@ require_once 'JSMin.php';
function test_JSMin() function test_JSMin()
{ {
global $thisDir; global $thisDir;
$src = file_get_contents($thisDir . '/_test_files/js/before.js'); $src = file_get_contents($thisDir . '/_test_files/js/before.js');
$minExpected = file_get_contents($thisDir . '/_test_files/js/before.min.js'); $minExpected = file_get_contents($thisDir . '/_test_files/js/before.min.js');
$minOutput = JSMin::minify($src); $minOutput = JSMin::minify($src);
@@ -16,11 +16,16 @@ function test_JSMin()
echo "---Expected: " .countBytes($minExpected). " bytes\n\n{$minExpected}\n\n"; echo "---Expected: " .countBytes($minExpected). " bytes\n\n{$minExpected}\n\n";
echo "---Source: " .countBytes($src). " bytes\n\n{$src}\n\n\n"; echo "---Source: " .countBytes($src). " bytes\n\n{$src}\n\n\n";
} }
$src = file_get_contents($thisDir . '/_test_files/js/issue144.js'); $src = file_get_contents($thisDir . '/_test_files/js/issue144.js');
$minExpected = file_get_contents($thisDir . '/_test_files/js/issue144.min.js'); $minExpected = file_get_contents($thisDir . '/_test_files/js/issue144.min.js');
$minOutput = JSMin::minify($src); $minOutput = JSMin::minify($src);
$passed = assertTrue($minExpected == $minOutput, 'JSMin : Don\'t minify files with + ++ (Issue 144)'); $passed = assertTrue($minExpected == $minOutput, 'JSMin : Handle "+ ++a" syntax (Issue 144)');
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
echo "\n---Output: " .countBytes($minOutput). " bytes\n\n{$minOutput}\n\n";
echo "---Expected: " .countBytes($minExpected). " bytes\n\n{$minExpected}\n\n";
echo "---Source: " .countBytes($src). " bytes\n\n{$src}\n\n\n";
}
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) { if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
$src = file_get_contents($thisDir . '/_test_files/js/issue132.js'); $src = file_get_contents($thisDir . '/_test_files/js/issue132.js');