JS::stripWhitespace()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 120

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 120
rs 8
c 0
b 0
f 0
cc 1
nc 1
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * JavaScript minifier
4
 *
5
 * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
 *
7
 * @author Matthias Mullie <[email protected]>
8
 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
 * @license MIT License
10
 */
11
namespace MatthiasMullie\Minify;
12
13
/**
14
 * JavaScript Minifier Class
15
 *
16
 * Please report bugs on https://github.com/matthiasmullie/minify/issues
17
 *
18
 * @package Minify
19
 * @author Matthias Mullie <[email protected]>
20
 * @author Tijs Verkoyen <[email protected]>
21
 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
22
 * @license MIT License
23
 */
24
class JS extends Minify
25
{
26
    /**
27
     * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
28
     *
29
     * Note that regular expressions using that bit must have the PCRE_UTF8
30
     * pattern modifier (/u) set.
31
     *
32
     * @var string
33
     */
34
    const REGEX_VARIABLE = '\b[$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}][$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}0-9\x{0300}-\x{036f}\x{0483}-\x{0487}\x{0591}-\x{05bd}\x{05bf}\x{05c1}\x{05c2}\x{05c4}\x{05c5}\x{05c7}\x{0610}-\x{061a}\x{064b}-\x{0669}\x{0670}\x{06d6}-\x{06dc}\x{06df}-\x{06e4}\x{06e7}\x{06e8}\x{06ea}-\x{06ed}\x{06f0}-\x{06f9}\x{0711}\x{0730}-\x{074a}\x{07a6}-\x{07b0}\x{07c0}-\x{07c9}\x{07eb}-\x{07f3}\x{0816}-\x{0819}\x{081b}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082d}\x{0859}-\x{085b}\x{08e4}-\x{08fe}\x{0900}-\x{0903}\x{093a}-\x{093c}\x{093e}-\x{094f}\x{0951}-\x{0957}\x{0962}\x{0963}\x{0966}-\x{096f}\x{0981}-\x{0983}\x{09bc}\x{09be}-\x{09c4}\x{09c7}\x{09c8}\x{09cb}-\x{09cd}\x{09d7}\x{09e2}\x{09e3}\x{09e6}-\x{09ef}\x{0a01}-\x{0a03}\x{0a3c}\x{0a3e}-\x{0a42}\x{0a47}\x{0a48}\x{0a4b}-\x{0a4d}\x{0a51}\x{0a66}-\x{0a71}\x{0a75}\x{0a81}-\x{0a83}\x{0abc}\x{0abe}-\x{0ac5}\x{0ac7}-\x{0ac9}\x{0acb}-\x{0acd}\x{0ae2}\x{0ae3}\x{0ae6}-\x{0aef}\x{0b01}-\x{0b03}\x{0b3c}\x{0b3e}-\x{0b44}\x{0b47}\x{0b48}\x{0b4b}-\x{0b4d}\x{0b56}\x{0b57}\x{0b62}\x{0b63}\x{0b66}-\x{0b6f}\x{0b82}\x{0bbe}-\x{0bc2}\x{0bc6}-\x{0bc8}\x{0bca}-\x{0bcd}\x{0bd7}\x{0be6}-\x{0bef}\x{0c01}-\x{0c03}\x{0c3e}-\x{0c44}\x{0c46}-\x{0c48}\x{0c4a}-\x{0c4d}\x{0c55}\x{0c56}\x{0c62}\x{0c63}\x{0c66}-\x{0c6f}\x{0c82}\x{0c83}\x{0cbc}\x{0cbe}-\x{0cc4}\x{0cc6}-\x{0cc8}\x{0cca}-\x{0ccd}\x{0cd5}\x{0cd6}\x{0ce2}\x{0ce3}\x{0ce6}-\x{0cef}\x{0d02}\x{0d03}\x{0d3e}-\x{0d44}\x{0d46}-\x{0d48}\x{0d4a}-\x{0d4d}\x{0d57}\x{0d62}\x{0d63}\x{0d66}-\x{0d6f}\x{0d82}\x{0d83}\x{0dca}\x{0dcf}-\x{0dd4}\x{0dd6}\x{0dd8}-\x{0ddf}\x{0df2}\x{0df3}\x{0e31}\x{0e34}-\x{0e3a}\x{0e47}-\x{0e4e}\x{0e50}-\x{0e59}\x{0eb1}\x{0eb4}-\x{0eb9}\x{0ebb}\x{0ebc}\x{0ec8}-\x{0ecd}\x{0ed0}-\x{0ed9}\x{0f18}\x{0f19}\x{0f20}-\x{0f29}\x{0f35}\x{0f37}\x{0f39}\x{0f3e}\x{0f3f}\x{0f71}-\x{0f84}\x{0f86}\x{0f87}\x{0f8d}-\x{0f97}\x{0f99}-\x{0fbc}\x{0fc6}\x{102b}-\x{103e}\x{1040}-\x{1049}\x{1056}-\x{1059}\x{105e}-\x{1060}\x{1062}-\x{1064}\x{1067}-\x{106d}\x{1071}-\x{1074}\x{1082}-\x{108d}\x{108f}-\x{109d}\x{135d}-\x{135f}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17d3}\x{17dd}\x{17e0}-\x{17e9}\x{180b}-\x{180d}\x{1810}-\x{1819}\x{18a9}\x{1920}-\x{192b}\x{1930}-\x{193b}\x{1946}-\x{194f}\x{19b0}-\x{19c0}\x{19c8}\x{19c9}\x{19d0}-\x{19d9}\x{1a17}-\x{1a1b}\x{1a55}-\x{1a5e}\x{1a60}-\x{1a7c}\x{1a7f}-\x{1a89}\x{1a90}-\x{1a99}\x{1b00}-\x{1b04}\x{1b34}-\x{1b44}\x{1b50}-\x{1b59}\x{1b6b}-\x{1b73}\x{1b80}-\x{1b82}\x{1ba1}-\x{1bad}\x{1bb0}-\x{1bb9}\x{1be6}-\x{1bf3}\x{1c24}-\x{1c37}\x{1c40}-\x{1c49}\x{1c50}-\x{1c59}\x{1cd0}-\x{1cd2}\x{1cd4}-\x{1ce8}\x{1ced}\x{1cf2}-\x{1cf4}\x{1dc0}-\x{1de6}\x{1dfc}-\x{1dff}\x{200c}\x{200d}\x{203f}\x{2040}\x{2054}\x{20d0}-\x{20dc}\x{20e1}\x{20e5}-\x{20f0}\x{2cef}-\x{2cf1}\x{2d7f}\x{2de0}-\x{2dff}\x{302a}-\x{302f}\x{3099}\x{309a}\x{a620}-\x{a629}\x{a66f}\x{a674}-\x{a67d}\x{a69f}\x{a6f0}\x{a6f1}\x{a802}\x{a806}\x{a80b}\x{a823}-\x{a827}\x{a880}\x{a881}\x{a8b4}-\x{a8c4}\x{a8d0}-\x{a8d9}\x{a8e0}-\x{a8f1}\x{a900}-\x{a909}\x{a926}-\x{a92d}\x{a947}-\x{a953}\x{a980}-\x{a983}\x{a9b3}-\x{a9c0}\x{a9d0}-\x{a9d9}\x{aa29}-\x{aa36}\x{aa43}\x{aa4c}\x{aa4d}\x{aa50}-\x{aa59}\x{aa7b}\x{aab0}\x{aab2}-\x{aab4}\x{aab7}\x{aab8}\x{aabe}\x{aabf}\x{aac1}\x{aaeb}-\x{aaef}\x{aaf5}\x{aaf6}\x{abe3}-\x{abea}\x{abec}\x{abed}\x{abf0}-\x{abf9}\x{fb1e}\x{fe00}-\x{fe0f}\x{fe20}-\x{fe26}\x{fe33}\x{fe34}\x{fe4d}-\x{fe4f}\x{ff10}-\x{ff19}\x{ff3f}]*\b';
35
36
    /**
37
     * Full list of JavaScript reserved words.
38
     * Will be loaded from /data/js/keywords_reserved.txt.
39
     *
40
     * @see https://mathiasbynens.be/notes/reserved-keywords
41
     *
42
     * @var string[]
43
     */
44
    protected $keywordsReserved = array();
45
46
    /**
47
     * List of JavaScript reserved words that accept a <variable, value, ...>
48
     * after them. Some end of lines are not the end of a statement, like with
49
     * these keywords.
50
     *
51
     * E.g.: we shouldn't insert a ; after this else
52
     * else
53
     *     console.log('this is quite fine')
54
     *
55
     * Will be loaded from /data/js/keywords_before.txt
56
     *
57
     * @var string[]
58
     */
59
    protected $keywordsBefore = array();
60
61
    /**
62
     * List of JavaScript reserved words that accept a <variable, value, ...>
63
     * before them. Some end of lines are not the end of a statement, like when
64
     * continued by one of these keywords on the newline.
65
     *
66
     * E.g.: we shouldn't insert a ; before this instanceof
67
     * variable
68
     *     instanceof String
69
     *
70
     * Will be loaded from /data/js/keywords_after.txt
71
     *
72
     * @var string[]
73
     */
74
    protected $keywordsAfter = array();
75
76
    /**
77
     * List of all JavaScript operators.
78
     *
79
     * Will be loaded from /data/js/operators.txt
80
     *
81
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
82
     *
83
     * @var string[]
84
     */
85
    protected $operators = array();
86
87
    /**
88
     * List of JavaScript operators that accept a <variable, value, ...> after
89
     * them. Some end of lines are not the end of a statement, like with these
90
     * operators.
91
     *
92
     * Note: Most operators are fine, we've only removed ++ and --.
93
     * ++ & -- have to be joined with the value they're in-/decrementing.
94
     *
95
     * Will be loaded from /data/js/operators_before.txt
96
     *
97
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
98
     *
99
     * @var string[]
100
     */
101
    protected $operatorsBefore = array();
102
103
    /**
104
     * List of JavaScript operators that accept a <variable, value, ...> before
105
     * them. Some end of lines are not the end of a statement, like when
106
     * continued by one of these operators on the newline.
107
     *
108
     * Note: Most operators are fine, we've only removed ), ], ++, --, ! and ~.
109
     * There can't be a newline separating ! or ~ and whatever it is negating.
110
     * ++ & -- have to be joined with the value they're in-/decrementing.
111
     * ) & ] are "special" in that they have lots or usecases. () for example
112
     * is used for function calls, for grouping, in if () and for (), ...
113
     *
114
     * Will be loaded from /data/js/operators_after.txt
115
     *
116
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
117
     *
118
     * @var string[]
119
     */
120
    protected $operatorsAfter = array();
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    public function __construct()
126
    {
127
        call_user_func_array(array('parent', '__construct'), func_get_args());
128
129
        $dataDir = __DIR__.'/../data/js/';
130
        $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
131
        $this->keywordsReserved = file($dataDir.'keywords_reserved.txt', $options);
132
        $this->keywordsBefore = file($dataDir.'keywords_before.txt', $options);
133
        $this->keywordsAfter = file($dataDir.'keywords_after.txt', $options);
134
        $this->operators = file($dataDir.'operators.txt', $options);
135
        $this->operatorsBefore = file($dataDir.'operators_before.txt', $options);
136
        $this->operatorsAfter = file($dataDir.'operators_after.txt', $options);
137
    }
138
139
    /**
140
     * Minify the data.
141
     * Perform JS optimizations.
142
     *
143
     * @param string[optional] $path Path to write the data to
144
     *
145
     * @return string The minified data
146
     */
147
    public function execute($path = null)
148
    {
149
        $content = '';
150
151
        /*
152
         * Let's first take out strings, comments and regular expressions.
153
         * All of these can contain JS code-like characters, and we should make
154
         * sure any further magic ignores anything inside of these.
155
         *
156
         * Consider this example, where we should not strip any whitespace:
157
         * var str = "a   test";
158
         *
159
         * Comments will be removed altogether, strings and regular expressions
160
         * will be replaced by placeholder text, which we'll restore later.
161
         */
162
        $this->extractStrings('\'"`');
163
        $this->stripComments();
164
        $this->extractRegex();
165
166
        // loop files
167
        foreach ($this->data as $source => $js) {
168
            // take out strings, comments & regex (for which we've registered
169
            // the regexes just a few lines earlier)
170
            $js = $this->replace($js);
171
172
            $js = $this->propertyNotation($js);
173
            $js = $this->shortenBools($js);
174
            $js = $this->stripWhitespace($js);
175
176
            // combine js: separating the scripts by a ;
177
            $content .= $js.";";
178
        }
179
180
        // clean up leftover `;`s from the combination of multiple scripts
181
        $content = ltrim($content, ';');
182
        $content = (string) substr($content, 0, -1);
183
184
        /*
185
         * Earlier, we extracted strings & regular expressions and replaced them
186
         * with placeholder text. This will restore them.
187
         */
188
        $content = $this->restoreExtractedData($content);
189
190
        return $content;
191
    }
192
193
    /**
194
     * Strip comments from source code.
195
     */
196 View Code Duplication
    protected function stripComments()
0 ignored issues
show
Duplication introduced by Matthias Mullie
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
    {
198
        // PHP only supports $this inside anonymous functions since 5.4
199
        $minifier = $this;
200
        $callback = function ($match) use ($minifier) {
201
            $count = count($minifier->extracted);
202
            $placeholder = '/*'.$count.'*/';
203
            $minifier->extracted[$placeholder] = $match[0];
204
205
            return $placeholder;
206
        };
207
        // multi-line comments
208
        $this->registerPattern('/\n?\/\*(!|.*[email protected]|.*[email protected]).*?\*\/\n?/s', $callback);
209
        $this->registerPattern('/\/\*.*?\*\//s', '');
210
211
        // single-line comments
212
        $this->registerPattern('/\/\/.*$/m', '');
213
    }
214
215
    /**
216
     * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
217
     *
218
     * The content inside the regex can contain characters that may be confused
219
     * for JS code: e.g. it could contain whitespace it needs to match & we
220
     * don't want to strip whitespace in there.
221
     *
222
     * The regex can be pretty simple: we don't have to care about comments,
223
     * (which also use slashes) because stripComments() will have stripped those
224
     * already.
225
     *
226
     * This method will replace all string content with simple REGEX#
227
     * placeholder text, so we've rid all regular expressions from characters
228
     * that may be misinterpreted. Original regex content will be saved in
229
     * $this->extracted and after doing all other minifying, we can restore the
230
     * original content via restoreRegex()
231
     */
232
    protected function extractRegex()
233
    {
234
        // PHP only supports $this inside anonymous functions since 5.4
235
        $minifier = $this;
236
        $callback = function ($match) use ($minifier) {
237
            $count = count($minifier->extracted);
238
            $placeholder = '"'.$count.'"';
239
            $minifier->extracted[$placeholder] = $match[0];
240
241
            return $placeholder;
242
        };
243
244
        // match all chars except `/` and `\`
245
        // `\` is allowed though, along with whatever char follows (which is the
246
        // one being escaped)
247
        // this should allow all chars, except for an unescaped `/` (= the one
248
        // closing the regex)
249
        // then also ignore bare `/` inside `[]`, where they don't need to be
250
        // escaped: anything inside `[]` can be ignored safely
251
        $pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
252
253
        // a regular expression can only be followed by a few operators or some
254
        // of the RegExp methods (a `\` followed by a variable or value is
255
        // likely part of a division, not a regex)
256
        $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return',  'typeof');
257
        $before = '([=:,;\+\-\*\/\}\(\{\[&\|!]|^|'.implode('|', $keywords).')\s*';
258
        $propertiesAndMethods = array(
259
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
260
            'constructor',
261
            'flags',
262
            'global',
263
            'ignoreCase',
264
            'multiline',
265
            'source',
266
            'sticky',
267
            'unicode',
268
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
269
            'compile(',
270
            'exec(',
271
            'test(',
272
            'toSource(',
273
            'toString(',
274
        );
275
        $delimiters = array_fill(0, count($propertiesAndMethods), '/');
276
        $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
277
        $after = '(?=\s*([\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).')))';
