Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

proper fix for Issue 144: + ++a

  • Loading branch information...
commit 5b6469467ef72496bfdfe74a7d7b0bb33490074f 1 parent a57f0fe
@mrclay mrclay authored
View
51 min/lib/JSMin.php
@@ -1,20 +1,20 @@
<?php
/**
- * jsmin.php - extended PHP implementation of Douglas Crockford's JSMin.
+ * JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
*
* <code>
* $minifiedJs = JSMin::minify($js);
* </code>
*
- * This is a direct port of jsmin.c to PHP with a few PHP performance tweaks and
- * modifications to preserve some comments (see below). Also, rather than using
- * stdin/stdout, JSMin::minify() accepts a string as input and returns another
- * string as output.
- *
- * Comments containing IE conditional compilation are preserved, as are multi-line
- * comments that begin with "/*!" (for documentation purposes). In the latter case
- * newlines are inserted around the comment to enhance readability.
+ * This is a modified port of jsmin.c. Improvements:
+ *
+ * Does not choke on some regexp literals containing quote characters. E.g. /'/
+ *
+ * Spaces are preserved after some add/sub operators, so they are not mistakenly
+ * converted to post-inc/dec. E.g. a + ++b -> a+ ++b
*
+ * Preserves multi-line comments that begin with /*!
+ *
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
@@ -68,6 +68,7 @@ class JSMin {
protected $inputLength = 0;
protected $lookAhead = null;
protected $output = '';
+ protected $lastByteOut = '';
/**
* Minify Javascript.
@@ -87,13 +88,6 @@ public static function minify($js)
public function __construct($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 @@ public function min()
// determine next command
$command = self::ACTION_KEEP_A; // default
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;
}
} elseif ($this->a === "\n") {
@@ -134,7 +132,7 @@ public function min()
}
} elseif (! $this->isAlphaNum($this->a)) {
if ($this->b === ' '
- || ($this->b === "\n"
+ || ($this->b === "\n"
&& (false === strpos('}])+-"\'', $this->a)))) {
$command = self::ACTION_DELETE_A_B;
}
@@ -156,9 +154,21 @@ public function min()
*/
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) {
case self::ACTION_KEEP_A:
$this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+
// fallthrough
case self::ACTION_DELETE_A:
$this->a = $this->b;
@@ -166,6 +176,8 @@ protected function action($command)
$str = $this->a; // in case needed for exception
while (true) {
$this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+
$this->a = $this->get();
if ($this->a === $this->b) { // end quote
break;
@@ -178,6 +190,8 @@ protected function action($command)
$str .= $this->a;
if ($this->a === '\\') {
$this->output .= $this->a;
+ $this->lastByteOut = $this->a;
+
$this->a = $this->get();
$str .= $this->a;
}
@@ -204,6 +218,7 @@ protected function action($command)
. $this->inputIndex .": {$pattern}");
}
$this->output .= $this->a;
+ $this->lastByteOut = $this->a;
}
$this->b = $this->next();
}
View
11 min_unit_tests/_test_files/js/issue144.js
@@ -1,2 +1,9 @@
-// JSMin should not alter this file
-if(!a.id)a.id="dp"+ ++this.uuid;
+a / ++b;
+a * --b;
+a++ - b;
+a + --b;
+a - ++b;
+a + -b;
+a + ++b;
+a + --b;
+a - --b;
View
3  min_unit_tests/_test_files/js/issue144.min.js
@@ -1,2 +1 @@
-// JSMin should not alter this file
-if(!a.id)a.id="dp"+ ++this.uuid;
+a/++b;a*--b;a++-b;a+--b;a-++b;a+-b;a+ ++b;a+--b;a- --b;
View
11 min_unit_tests/test_JSMin.php
@@ -6,7 +6,7 @@
function test_JSMin()
{
global $thisDir;
-
+
$src = file_get_contents($thisDir . '/_test_files/js/before.js');
$minExpected = file_get_contents($thisDir . '/_test_files/js/before.min.js');
$minOutput = JSMin::minify($src);
@@ -16,11 +16,16 @@ function test_JSMin()
echo "---Expected: " .countBytes($minExpected). " bytes\n\n{$minExpected}\n\n";
echo "---Source: " .countBytes($src). " bytes\n\n{$src}\n\n\n";
}
-
+
$src = file_get_contents($thisDir . '/_test_files/js/issue144.js');
$minExpected = file_get_contents($thisDir . '/_test_files/js/issue144.min.js');
$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)) {
$src = file_get_contents($thisDir . '/_test_files/js/issue132.js');
Please sign in to comment.
Something went wrong with that request. Please try again.