protected function JSqueeze::extractStrings in Minify JS 7
1 call to JSqueeze::extractStrings()
- JSqueeze::squeeze in lib/
JSqueeze.php - Squeezes a JavaScript source code.
File
- lib/
JSqueeze.php, line 188
Class
Code
protected function extractStrings($f) {
if ($cc_on = false !== strpos($f, '@cc_on')) {
// Protect conditional comments from being removed
$f = str_replace('#', '##', $f);
$f = str_replace('/*@', '1#@', $f);
$f = preg_replace("'//@([^\n]+)'", '2#@$1@#3', $f);
$f = str_replace('@*/', '@#1', $f);
}
$len = strlen($f);
$code = str_repeat(' ', $len);
$j = 0;
$strings = array();
$K = 0;
$instr = false;
$q = array(
"'",
'"',
"'" => 0,
'"' => 0,
);
// Extract strings, removes comments
for ($i = 0; $i < $len; ++$i) {
if ($instr) {
if ('//' == $instr) {
if ("\n" == $f[$i]) {
$f[$i--] = ' ';
$instr = false;
}
}
elseif ($f[$i] == $instr || '/' == $f[$i] && "/'" == $instr) {
if ('!' == $instr) {
}
elseif ('*' == $instr) {
if ('/' == $f[$i + 1]) {
++$i;
$instr = false;
}
}
else {
if ("/'" == $instr) {
while (isset($f[$i + 1]) && false !== strpos('gmi', $f[$i + 1])) {
$s[] = $f[$i++];
}
$s[] = $f[$i];
}
$instr = false;
}
}
elseif ('*' == $instr) {
}
elseif ('!' == $instr) {
if ('*' == $f[$i] && '/' == $f[$i + 1]) {
$s[] = "*/\r";
++$i;
$instr = false;
}
elseif ("\n" == $f[$i]) {
$s[] = "\r";
}
else {
$s[] = $f[$i];
}
}
elseif ('\\' == $f[$i]) {
++$i;
if ("\n" != $f[$i]) {
isset($q[$f[$i]]) && ++$q[$f[$i]];
$s[] = '\\' . $f[$i];
}
}
elseif ('[' == $f[$i] && "/'" == $instr) {
$instr = '/[';
$s[] = '[';
}
elseif (']' == $f[$i] && '/[' == $instr) {
$instr = "/'";
$s[] = ']';
}
elseif ("'" == $f[$i] || '"' == $f[$i]) {
++$q[$f[$i]];
$s[] = '\\' . $f[$i];
}
else {
$s[] = $f[$i];
}
}
else {
switch ($f[$i]) {
case ';':
// Remove triple semi-colon
if ($i > 0 && ';' == $f[$i - 1] && $i + 1 < $len && ';' == $f[$i + 1]) {
$f[$i] = $f[$i + 1] = '/';
}
else {
$code[++$j] = ';';
break;
}
case '/':
if ('*' == $f[$i + 1]) {
++$i;
$instr = '*';
if ($this->keepImportantComments && '!' == $f[$i + 1]) {
++$i;
// no break here
}
else {
break;
}
}
elseif ('/' == $f[$i + 1]) {
++$i;
$instr = '//';
break;
}
else {
$a = $j && (' ' == $code[$j] || "" == $code[$j]) ? $code[$j - 1] : $code[$j];
if (false !== strpos('-!%&;<=>~:^+|,()*?[{} ', $a) || false !== strpos('oenfd', $a) && preg_match("'(?<![\$.a-zA-Z0-9_])(do|else|return|typeof|yield[ ]?\\*?)[ ]?\$'", substr($code, $j - 7, 8))) {
if (')' === $a && $j > 1) {
$a = 1;
$k = $j - (' ' == $code[$j] || "" == $code[$j]) - 1;
while ($k >= 0 && $a) {
if ('(' === $code[$k]) {
--$a;
}
elseif (')' === $code[$k]) {
++$a;
}
--$k;
}
if (!preg_match("'(?<![\$.a-zA-Z0-9_])(if|for|while)[ ]?\$'", substr($code, 0, $k + 1))) {
$code[++$j] = '/';
break;
}
}
$key = "//''\"\"" . $K++ . ($instr = "/'");
$a = $j;
$code .= $key;
while (isset($key[++$j - $a - 1])) {
$code[$j] = $key[$j - $a - 1];
}
--$j;
isset($s) && ($s = implode('', $s)) && $cc_on && $this
->restoreCc($s);
$strings[$key] = array(
'/',
);
$s =& $strings[$key];
}
else {
$code[++$j] = '/';
}
break;
}
case "'":
case '"':
$instr = $f[$i];
$key = "//''\"\"" . $K++ . ('!' == $instr ? ']' : "'");
$a = $j;
$code .= $key;
while (isset($key[++$j - $a - 1])) {
$code[$j] = $key[$j - $a - 1];
}
--$j;
isset($s) && ($s = implode('', $s)) && $cc_on && $this
->restoreCc($s);
$strings[$key] = array();
$s =& $strings[$key];
'!' == $instr && ($s[] = "\r/*!");
break;
case "\n":
if ($j > 3) {
if (' ' == $code[$j] || "" == $code[$j]) {
--$j;
}
$code[++$j] = false !== strpos('kend+-', $code[$j - 1]) && preg_match("'(?:\\+\\+|--|(?<![\$.a-zA-Z0-9_])(break|continue|return|yield[ ]?\\*?))[ ]?\$'", substr($code, $j - 8, 9)) ? ';' : "";
break;
}
case "\t":
$f[$i] = ' ';
case ' ':
if (!$j || ' ' == $code[$j] || "" == $code[$j]) {
break;
}
default:
$code[++$j] = $f[$i];
}
}
}
isset($s) && ($s = implode('', $s)) && $cc_on && $this
->restoreCc($s);
unset($s);
$code = substr($code, 0, $j + 1);
$cc_on && $this
->restoreCc($code, false);
// Protect wanted spaces and remove unwanted ones
$code = strtr($code, "", ' ');
$code = str_replace('- -', "--", $code);
$code = str_replace('+ +', "++", $code);
$code = preg_replace("'(\\d)\\s+\\.\\s*([a-zA-Z\$_[(])'", "\$1.\$2", $code);
$code = preg_replace("# ([-!%&;<=>~:.^+|,()*?[\\]{}/']+)#", '$1', $code);
$code = preg_replace("#([-!%&;<=>~:.^+|,()*?[\\]{}/]+) #", '$1', $code);
$cc_on && ($code = preg_replace_callback("'//[^\\'].*?@#3'", function ($m) {
return strtr($m[0], ' ', "");
}, $code));
// Replace new Array/Object by []/{}
false !== strpos($code, 'new Array') && ($code = preg_replace("'new Array(?:\\(\\)|([;\\])},:]))'", '[]$1', $code));
false !== strpos($code, 'new Object') && ($code = preg_replace("'new Object(?:\\(\\)|([;\\])},:]))'", '{}$1', $code));
// Add missing semi-colons after curly braces
// This adds more semi-colons than strictly needed,
// but it seems that later gzipping is favorable to the repetition of "};"
$code = preg_replace("'\\}(?![:,;.()\\[\\]}\\|&]|(else|catch|finally|while)[^\$.a-zA-Z0-9_])'", '};', $code);
// Tag possible empty instruction for easy detection
$code = preg_replace("'(?<![\$.a-zA-Z0-9_])if\\('", '1#(', $code);
$code = preg_replace("'(?<![\$.a-zA-Z0-9_])for\\('", '2#(', $code);
$code = preg_replace("'(?<![\$.a-zA-Z0-9_])while\\('", '3#(', $code);
$forPool = array();
$instrPool = array();
$s = 0;
$f = array();
$j = -1;
// Remove as much semi-colon as possible
$len = strlen($code);
for ($i = 0; $i < $len; ++$i) {
switch ($code[$i]) {
case '(':
if ($j >= 0 && "\n" == $f[$j]) {
$f[$j] = ';';
}
++$s;
if ($i && '#' == $code[$i - 1]) {
$instrPool[$s - 1] = 1;
if ('2' == $code[$i - 2]) {
$forPool[$s] = 1;
}
}
$f[++$j] = '(';
break;
case ']':
case ')':
if ($i + 1 < $len && !isset($forPool[$s]) && !isset($instrPool[$s - 1]) && preg_match("'[a-zA-Z0-9_\$]'", $code[$i + 1])) {
$f[$j] .= $code[$i];
$f[++$j] = "\n";
}
else {
$f[++$j] = $code[$i];
}
if (')' == $code[$i]) {
unset($forPool[$s]);
--$s;
}
continue 2;
case '}':
if ("\n" == $f[$j]) {
$f[$j] = '}';
}
else {
$f[++$j] = '}';
}
break;
case ';':
if (isset($forPool[$s]) || isset($instrPool[$s])) {
$f[++$j] = ';';
}
elseif ($j >= 0 && "\n" != $f[$j] && ';' != $f[$j]) {
$f[++$j] = "\n";
}
break;
case '#':
switch ($f[$j]) {
case '1':
$f[$j] = 'if';
break 2;
case '2':
$f[$j] = 'for';
break 2;
case '3':
$f[$j] = 'while';
break 2;
}
case '[':
if ($j >= 0 && "\n" == $f[$j]) {
$f[$j] = ';';
}
default:
$f[++$j] = $code[$i];
}
unset($instrPool[$s]);
}
$f = implode('', $f);
$cc_on && ($f = str_replace('@#3', "\r", $f));
// Fix "else ;" empty instructions
$f = preg_replace("'(?<![\$.a-zA-Z0-9_])else\n'", "\n", $f);
$r1 = array(
// keywords with a direct object
'case',
'delete',
'do',
'else',
'function',
'in',
'instanceof',
'of',
'break',
'new',
'return',
'throw',
'typeof',
'var',
'void',
'yield',
'let',
'if',
'const',
'get',
'set',
);
$r2 = array(
// keywords with a subject
'in',
'instanceof',
'of',
);
// Fix missing semi-colons
$f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])" . implode(')(?<!(?<![a-zA-Z0-9_\\$])', $r1) . ') (?!(' . implode('|', $r2) . ")(?![a-zA-Z0-9_\$]))'", "\n", $f);
$f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])do)(?<!(?<![a-zA-Z0-9_\$])else) if\\('", "\nif(", $f);
$f = preg_replace("'(?<=--|\\+\\+)(?<![a-zA-Z0-9_\$])(" . implode('|', $r1) . ")(?![a-zA-Z0-9_\$])'", "\n\$1", $f);
$f = preg_replace("'(?<![a-zA-Z0-9_\$])for\neach\\('", 'for each(', $f);
$f = preg_replace("'(?<![a-zA-Z0-9_\$])\n(" . implode('|', $r2) . ")(?![a-zA-Z0-9_\$])'", '$1', $f);
// Merge strings
if ($q["'"] > $q['"']) {
$q = array(
$q[1],
$q[0],
);
}
$f = preg_replace("#//''\"\"[0-9]+'#", $q[0] . '$0' . $q[0], $f);
strpos($f, $q[0] . '+' . $q[0]) && ($f = str_replace($q[0] . '+' . $q[0], '', $f));
$len = count($strings);
foreach ($strings as $r1 => &$r2) {
$r2 = "/'" == substr($r1, -2) ? str_replace(array(
"\\'",
'\\"',
), array(
"'",
'"',
), $r2) : str_replace('\\' . $q[1], $q[1], $r2);
}
// Restore wanted spaces
$f = strtr($f, "", ' ');
return array(
$f,
$strings,
);
}