278
        $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
279
280
        // regular expressions following a `)` are rather annoying to detect...
281
        // quite often, `/` after `)` is a division operator & if it happens to
282
        // be followed by another one (or a comment), it is likely to be
283
        // confused for a regular expression
284
        // however, it's perfectly possible for a regex to follow a `)`: after
285
        // a single-line `if()`, `while()`, ... statement, for example
286
        // since, when they occur like that, they're always the start of a
287
        // statement, there's only a limited amount of ways they can be useful:
288
        // by calling the regex methods directly
289
        // if a regex following `)` is not followed by `.<property or method>`,
290
        // it's quite likely not a regex
291
        $before = '\)\s*';
292
        $after = '(?=\s*\.('.implode('|', $propertiesAndMethods).'))';
293
        $this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
294
295
        // 1 more edge case: a regex can be followed by a lot more operators or
296
        // keywords if there's a newline (ASI) in between, where the operator
297
        // actually starts a new statement
298
        // (https://github.com/matthiasmullie/minify/issues/56)
299
        $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
300
        $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
301
        $after = '(?=\s*\n\s*('.implode('|', $operators).'))';
302
        $this->registerPattern('/'.$pattern.$after.'/', $callback);
303
    }
304
305
    /**
306
     * Strip whitespace.
307
     *
308
     * We won't strip *all* whitespace, but as much as possible. The thing that
309
     * we'll preserve are newlines we're unsure about.
310
     * JavaScript doesn't require statements to be terminated with a semicolon.
311
     * It will automatically fix missing semicolons with ASI (automatic semi-
312
     * colon insertion) at the end of line causing errors (without semicolon.)
313
     *
314
     * Because it's sometimes hard to tell if a newline is part of a statement
315
     * that should be terminated or not, we'll just leave some of them alone.
316
     *
317
     * @param string $content The content to strip the whitespace for
318
     *
319
     * @return string
320
     */
