Completed
Push — release-2.1 ( 77cc5b...079e66 )
by Mert
06:13
created

JS::shortenBools()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 35
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 35
rs 8.5806
cc 4
eloc 17
nc 4
nop 1
1
<?php
2
3
namespace MatthiasMullie\Minify;
4
5
/**
6
 * JavaScript minifier.
7
 *
8
 * Please report bugs on https://github.com/matthiasmullie/minify/issues
9
 *
10
 * @author Matthias Mullie <[email protected]>
11
 * @author Tijs Verkoyen <[email protected]>
12
 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved.
13
 * @license MIT License
14
 */
15
class JS extends Minify
16
{
17
    /**
18
     * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
19
     *
20
     * Note that regular expressions using that bit must have the PCRE_UTF8
21
     * pattern modifier (/u) set.
22
     *
23
     * @var string
24
     */
25
    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';
26
27
    /**
28
     * Full list of JavaScript reserved words.
29
     * Will be loaded from /data/js/keywords_reserved.txt.
30
     *
31
     * @see https://mathiasbynens.be/notes/reserved-keywords
32
     *
33
     * @var string[]
34
     */
35
    protected $keywordsReserved = array();
36
37
    /**
38
     * List of JavaScript reserved words that accept a <variable, value, ...>
39
     * after them. Some end of lines are not the end of a statement, like with
40
     * these keywords.
41
     *
42
     * E.g.: we shouldn't insert a ; after this else
43
     * else
44
     *     console.log('this is quite fine')
45
     *
46
     * Will be loaded from /data/js/keywords_before.txt
47
     *
48
     * @var string[]
49
     */
50
    protected $keywordsBefore = array();
51
52
    /**
53
     * List of JavaScript reserved words that accept a <variable, value, ...>
54
     * before them. Some end of lines are not the end of a statement, like when
55
     * continued by one of these keywords on the newline.
56
     *
57
     * E.g.: we shouldn't insert a ; before this instanceof
58
     * variable
59
     *     instanceof String
60
     *
61
     * Will be loaded from /data/js/keywords_after.txt
62
     *
63
     * @var string[]
64
     */
65
    protected $keywordsAfter = array();
66
67
    /**
68
     * List of JavaScript operators that accept a <variable, value, ...> after
69
     * them. Some end of lines are not the end of a statement, like with these
70
     * operators.
71
     *
72
     * Note: Most operators are fine, we've only removed !, ++ and --.
73
     * There can't be a newline separating ! and whatever it is negating.
74
     * ++ & -- have to be joined with the value they're in-/decrementing.
75
     *
76
     * Will be loaded from /data/js/operators_before.txt
77
     *
78
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
79
     *
80
     * @var string[]
81
     */
82
    protected $operatorsBefore = array();
83
84
    /**
85
     * List of JavaScript operators that accept a <variable, value, ...> before
86
     * them. Some end of lines are not the end of a statement, like when
87
     * continued by one of these operators on the newline.
88
     *
89
     * Note: Most operators are fine, we've only removed ), ], ++ and --.
90
     * ++ & -- have to be joined with the value they're in-/decrementing.
91
     * ) & ] are "special" in that they have lots or usecases. () for example
92
     * is used for function calls, for grouping, in if () and for (), ...
93
     *
94
     * Will be loaded from /data/js/operators_after.txt
95
     *
96
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
97
     *
98
     * @var string[]
99
     */
100
    protected $operatorsAfter = array();
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function __construct()
106
    {
107
        call_user_func_array(array('parent', '__construct'), func_get_args());
108
109
        $dataDir = __DIR__.'/../data/js/';
110
        $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
111
        $this->keywordsReserved = file($dataDir.'keywords_reserved.txt', $options);
112
        $this->keywordsBefore = file($dataDir.'keywords_before.txt', $options);
113
        $this->keywordsAfter = file($dataDir.'keywords_after.txt', $options);
114
        $this->operatorsBefore = file($dataDir.'operators_before.txt', $options);
115
        $this->operatorsAfter = file($dataDir.'operators_after.txt', $options);
116
    }
117
118
    /**
119
     * Minify the data.
120
     * Perform JS optimizations.
121
     *
122
     * @param string[optional] $path Path to write the data to.
123
     *
124
     * @return string The minified data.
125
     */
126
    public function execute($path = null)
127
    {
128
        $content = '';
129
130
        // loop files
131
        foreach ($this->data as $source => $js) {
132
            /*
133
             * Combine js: separating the scripts by a ;
134
             * I'm also adding a newline: it will be eaten when whitespace is
135
             * stripped, but we need to make sure we're not just appending
136
             * a new script right after a previous script that ended with a
137
             * singe-line comment on the last line (in which case it would also
138
             * be seen as part of that comment)
139
             */
140
            $content .= $js."\n;";
141
        }
142
143
        /*
144
         * Let's first take out strings, comments and regular expressions.
145
         * All of these can contain JS code-like characters, and we should make
146
         * sure any further magic ignores anything inside of these.
147
         *
148
         * Consider this example, where we should not strip any whitespace:
149
         * var str = "a   test";
150
         *
151
         * Comments will be removed altogether, strings and regular expressions
152
         * will be replaced by placeholder text, which we'll restore later.
153
         */
154
        $this->extractStrings('\'"`');
155
        $this->stripComments();
156
        $this->extractRegex();
157
        $content = $this->replace($content);
158
159
        $content = $this->stripWhitespace($content);
160
        $content = $this->propertyNotation($content);
161
        $content = $this->shortenBools($content);
162
163
        /*
164
         * Earlier, we extracted strings & regular expressions and replaced them
165
         * with placeholder text. This will restore them.
166
         */
167
        $content = $this->restoreExtractedData($content);
168
169
        return $content;
170
    }
171
172
    /**
173
     * Strip comments from source code.
174
     */
175
    protected function stripComments()
176
    {
177
        // single-line comments
178
        $this->registerPattern('/\/\/.*$/m', '');
179
180
        // multi-line comments
181
        $this->registerPattern('/\/\*.*?\*\//s', '');
182
    }
183
184
    /**
185
     * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
186
     *
187
     * The content inside the regex can contain characters that may be confused
188
     * for JS code: e.g. it could contain whitespace it needs to match & we
189
     * don't want to strip whitespace in there.
190
     *
191
     * The regex can be pretty simple: we don't have to care about comments,
192
     * (which also use slashes) because stripComments() will have stripped those
193
     * already.
194
     *
195
     * This method will replace all string content with simple REGEX#
196
     * placeholder text, so we've rid all regular expressions from characters
197
     * that may be misinterpreted. Original regex content will be saved in
198
     * $this->extracted and after doing all other minifying, we can restore the
199
     * original content via restoreRegex()
200
     */
201
    protected function extractRegex()
202
    {
203
        // PHP only supports $this inside anonymous functions since 5.4
204
        $minifier = $this;
205
        $callback = function ($match) use ($minifier) {
206
            $count = count($minifier->extracted);
207
            $placeholder = '/'.$count.'/';
208
            $minifier->extracted[$placeholder] = $match[1];
209
210
            return $placeholder;
211
        };
212
213
        // it's a regex if we can find an opening and (not escaped) closing /,
214
        // include \n because it may be there for a reason
215
        // (https://github.com/matthiasmullie/minify/issues/56)
216
        $pattern = '(\/.*?(?<!\\\\)(\\\\\\\\)*+\/\n?)';
217
218
        // / can't be preceded by variable, value, or similar because then
219
        // it's going to be division
220
        // checking for that is complex, so we'll do inverse:
221
        // * at the beginning of the file, it's not division, but regex
222
        $this->registerPattern('/^\s*\K'.$pattern.'/', $callback);
223
        // * following another operator, it's not division, but regex
224
        $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
225
        $operators += $this->getKeywordsForRegex($this->keywordsReserved, '/');
226
        $this->registerPattern('/(?:'.implode('|', $operators).')\s*\K'.$pattern.'/', $callback);
227
    }
228
229
    /**
230
     * Strip whitespace.
231
     *
232
     * We won't strip *all* whitespace, but as much as possible. The thing that
233
     * we'll preserve are newlines we're unsure about.
234
     * JavaScript doesn't require statements to be terminated with a semicolon.
235
     * It will automatically fix missing semicolons with ASI (automatic semi-
236
     * colon insertion) at the end of line causing errors (without semicolon.)
237
     *
238
     * Because it's sometimes hard to tell if a newline is part of a statement
239
     * that should be terminated or not, we'll just leave some of them alone.
240
     *
241
     * @param string $content The content to strip the whitespace for.
242
     *
243
     * @return string
244
     */
245
    protected function stripWhitespace($content)
246
    {
247
        // uniform line endings, make them all line feed
248
        $content = str_replace(array("\r\n", "\r"), "\n", $content);
249
250
        // collapse all non-line feed whitespace into a single space
251
        $content = preg_replace('/[^\S\n]+/', ' ', $content);
252
253
        // strip leading & trailing whitespace
254
        $content = str_replace(array(" \n", "\n "), "\n", $content);
255
256
        // collapse consecutive line feeds into just 1
257
        $content = preg_replace('/\n+/', "\n", $content);
258
259
        $before = $this->getOperatorsForRegex($this->operatorsBefore, '/');
260
        $after = $this->getOperatorsForRegex($this->operatorsAfter, '/');
261
        $operators = $before + $after;
262
263
        // strip whitespace that ends in (or next line begin with) an operator
264
        // that allows statements to be broken up over multiple lines
265
        unset($before['+'], $before['-'], $after['+'], $after['-']);
266
        $content = preg_replace(
267
            array(
268
                '/('.implode('|', $before).')\s+/',
269
                '/\s+('.implode('|', $after).')/',
270
            ), '\\1', $content
271
        );
272
273
        // make sure + and - can't be mistaken for, or joined into ++ and --
274
        $content = preg_replace(
275
            array(
276
                '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
277
                '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
278
            ), '\\1', $content
279
        );
280
281
        /*
282
         * We didn't strip whitespace after a couple of operators because they
283
         * could be used in different contexts and we can't be sure it's ok to
284
         * strip the newlines. However, we can safely strip any non-line feed
285
         * whitespace that follows them.
286
         */
287
        $content = preg_replace('/([\}\)\]])[^\S\n]+(?!'.implode('|', $operators).')/', '\\1', $content);
288
289
        // collapse whitespace around reserved words into single space
290
        $before = $this->getKeywordsForRegex($this->keywordsBefore, '/');
291
        $after = $this->getKeywordsForRegex($this->keywordsAfter, '/');
292
        $content = preg_replace('/(^|[;\}\s])\K('.implode('|', $before).')\s+/', '\\2 ', $content);
293
        $content = preg_replace('/\s+('.implode('|', $after).')(?=([;\{\s]|$))/', ' \\1', $content);
294
295
        /*
296
         * Get rid of double semicolons, except where they can be used like:
297
         * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
298
         * I'll safeguard these double semicolons inside for-loops by
299
         * temporarily replacing them with an invalid condition: they won't have
300
         * a double semicolon and will be easy to spot to restore afterwards.
301
         */
302
        $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
303
        $content = preg_replace('/;+/', ';', $content);
304
        $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
305
306
        /*
307
         * Next, we'll be removing all semicolons where ASI kicks in.
308
         * for-loops however, can have an empty body (ending in only a
309
         * semicolon), like: `for(i=1;i<3;i++);`
310
         * Here, nothing happens during the loop; it's just used to keep
311
         * increasing `i`. With that ; omitted, the next line would be expected
312
         * to be the for-loop's body...
313
         * I'm going to double that semicolon (if any) so after the next line,
314
         * which strips semicolons here & there, we're still left with this one.
315
         */
316
        $content = preg_replace('/(for\([^;]*;[^;]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
317
318
        /*
319
         * We also can't strip empty else-statements. Even though they're
320
         * useless and probably shouldn't be in the code in the first place, we
321
         * shouldn't be stripping the `;` that follows it as it breaks the code.
322
         * We can just remove those useless else-statements completely.
323
         *
324
         * @see https://github.com/matthiasmullie/minify/issues/91
325
         */
326
        $content = preg_replace('/else;/s', '', $content);
327
328
        /*
329
         * We also don't really want to terminate statements followed by closing
330
         * curly braces (which we've ignored completely up until now) or end-of-
331
         * script: ASI will kick in here & we're all about minifying.
332
         * Semicolons at beginning of the file don't make any sense either.
333
         */
334
        $content = preg_replace('/;(\}|$)/s', '\\1', $content);
335
        $content = ltrim($content, ';');
336
337
        // get rid of remaining whitespace af beginning/end
338
        return trim($content);
339
    }
340
341
    /**
342
     * We'll strip whitespace around certain operators with regular expressions.
343
     * This will prepare the given array by escaping all characters.
344
     *
345
     * @param string[] $operators
346
     * @param string   $delimiter
347
     *
348
     * @return string[]
349
     */
350
    protected function getOperatorsForRegex(array $operators, $delimiter = '/')
351
    {
352
        // escape operators for use in regex
353
        $delimiter = array_fill(0, count($operators), $delimiter);
354
        $escaped = array_map('preg_quote', $operators, $delimiter);
355
356
        $operators = array_combine($operators, $escaped);
357
358
        // ignore + & - for now, they'll get special treatment
359
        unset($operators['+'], $operators['-']);
360
361
        // dot can not just immediately follow a number; it can be confused for
362
        // decimal point, or calling a method on it, e.g. 42 .toString()
363
        $operators['.'] = '(?<![0-9]\s)\.';
364
365
        // don't confuse = with other assignment shortcuts (e.g. +=)
366
        $chars = preg_quote('+-*\=<>%&|');
367
        $operators['='] = '(?<!['.$chars.'])\=';
368
369
        return $operators;
370
    }
371
372
    /**
373
     * We'll strip whitespace around certain keywords with regular expressions.
374
     * This will prepare the given array by escaping all characters.
375
     *
376
     * @param string[] $keywords
377
     * @param string   $delimiter
378
     *
379
     * @return string[]
380
     */
381
    protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
382
    {
383
        // escape keywords for use in regex
384
        $delimiter = array_fill(0, count($keywords), $delimiter);
385
        $escaped = array_map('preg_quote', $keywords, $delimiter);
386
387
        // add word boundaries
388
        array_walk($keywords, function ($value) {
389
            return '\b'.$value.'\b';
390
        });
391
392
        $keywords = array_combine($keywords, $escaped);
393
394
        return $keywords;
395
    }
396
397
    /**
398
     * Replaces all occurrences of array['key'] by array.key.
399
     *
400
     * @param string $content
401
     *
402
     * @return string
403
     */
404
    protected function propertyNotation($content)
405
    {
406
        // PHP only supports $this inside anonymous functions since 5.4
407
        $minifier = $this;
408
        $keywords = $this->keywordsReserved;
409
        $callback = function ($match) use ($minifier, $keywords) {
410
            $property = trim($minifier->extracted[$match[1]], '\'"');
411
412
            /*
413
             * Check if the property is a reserved keyword. In this context (as
414
             * property of an object literal/array) it shouldn't matter, but IE8
415
             * freaks out with "Expected identifier".
416
             */
417
            if (in_array($property, $keywords)) {
418
                return $match[0];
419
            }
420
421
            /*
422
             * See if the property is in a variable-like format (e.g.
423
             * array['key-here'] can't be replaced by array.key-here since '-'
424
             * is not a valid character there.
425
             */
426
            if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
427
                return $match[0];
428
            }
429
430
            return '.'.$property;
431
        };
432
433
        /*
434
         * Figure out if previous character is a variable name (of the array
435
         * we want to use property notation on) - this is to make sure
436
         * standalone ['value'] arrays aren't confused for keys-of-an-array.
437
         * We can (and only have to) check the last character, because PHP's
438
         * regex implementation doesn't allow unfixed-length look-behind
439
         * assertions.
440
         */
441
        preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
442
        $previousChar = $previousChar[1];
443
444
        /*
445
         * Make sure word preceding the ['value'] is not a keyword, e.g.
446
         * return['x']. Because -again- PHP's regex implementation doesn't allow
447
         * unfixed-length look-behind assertions, I'm just going to do a lot of
448
         * separate look-behind assertions, one for each keyword.
449
         */
450
        $keywords = $this->getKeywordsForRegex($keywords);
451
        $keywords = '(?<!'.implode(')(?<!', $keywords).')';
452
453
        return preg_replace_callback('/(?<='.$previousChar.'|\])'.$keywords.'\[(([\'"])[0-9]+\\2)\]/u', $callback, $content);
454
    }
455
456
    /**
457
     * Replaces true & false by !0 and !1.
458
     *
459
     * @param string $content
460
     *
461
     * @return string
462
     */
463
    protected function shortenBools($content)
464
    {
465
        $content = preg_replace('/\btrue\b/', '!0', $content);
466
        $content = preg_replace('/\bfalse\b/', '!1', $content);
467
468
        // for(;;) is exactly the same as while(true)
469
        $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
470
471
        // now make sure we didn't turn any do ... while(true) into do ... for(;;)
472
        preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
473
474
        // go backward to make sure positional offsets aren't altered when $content changes
475
        $dos = array_reverse($dos);
476
        foreach ($dos as $do) {
477
            $offsetDo = $do[0][1];
478
479
            // find all `while` (now `for`) following `do`: one of those must be
480
            // associated with the `do` and be turned back into `while`
481
            preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
482
            foreach ($whiles as $while) {
0 ignored issues
show
Bug introduced by
The expression $whiles of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
483
                $offsetWhile = $while[0][1];
484
485
                $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
486
                $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
487
                if ($open === $close) {
488
                    // only restore `while` if amount of `{` and `}` are the same;
489
                    // otherwise, that `for` isn't associated with this `do`
490
                    $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
491
                    break;
492
                }
493
            }
494
        }
495
496
        return $content;
497
    }
498
}
499