321
    protected function stripWhitespace($content)
322
    {
323
        // uniform line endings, make them all line feed
324
        $content = str_replace(array("\r\n", "\r"), "\n", $content);
325
326
        // collapse all non-line feed whitespace into a single space
327
        $content = preg_replace('/[^\S\n]+/', ' ', $content);
328
329
        // strip leading & trailing whitespace
330
        $content = str_replace(array(" \n", "\n "), "\n", $content);
331
332
        // collapse consecutive line feeds into just 1
333
        $content = preg_replace('/\n+/', "\n", $content);
334
335
        $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
336
        $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
337
        $operators = $this->getOperatorsForRegex($this->operators, '/');
338
        $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
339
        $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
340
341
        // strip whitespace that ends in (or next line begin with) an operator
342
        // that allows statements to be broken up over multiple lines
343
        unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
344
        $content = preg_replace(
345
            array(
346
                '/('.implode('|', $operatorsBefore).')\s+/',
347
                '/\s+('.implode('|', $operatorsAfter).')/',
348
            ),
349
            '\\1',
350
            $content
351
        );
352
353
        // make sure + and - can't be mistaken for, or joined into ++ and --
354
        $content = preg_replace(
355
            array(
356
                '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
357
                '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
358
            ),
359
            '\\1',
360
            $content
361
        );
362
363
        // collapse whitespace around reserved words into single space
364
        $content = preg_replace('/(^|[;\}\s])\K('.implode('|', $keywordsBefore).')\s+/', '\\2 ', $content);
365
        $content = preg_replace('/\s+('.implode('|', $keywordsAfter).')(?=([;\{\s]|$))/', ' \\1', $content);
366
367
        /*
368
         * We didn't strip whitespace after a couple of operators because they
369
         * could be used in different contexts and we can't be sure it's ok to
370
         * strip the newlines. However, we can safely strip any non-line feed
371
         * whitespace that follows them.
372
         */
373
        $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
374
        $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
375
        $content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
376
        $content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
377
378
        /*
379
         * Whitespace after `return` can be omitted in a few occasions
380
         * (such as when followed by a string or regex)
381
         * Same for whitespace in between `)` and `{`, or between `{` and some
382
         * keywords.
383
         */
384
        $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
385
        $content = preg_replace('/\)\s+\{/', '){', $content);
386
        $content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
387
388
        /*
389
         * Get rid of double semicolons, except where they can be used like:
390
         * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
391
         * I'll safeguard these double semicolons inside for-loops by
392
         * temporarily replacing them with an invalid condition: they won't have
393
         * a double semicolon and will be easy to spot to restore afterwards.
394
         */
395
        $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
396
        $content = preg_replace('/;+/', ';', $content);
397
        $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
398
399
        /*
400
         * Next, we'll be removing all semicolons where ASI kicks in.
401
         * for-loops however, can have an empty body (ending in only a
402
         * semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
403
         * Here, nothing happens during the loop; it's just used to keep
404
         * increasing `i`. With that ; omitted, the next line would be expected
405
         * to be the for-loop's body... Same goes for while loops.
406
         * I'm going to double that semicolon (if any) so after the next line,
407
         * which strips semicolons here & there, we're still left with this one.
408
         */
409
        $content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
410
        $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
411
        /*
412
         * Below will also keep `;` after a `do{}while();` along with `while();`
413
         * While these could be stripped after do-while, detecting this
414
         * distinction is cumbersome, so I'll play it safe and make sure `;`
415
         * after any kind of `while` is kept.
416
         */
417
        $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
418
419
        /*
420
         * We also can't strip empty else-statements. Even though they're
421
         * useless and probably shouldn't be in the code in the first place, we
422
         * shouldn't be stripping the `;` that follows it as it breaks the code.
423
         * We can just remove those useless else-statements completely.
424
         *
425
         * @see https://github.com/matthiasmullie/minify/issues/91
426
         */
427
        $content = preg_replace('/else;/s', '', $content);
428
429
        /*
430
         * We also don't really want to terminate statements followed by closing
431
         * curly braces (which we've ignored completely up until now) or end-of-
432
         * script: ASI will kick in here & we're all about minifying.
433
         * Semicolons at beginning of the file don't make any sense either.
434
         */
435
        $content = preg_replace('/;(\}|$)/s', '\\1', $content);
436
        $content = ltrim($content, ';');
437
438
        // get rid of remaining whitespace af beginning/end
439
        return trim($content);
440
    }
441
442
    /**
443
     * We'll strip whitespace around certain operators with regular expressions.
444
     * This will prepare the given array by escaping all characters.
445
     *
446
     * @param string[] $operators
447
     * @param string   $delimiter
448
     *
449
     * @return string[]
450
     */
451
    protected function getOperatorsForRegex(array $operators, $delimiter = '/')
452
    {
453
        // escape operators for use in regex
454
        $delimiters = array_fill(0, count($operators), $delimiter);
455
        $escaped = array_map('preg_quote', $operators, $delimiters);
456
457
        $operators = array_combine($operators, $escaped);
458
459
        // ignore + & - for now, they'll get special treatment
460
        unset($operators['+'], $operators['-']);
461
462
        // dot can not just immediately follow a number; it can be confused for
463
        // decimal point, or calling a method on it, e.g. 42 .toString()
464
        $operators['.'] = '(?<![0-9]\s)\.';
465
466
        // don't confuse = with other assignment shortcuts (e.g. +=)
467
        $chars = preg_quote('+-*\=<>%&|', $delimiter);
468
        $operators['='] = '(?<!['.$chars.'])\=';
469
470
        return $operators;
471
    }
472
473
    /**
474
     * We'll strip whitespace around certain keywords with regular expressions.
475
     * This will prepare the given array by escaping all characters.
476
     *
477
     * @param string[] $keywords
478
     * @param string   $delimiter
479
     *
480
     * @return string[]
481
     */
482
    protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
483
    {
484
        // escape keywords for use in regex
485
        $delimiter = array_fill(0, count($keywords), $delimiter);
486
        $escaped = array_map('preg_quote', $keywords, $delimiter);
487
488
        // add word boundaries
489
        array_walk($keywords, function ($value) {
490
            return '\b'.$value.'\b';
491
        });
492
493
        $keywords = array_combine($keywords, $escaped);
494
495
        return $keywords;
496
    }
497
498
    /**
499
     * Replaces all occurrences of array['key'] by array.key.
500
     *
501
     * @param string $content
502
     *
503
     * @return string
504
     */
505
    protected function propertyNotation($content)
506
    {
507
        // PHP only supports $this inside anonymous functions since 5.4
508
        $minifier = $this;
509
        $keywords = $this->keywordsReserved;
510
        $callback = function ($match) use ($minifier, $keywords) {
511
            $property = trim($minifier->extracted[$match[1]], '\'"');
512
513
            /*
514
             * Check if the property is a reserved keyword. In this context (as
515
             * property of an object literal/array) it shouldn't matter, but IE8
516
             * freaks out with "Expected identifier".
517
             */
518
            if (in_array($property, $keywords)) {
519
                return $match[0];
520
            }
521
522
            /*
523
             * See if the property is in a variable-like format (e.g.
524
             * array['key-here'] can't be replaced by array.key-here since '-'
525
             * is not a valid character there.
526
             */
527
            if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
528
                return $match[0];
529
            }
530
531
            return '.'.$property;
532
        };
533
534
        /*
535
         * Figure out if previous character is a variable name (of the array
536
         * we want to use property notation on) - this is to make sure
537
         * standalone ['value'] arrays aren't confused for keys-of-an-array.
538
         * We can (and only have to) check the last character, because PHP's
539
         * regex implementation doesn't allow unfixed-length look-behind
540
         * assertions.
541
         */
542
        preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
543
        $previousChar = $previousChar[1];
544
545
        /*
546
         * Make sure word preceding the ['value'] is not a keyword, e.g.
547
         * return['x']. Because -again- PHP's regex implementation doesn't allow
548
         * unfixed-length look-behind assertions, I'm just going to do a lot of
549
         * separate look-behind assertions, one for each keyword.
550
         */
551
        $keywords = $this->getKeywordsForRegex($keywords);
552
        $keywords = '(?<!'.implode(')(?<!', $keywords).')';
553
554
        return preg_replace_callback('/(?<='.$previousChar.'|\])'.$keywords.'\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
555
    }
556
557
    /**
558
     * Replaces true & false by !0 and !1.
559
     *
560
     * @param string $content
561
     *
562
     * @return string
563
     */
564
    protected function shortenBools($content)
565
    {
566
        /*
567
         * 'true' or 'false' could be used as property names (which may be
568
         * followed by whitespace) - we must not replace those!
569
         * Since PHP doesn't allow variable-length (to account for the
570
         * whitespace) lookbehind assertions, I need to capture the leading
571
         * character and check if it's a `.`
572
         */
573
        $callback = function ($match) {
574
            if (trim($match[1]) === '.') {
575
                return $match[0];
576
            }
577
578
            return $match[1].($match[2] === 'true' ? '!0' : '!1');
579
        };
580
        $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
581
582
        // for(;;) is exactly the same as while(true), but shorter :)
583
        $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
584
585
        // now make sure we didn't turn any do ... while(true) into do ... for(;;)
586
        preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
587
588
        // go backward to make sure positional offsets aren't altered when $content changes
589
        $dos = array_reverse($dos);
590
        foreach ($dos as $do) {
591
            $offsetDo = $do[0][1];
592
593
            // find all `while` (now `for`) following `do`: one of those must be
594
            // associated with the `do` and be turned back into `while`
595
            preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
596
            foreach ($whiles as $while) {
597
                $offsetWhile = $while[0][1];
598
599
                $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
600
                $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
601
                if ($open === $close) {
602
                    // only restore `while` if amount of `{` and `}` are the same;
603
                    // otherwise, that `for` isn't associated with this `do`
604
                    $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
605
                    break;
606
                }
607
            }
608
        }
609
610
        return $content;
611
    }
612
}
613