├── LICENSE ├── README.md ├── composer.json ├── config └── laravel-html-minifier.php ├── example-output ├── after.jpg └── before.jpg └── src ├── HtmlMinifierServiceProvider.php └── Middleware ├── Minifier.php ├── MinifyCss.php ├── MinifyHtml.php └── MinifyJavascript.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 :D 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Build Status 3 | Latest Stable Version 4 | Total Downloads 5 | License 6 |

7 | 8 | # Laravel Html Minifier 9 | 10 | Adalah Paket simpel untuk minify HTML, Css Style, dan Javascript sebelum dirender ke browser untuk aplikasi Laravel anda. 11 | 12 | Alat ini hanya bekerja jika output yang diberikan adalah bentuk struktur html yang valid meliputi tag html, head dan body. contohnya 13 | 14 | - Html yang valid (akan diproses dan diminify) 15 | 16 | ```html 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ``` 26 | 27 | - Html yang tidak valid (tidak diminify) 28 | 29 | ```html 30 | < html> 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | atau 40 | 41 | 42 | 43 | 44 | 45 | 46 | ``` 47 | 48 | ## Contoh Hasilnya : 49 | 50 | ![Sebelum](example-output/before.jpg) 51 | 52 | ![Sesudah](example-output/after.jpg) 53 | 54 | ## Installasi 55 | 56 | > **Membutuhkan:** 57 | - **[PHP 7.2.5+](https://php.net/releases/)** 58 | - **[Laravel 6.0+](https://github.com/laravel/laravel)** 59 | 60 | ## Tahap pertama anda bisa menginstall paket ini dengan [Composer 2x](https://getcomposer.org/download/) 61 | 62 | ```sh 63 | composer require dz-id/laravel-html-minifier 64 | ``` 65 | 66 | ## Publish konfigurasi file 67 | 68 | ```sh 69 | php artisan vendor:publish --provider="DzId\LaravelHtmlMinifier\HtmlMinifierServiceProvider" 70 | ``` 71 | 72 | ## Jangan lupa untuk mendaftarkan ke Global Middleware 73 | 74 | [\DzId\LaravelHtmlMinifier\Middleware\MinifyHtml::class](src/Middleware/MinifyHtml.php) dan Middleware lainnya harus didaftarkan ke kernel jika diperlukan, contoh : 75 | 76 | ```php 77 | 78 | // file : app/Http/Karnel.php 79 | 80 | protected $middleware = [ 81 | .... 82 | \DzId\LaravelHtmlMinifier\Middleware\MinifyHtml::class, // middleware untuk minify html 83 | \DzId\LaravelHtmlMinifier\Middleware\MinifyCss::class, // middleware untuk minify css style 84 | \DzId\LaravelHtmlMinifier\Middleware\MinifyJavascript::class, // middleware untuk minify kode javascript 85 | ]; 86 | ``` 87 | 88 | ## Informasi Middleware 89 | 90 | ##### [\DzId\LaravelHtmlMinifier\Middleware\MinifyHtml::class](src/Middleware/MinifyHtml.php) 91 | 92 | ```MinifyHtml::class``` fungsinya adalah untuk minify html menghapus blank spasi dan juga baris baru menjadi satu baris. 93 | 94 | Contoh Hasil : 95 | 96 | - Sebelum diminify 97 | ```html 98 | 99 | 100 | Laravel Html Minifier 101 | 102 | 103 |

Laravel Html Minifier

104 | 105 | 106 | ``` 107 | 108 | - Sesudah diminify 109 | ```html 110 | Laravel Html Minifier

Laravel Html Minifier

111 | ``` 112 | 113 | ##### [\DzId\LaravelHtmlMinifier\Middleware\MinifyCss::class](src/Middleware/MinifyCss.php) 114 | 115 | ```MinifyCss::class``` fungsinya adalah untuk minify css style menghapus blank spasi dan juga baris baru menjadi satu baris. 116 | 117 | Contoh Hasil : 118 | 119 | - Sebelum diminify 120 | ```css 121 | body { 122 | background-color: salmon; 123 | width: 100%; 124 | height: 100%; 125 | } 126 | ``` 127 | 128 | - Sesudah diminify 129 | ```css 130 | body{background-color:salmon;width:100%;height:100%} 131 | ``` 132 | 133 | ##### [\DzId\LaravelHtmlMinifier\Middleware\MinifyJavascript::class](src/Middleware/MinifyJavascript.php) 134 | 135 | ```MinifyJavascript::class``` fungsinya adalah untuk minify kode javascript menghapus blank spasi dan juga baris baru menjadi satu baris. 136 | 137 | Catatan: jangan menggunakan kode tanpa kurung kurawal (```{}```) untuk if, elseif, else, while, for, dll. ini akan menyebebkan kode anda error, contohnya 138 | 139 | - kode yang disarankan 140 | ```javascript 141 | for (let i = 0; i < 10; i++) { 142 | console.log('hello dunia'); 143 | } 144 | ``` 145 | 146 | - kode yang tidak disarankan, tanpa kurung kurawal biasanya akan menyebabkan error jika menggunakan ```MinifyJavascript::class``` 147 | ```javascript 148 | for (let i = 0; i < 10; i++) // tanpa 149 | console.log('hello dunia'); // kurung kurawal 150 | ``` 151 | 152 | - sesudah diminify 153 | ```javascript 154 | for (let i = 0; i < 10; i++){console.log('hello dunia')} 155 | ``` 156 | 157 | kamu juga bisa mengaburkan kode javascript dengan menyetel ke ```true``` bagian ```"obfuscate_javascript"``` dalam file : [config/laravel-html-minifier.php](config/laravel-html-minifier.php) 158 | 159 | ## File Konfigurasi 160 | 161 | Setelah menginstall paket anda mungkin perlu mengkonfigurasi beberapa opsi 162 | Silahkan masuk ke file [config/laravel-html-minifier.php](config/laravel-html-minifier.php) untuk mengubah konfigurasi 163 | 164 | ##### Nonaktifkan Layanan 165 | 166 | Anda cukup menyetel ke ```false``` untuk menonaktifkan Layanan. 167 | 168 | ```php 169 | 170 | // file: config/laravel-html-minifier.php 171 | 172 | // setel bagian ini ke false untuk menonaktifkan layanan minify Laravel. 173 | "enable" => env("LARAVEL_HTML_MINIFIER_ENABLE", true), 174 | ``` 175 | 176 | ##### Otomatis Menambahkan Semicolon Atau Titik Koma Diakhir kode Pada CSS 177 | 178 | Jika kode css anda mengalami bug saat menggunakan ```MinifyCss::class``` silahkan setel bidang ini ke ```false```. 179 | 180 | ```php 181 | 182 | // file: config/laravel-html-minifier.php 183 | 184 | "css_automatic_insert_semicolon" => env("LARAVEL_HTML_MINIFIER_CSS_AUTOMATIC_INSERT_SEMICOLON", true), 185 | ``` 186 | 187 | ##### Otomatis Menambahkan Semicolon Atau Titik Koma Diakhir kode Pada Javascript 188 | 189 | Catatan: Jangan menggunakan jeda baris untuk while, do while, for, if, elseif, else, return, dll. ataupun kode tanpa kurung kurawal (```{}```). contohnya 190 | 191 | - kode yang disarankan 192 | ```javascript 193 | var log = function(log) { 194 | return console.log(log); 195 | } 196 | 197 | let i = 0; 198 | 199 | do { 200 | if (i == 5) { 201 | break; 202 | } 203 | i++; 204 | log("hello dunia"); 205 | } while (true); 206 | ``` 207 | 208 | - kode yang tidak disarankan, akan menyebabkan error jika mengaktifkan "js_automatic_insert_semicolon" 209 | ```javascript 210 | var log = function(log) { 211 | return // jeda 212 | console.log(log); // baris 213 | } 214 | 215 | let i = 0; 216 | 217 | do 218 | // jeda baris 219 | { 220 | if (i == 5) // tanpa 221 | break; // kurung kurawal 222 | i++; 223 | log("hello dunia"); 224 | } 225 | // jeda baris 226 | while (true); 227 | ``` 228 | 229 | mungkin aja jika mengaktifkan bidang ini kode anda akan menjadi bug karena suatu kondisi yang salah 230 | dalam hal ini jika itu terjadi anda cukup menyetel bidang ini ke ```false``` 231 | 232 | jangan lupa untuk selalu menggunakan titik koma pada kode javascript jika bidang ini disetel ke ```false``` 233 | 234 | fungsi ini hanya berlaku jika menggunakan ```MinifyJavascript::class``` 235 | 236 | ```php 237 | 238 | // file: config/laravel-html-minifier.php 239 | 240 | "js_automatic_insert_semicolon" => env("LARAVEL_HTML_MINIFIER_JS_AUTOMATIC_INSERT_SEMICOLON", true), 241 | ``` 242 | 243 | ##### Menghapus Komentar HTML 244 | 245 | Cukup setel ke ```true``` untuk mengaktifkan dan setel ke ```false``` untuk menonaktifkan. 246 | 247 | Fungsi ini hanya berlaku jika menggunakan Middleware ```MinifyHtml::class``` 248 | 249 | ```php 250 | 251 | // file: config/laravel-html-minifier.php 252 | 253 | // setel bidang ini ke false untuk mematikan 254 | "remove_comments" => env("LARAVEL_HTML_MINIFIER_REMOVE_COMMENTS", true), 255 | ``` 256 | 257 | ##### Kaburkan Kode Javascript (Obfuscate) 258 | 259 | Catatan : jika ada mengaktifkan fungsi ini mungkin kode javascript anda akan menjadi panjang, 260 | Fungsi ini akan mengubah satu per satu dari setiap string / text ke ```chr()``` fungsi PHP dan didecode dengan ```String.fromCharCode()``` fungsi javascript. 261 | 262 | Fungsi ini hanya berlaku jika kamu menggunakan Middleware ```MinifyJavascript::class``` jika bidang ini disetel ke ```false``` kode javascript hanya diminify tidak dikaburkan/Obfuscate 263 | 264 | Contoh Hasil : 265 | 266 | - Sebelum dikaburkan 267 | ```javascript 268 | alert(1); 269 | ``` 270 | 271 | - Sesudah dikaburkan 272 | ```javascript 273 | eval(((_,__,___,____,_____,______,_______)=>{______[___](x=>_______[__](String[____](x)));return _______[_](_____)})('join','push','forEach','fromCharCode','',[97,108,101,114,116,40,49,41,59],[])) 274 | ``` 275 | 276 | Dalam kasus ini kamu cukup menyetelnya ke ```false``` untuk menonaktifkan dan menyetelnya ke ```true``` untuk mengaktifkan 277 | 278 | ```php 279 | 280 | // file : config/laravel-html-minifier.php 281 | 282 | // setel ke true untuk mengaktifkan 283 | "obfuscate_javascript" => env("LARAVEL_HTML_MINIFIER_OBFUSCATE_JS", false), 284 | ``` 285 | 286 | ##### Ignore / Abaikan Route 287 | 288 | Anda mungkin ingin mengonfigurasi paket untuk melewati beberapa rute. 289 | 290 | ```php 291 | 292 | // file : config/laravel-html-minifier.php 293 | 294 | "ignore" => [ 295 | "*/download/*", // Abaikan semua route yang mengandung download 296 | "admin/*", // Abaikan semua route dengan awalan admin, 297 | "*/user" // Abaikan route dengan akhiran user 298 | ] 299 | ``` 300 | 301 | ## Skip / Lewati dengan menambahkan attribute ignore--minify 302 | 303 | Kamu cukup menambahkan attribute ```ignore--minify``` dalam tag script / style untuk melewati proses minify. 304 | 305 | Contoh : 306 | 307 | ```html 308 | 309 | 312 | 313 | 316 | ``` 317 | 318 | Setiap tag style/script yang memiliki attribute ```ignore--minify``` akan dilewati tidak diminify. 319 | 320 | ## Skip / Lewati View dengan menambahkan data "ignore_minify" ke dalam view 321 | 322 | Kamu juga bisa melewati minify dengan memasukan data ```"ignore_minify"``` kedalam view 323 | 324 | Contoh : 325 | 326 | ```php 327 | 328 | // View ini tidak diminify akan di skip. 329 | return view("welcome", ["ignore_minify" => true]); 330 | ``` 331 | 332 | ## Lisensi 333 | 334 | [MIT](LICENSE) (MIT) 335 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dz-id/laravel-html-minifier", 3 | "description": "Html Minifier adalah paket simpel untuk minify output Html, Css style, dan Javascript sebelum dirender ke browser untuk aplikasi Laravel anda.", 4 | "keywords": [ 5 | "laravel", 6 | "Laravel Html Minifier", 7 | "Laravel-Html-Minifier", 8 | "Html Minify", 9 | "Minify" 10 | ], 11 | "authors": [ 12 | { 13 | "name": "DulLah", 14 | "email": "dulah755@gmail.com" 15 | } 16 | ], 17 | "require": { 18 | "php": "^7.2.5 || ^8.0", 19 | "illuminate/support": "^6.0 || ^7.0 || ^8.0", 20 | "illuminate/http": "^6.0 || ^7.0 || ^8.0", 21 | "illuminate/view": "^6.0 || ^7.0 || ^8.0" 22 | }, 23 | "license": "MIT", 24 | "autoload": { 25 | "psr-4": { 26 | "DzId\\LaravelHtmlMinifier\\": "src/" 27 | } 28 | }, 29 | "minimum-stability": "dev", 30 | "extra": { 31 | "laravel": { 32 | "providers": [ 33 | "DzId\\LaravelHtmlMinifier\\HtmlMinifierServiceProvider" 34 | ] 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /config/laravel-html-minifier.php: -------------------------------------------------------------------------------- 1 | env("LARAVEL_HTML_MINIFIER_ENABLE", true), 13 | 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | Otomatis menambahkan semicolon diakhir kode untuk CSS 17 | |-------------------------------------------------------------------------- 18 | | 19 | | Silahkan setel bidang ini ke false 20 | | jika kode CSS anda mengalami bug atau tidak berfungi 21 | | setelah menggunakan MinifyCss::class 22 | | 23 | */ 24 | "css_automatic_insert_semicolon" => env("LARAVEL_HTML_MINIFIER_CSS_AUTOMATIC_INSERT_SEMICOLON", true), 25 | 26 | /* 27 | |-------------------------------------------------------------------------- 28 | | Otomatis menambahkan semicolon diakhir kode untuk Javascript 29 | |-------------------------------------------------------------------------- 30 | | 31 | | Silahkan setel bidang ini ke false 32 | | jika kode Javascript anda mengalami bug atau tidak berfungi 33 | | setelah menggunakan MinifyJavascript::class 34 | | 35 | | Dan jangan lupa untuk selalu mengakhiri dengan semicolon 36 | | jika bidang ini disetel ke false. 37 | */ 38 | "js_automatic_insert_semicolon" => env("LARAVEL_HTML_MINIFIER_JS_AUTOMATIC_INSERT_SEMICOLON", true), 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Aktifkan penghapusan komentar pada HTML 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Setel bagian ini ke false jika komentar HTML tidak ingin dihapus 46 | | 47 | | Catatan: Pengaturan ini akan berlaku jika menggunakan Middleware MinifyHtml::class 48 | | 49 | */ 50 | "remove_comments" => env("LARAVEL_HTML_MINIFIER_REMOVE_COMMENTS", true), 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | Kaburkan Kode Javascript 55 | |-------------------------------------------------------------------------- 56 | | 57 | | Pengaturan ini akan berlaku jika menggunakan MinifyJavascript::class 58 | | 59 | | Jika di setel ke true maka kode javascript akan dikaburkan, 60 | | Akan mentranslate kode js ke ord() fungsi php 61 | | Dan akan didecode dengan String.fromCharCode() Fungsi javascript 62 | | 63 | | Catatan: Mungkin fungsi ini akan membuat kode Anda menjadi panjang. 64 | | 65 | */ 66 | "obfuscate_javascript" => env("LARAVEL_HTML_MINIFIER_OBFUSCATE_JS", false), 67 | 68 | /* 69 | |-------------------------------------------------------------------------- 70 | | Abaikan Routes 71 | |-------------------------------------------------------------------------- 72 | | 73 | | Dalam pengecekan akan menggunakan fungsi request()->is(), 74 | | Dan jika route cocok maka akan diabaikan / Tidak diminify. 75 | | Anda dapat menggunakan * sebagai wildcard. 76 | | 77 | */ 78 | 79 | "ignore" => [ 80 | // "*/download/*", // Abaikan semua route yang mengandung download 81 | // "admin/*", // Abaikan semua route dengan awalan admin, 82 | // "*/user" // Abaikan route dengan akhiran user 83 | ], 84 | ]; 85 | -------------------------------------------------------------------------------- /example-output/after.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dz-id/laravel-html-minifier/0f016d1a7b6ec8b2d7a8696e26c2b8ecf4dac0ac/example-output/after.jpg -------------------------------------------------------------------------------- /example-output/before.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dz-id/laravel-html-minifier/0f016d1a7b6ec8b2d7a8696e26c2b8ecf4dac0ac/example-output/before.jpg -------------------------------------------------------------------------------- /src/HtmlMinifierServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 22 | __DIR__."/../config/laravel-html-minifier.php" => config_path("laravel-html-minifier.php"), 23 | ]); 24 | } 25 | 26 | /** 27 | * Register the service provider. 28 | */ 29 | public function register() 30 | { 31 | $this->mergeConfigFrom(__DIR__."/../config/laravel-html-minifier.php", "laravel-html-minifier.php"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Middleware/Minifier.php: -------------------------------------------------------------------------------- 1 | ]*>.*]*>.*<\/head[^>]*>.*]*>.*<\/body[^>]*>.*<\/html[^>]*>/is"; 23 | 24 | abstract protected function apply(); 25 | 26 | /** 27 | * Handle an incoming request. 28 | * 29 | * @param \Illuminate\Http\Request $request 30 | * @param \Closure $next 31 | * @return mixed 32 | */ 33 | public function handle(Request $request, Closure $next) 34 | { 35 | $response = $next($request); 36 | 37 | /* Apakah boleh menjalankan minify? */ 38 | if (!$this->shouldProcessMinify($request, $response)) 39 | { 40 | if (!(static::$dom instanceof DOMDocument)) 41 | { 42 | return $response; 43 | } 44 | } 45 | 46 | $html = $response->getContent(); 47 | 48 | $this->loadDom($html); 49 | 50 | return $response->setContent($this->apply()); 51 | } 52 | 53 | protected function shouldProcessMinify($request, $response) : bool 54 | { 55 | /* Apakah fungsi laravel-html-minifier.enable dimatikan? */ 56 | if (!$this->isEnable()) 57 | { 58 | return false; 59 | } 60 | 61 | /* Apakah responnya adalah json? */ 62 | if ($response instanceof JsonResponse) 63 | { 64 | return false; 65 | } 66 | 67 | /* Apakah responnya adalah binnary? */ 68 | if ($response instanceof BinaryFileResponse) 69 | { 70 | return false; 71 | } 72 | 73 | /* Apakah responsenya adalah stream? */ 74 | if ($response instanceof StreamedResponse) 75 | { 76 | return false; 77 | } 78 | 79 | /* Apakah responnya dari view() fungsi laravel? */ 80 | if ($response->original instanceof View) 81 | { 82 | $data = $response->original->getData(); 83 | /* Apakah ada data ignore_minify di view() dan isinya true? */ 84 | if (isset($data["ignore_minify"]) && $data["ignore_minify"] === true) 85 | { 86 | return false; 87 | } 88 | } 89 | 90 | foreach ($this->ignore() as $route) 91 | { 92 | /* Apakah route yang sedang diakses cocok dengan yang ada di laravel-html-minifier.ignore? */ 93 | if ($request->is($route)) 94 | { 95 | return false; 96 | } 97 | } 98 | 99 | $response = $response->getContent(); 100 | 101 | /* Apakah responnya kosong atau NULL? */ 102 | if (empty($response) or !is_string($response) or $this->isEmpty($response)) 103 | { 104 | return false; 105 | } 106 | 107 | /* 108 | * $result = true; 109 | * 110 | * foreach (["html", "body"] as $name) 111 | * { 112 | * $result = $result && !is_null($this->matchHtmlTag($response, $name)); 113 | * } 114 | * 115 | * return $result; 116 | */ 117 | 118 | /* Apakah responnya adalah bentuk syntax html yang valid? */ 119 | return $this->validHtml($response); 120 | } 121 | 122 | protected function isEmpty(string $value) : bool 123 | { 124 | return (bool) preg_match("/^\s*$/", $value); 125 | } 126 | 127 | protected function validHtml(string $value) : bool 128 | { 129 | return (bool) preg_match(self::REGEX_VALID_HTML, $value); 130 | } 131 | 132 | protected function isEnable() : bool 133 | { 134 | /* Apakah laravel-html-minifier.enable belum dipanggil? */ 135 | if (is_null(static::$isEnable)) 136 | { 137 | static::$isEnable = (bool) config("laravel-html-minifier.enable", true); 138 | } 139 | 140 | return static::$isEnable; 141 | } 142 | 143 | protected function ignore() : array 144 | { 145 | /* Apakah laravel-html-minifier.enable belum dipanggil? */ 146 | if (is_null(static::$ignore)) 147 | { 148 | static::$ignore = (array) config("laravel-html-minifier.ignore", []); 149 | } 150 | 151 | return static::$ignore; 152 | } 153 | 154 | protected function matchHtmlTag(string $value, string $tags) 155 | { 156 | if (!preg_match_all("/<" . $tags . "[^>]*>(.*?)<\/" . $tags . "[^>]*>/is", $value, $matches)) 157 | { 158 | return null; 159 | } 160 | 161 | return $matches; 162 | } 163 | 164 | protected function loadDom(string $html, bool $force = false) 165 | { 166 | /* Apakah dom sudah diload? */ 167 | if ((static::$dom instanceof DOMDocument)) 168 | { 169 | /* Apakah dipaksa untuk meload dom lagi? */ 170 | if ($force) 171 | { 172 | 173 | } 174 | else 175 | { 176 | return; 177 | } 178 | } 179 | 180 | static::$dom = new DOMDocument; 181 | @static::$dom->loadHTML($html, LIBXML_HTML_NODEFDTD | LIBXML_SCHEMA_CREATE); 182 | } 183 | 184 | protected function getByTag(string $tags) : array 185 | { 186 | $result = []; 187 | $element = static::$dom->getElementsByTagName($tags); 188 | 189 | foreach ($element as $el) 190 | { 191 | $value = $el->nodeValue; 192 | 193 | /* Apakah isinya kosong? */ 194 | if ($this->isEmpty($value)) 195 | { 196 | continue; 197 | } 198 | 199 | /* Apakah memiliki attribute ignore--minify? */ 200 | if ($el->hasAttribute("ignore--minify")) 201 | { 202 | continue; 203 | } 204 | 205 | $result[] = $el; 206 | } 207 | 208 | return $result; 209 | } 210 | 211 | protected function getByTagOnlyIgnored(string $tags) : array 212 | { 213 | $result = []; 214 | $element = static::$dom->getElementsByTagName($tags); 215 | 216 | foreach ($element as $el) 217 | { 218 | $value = $el->nodeValue; 219 | 220 | /* Apakah isinya kosong? */ 221 | if ($this->isEmpty($value)) 222 | { 223 | continue; 224 | } 225 | 226 | /* Apakah tidak memiliki attribute ignore--minify? */ 227 | if (!$el->hasAttribute("ignore--minify")) 228 | { 229 | continue; 230 | } 231 | 232 | $result[] = $el; 233 | } 234 | 235 | return $result; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/Middleware/MinifyCss.php: -------------------------------------------------------------------------------- 1 | getByTag("style") as $el) 16 | { 17 | $value = $this->replace($el->nodeValue); 18 | 19 | $el->nodeValue = ""; 20 | $el->appendChild(static::$dom->createTextNode($value)); 21 | } 22 | 23 | return static::$dom->saveHtml(); 24 | } 25 | 26 | protected function insertSemicolon($value) 27 | { 28 | return preg_replace([ 29 | // otomatis menambahkan semicolon atau titik koma (;) diakhir kode 30 | // kecuali yang diakhiri dengan braces ({}) ataupun dengan semicolon (;) 31 | '#^[A-Za-z\s\-]+:.+(?insertSemicolon($value); 45 | } 46 | 47 | return trim(preg_replace([ 48 | // Remove comment(s) 49 | '#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')|\/\*(?!\!)(?>.*?\*\/)|^\s*|\s*$#s', 50 | // Remove unused white-space(s) 51 | '#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\'|\/\*(?>.*?\*\/))|\s*+;\s*+(})\s*+|\s*+([*$~^|]?+=|[{};,>~]|\s(?![0-9\.])|!important\b)\s*+|([[(:])\s++|\s++([])])|\s++(:)\s*+(?!(?>[^{}"\']++|"(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')*+{)|^\s++|\s++\z|(\s)\s+#si', 52 | // Replace `0(cm|em|ex|in|mm|pc|pt|px|vh|vw|%)` with `0` 53 | '#(?<=[\s:])(0)(cm|em|ex|in|mm|pc|pt|px|vh|vw|%)#si', 54 | // Replace `:0 0 0 0` with `:0` 55 | '#:(0\s+0|0\s+0\s+0\s+0)(?=[;\}]|\!important)#i', 56 | // Replace `background-position:0` with `background-position:0 0` 57 | '#(background-position):0(?=[;\}])#si', 58 | // Replace `0.6` with `.6`, but only when preceded by `:`, `,`, `-` or a white-space 59 | '#(?<=[\s:,\-])0+\.(\d+)#s', 60 | // Minify string value 61 | '#(\/\*(?>.*?\*\/))|(?.*?\*\/))|(\burl\()([\'"])([^\s]+?)\3(\))#si', 63 | // Minify HEX color code 64 | '#(?<=[\s:,\-]\#)([a-f0-6]+)\1([a-f0-6]+)\2([a-f0-6]+)\3#i', 65 | // Replace `(border|outline):none` with `(border|outline):0` 66 | '#(?<=[\{;])(border|outline):none(?=[;\}\!])#', 67 | // Remove empty selector(s) 68 | '#(\/\*(?>.*?\*\/))|(^|[\{\}])(?:[^\s\{\}]+)\{\}#s' // 69 | ],[ 70 | '$1', 71 | '$1$2$3$4$5$6$7', 72 | '$1', 73 | ':0', 74 | '$1:0 0', 75 | '.$1', 76 | '$1$3', 77 | '$1$2$4$5', 78 | '$1$2$3', 79 | '$1:0', 80 | '$1$2' 81 | ], $value)); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Middleware/MinifyHtml.php: -------------------------------------------------------------------------------- 1 | \s*|(?)\n+(?=\<[^!])#s"; 8 | 9 | protected function apply() 10 | { 11 | // simpan css dan javascript yang belum di minify dan yang ada attribute ignored--minify nya 12 | $ignoredCss = $this->getByTagOnlyIgnored("style"); 13 | $ignoredJs = $this->getByTagOnlyIgnored("script"); 14 | 15 | if (static::$minifyCssHasBeenUsed && static::$minifyJavascriptHasBeenUsed) 16 | { 17 | $html = $this->replace(static::$dom->saveHtml()); 18 | 19 | if (empty($ignoredCss) || empty($ignoredCss)) 20 | { 21 | return $html; 22 | } 23 | 24 | $this->loadDom($html, true); 25 | } 26 | else 27 | { 28 | if (!static::$minifyCssHasBeenUsed) 29 | { 30 | $css = $this->getByTag("style"); 31 | } 32 | 33 | if (!static::$minifyJavascriptHasBeenUsed) 34 | { 35 | $js = $this->getByTag("script"); 36 | } 37 | 38 | $html = $this->replace(static::$dom->saveHtml()); 39 | 40 | $this->loadDom($html, true); 41 | 42 | if (isset($css)) { 43 | $this->append("getByTag", "style", $css); 44 | } 45 | if (isset($js)) { 46 | $this->append("getByTag", "script", $js); 47 | } 48 | } 49 | 50 | if (!empty($ignoredCss)) 51 | { 52 | $this->append("getByTagOnlyIgnored", "style", $ignoredCss); 53 | } 54 | if (!empty($ignoredJs)) 55 | { 56 | $this->append("getByTagOnlyIgnored", "script", $ignoredJs); 57 | } 58 | 59 | return trim(static::$dom->saveHtml()); 60 | } 61 | 62 | protected function append(string $function, string $tags, array $backup) 63 | { 64 | $index = 0; 65 | foreach ($this->{$function}($tags) as $el) 66 | { 67 | $el->nodeValue = ""; 68 | $el->appendChild(static::$dom->createTextNode($backup[$index]->nodeValue)); 69 | $index++; 70 | } 71 | } 72 | 73 | protected function removeComment($value) 74 | { 75 | return preg_replace(self::REGEX_REMOVE_COMMENT, "", $value); 76 | } 77 | 78 | protected function replace($value) 79 | { 80 | $value = trim(preg_replace([ 81 | // t = text 82 | // o = tag open 83 | // c = tag close 84 | // Keep important white-space(s) after self-closing HTML tag(s) 85 | '#<(img|input)(>| .*?>)#s', 86 | // Remove a line break and two or more white-space(s) between tag(s) 87 | '#()|(>)(?:\n*|\s{2,})(<)|^\s*|\s*$#s', 88 | '#()|(?)\s+(<\/.*?>)|(<[^\/]*?>)\s+(?!\<)#s', 89 | // t+c || o+t 90 | '#()|(<[^\/]*?>)\s+(<[^\/]*?>)|(<\/.*?>)\s+(<\/.*?>)#s', 91 | // o+o || c+c 92 | '#()|(<\/.*?>)\s+(\s)(?!\<)|(?)\s+(\s)(<[^\/]*?\/?>)|(<[^\/]*?\/?>)\s+(\s)(?!\<)#s', 93 | // c+t || t+o || o+t -- separated by long white-space(s) 94 | '#()|(<[^\/]*?>)\s+(<\/.*?>)#s', 95 | // empty tag 96 | '#<(img|input)(>| .*?>)<\/\1>#s', 97 | // reset previous fix 98 | '#( ) (?![<\s])#', 99 | // clean up ... 100 | '#(?<=\>)( )(?=\<)#', 101 | // --ibid 102 | '/\s+/', 103 | ],[ 104 | '<$1$2', 105 | '$1$2$3', 106 | '$1$2$3', 107 | '$1$2$3$4$5', 108 | '$1$2$3$4$5$6$7', 109 | '$1$2$3', 110 | '<$1$2', 111 | '$1 ', 112 | '$1', 113 | " ", 114 | ], $value)); 115 | 116 | $allowRemoveComments = (bool) config("laravel-html-minifier.remove_comments", true); 117 | 118 | return $allowRemoveComments == false 119 | ? $value 120 | : $this->removeComment($value); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Middleware/MinifyJavascript.php: -------------------------------------------------------------------------------- 1 | getByTag("script") as $el) 18 | { 19 | $value = $this->replace($el->nodeValue); 20 | 21 | /* Apakah fungsi obfuscate diaktifkan? */ 22 | if ($obfuscate) 23 | { 24 | $value = $this->obfuscate($value); 25 | } 26 | $el->nodeValue = ""; 27 | $el->appendChild(static::$dom->createTextNode($value)); 28 | } 29 | 30 | return static::$dom->saveHtml(); 31 | } 32 | 33 | protected function insertSemicolon($value) 34 | { 35 | /** 36 | * menghapus semua komentar 37 | */ 38 | $value = preg_replace('/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?, :, ?, . 62 | '#(?:({|\[|\(|,|;|=>|\:|\?|\.))$#', 63 | // temukan blank spasi 64 | '#^\s*$#', 65 | // temukan string pertama dan terakhir do, else 66 | '#^(do|else)$#' 67 | ]; 68 | 69 | $loop = 0; 70 | 71 | foreach ($code as $line) 72 | { 73 | $loop++; 74 | $insert = false; 75 | $shouldInsert = true; 76 | 77 | foreach ($patternRegex as $pattern) 78 | { 79 | // jika pattern tidak cocok artinya boleh ditambahkan semicolon 80 | $match = preg_match($pattern, trim($line)); 81 | $shouldInsert = $shouldInsert && (bool) !$match; 82 | } 83 | 84 | if ($shouldInsert) 85 | { 86 | $i = $loop; 87 | 88 | while (true) 89 | { 90 | if ($i >= count($code)) 91 | { 92 | $insert = true; 93 | break; 94 | } 95 | 96 | $c = trim($code[$i]); 97 | $i++; 98 | 99 | if (!$c) 100 | { 101 | continue; 102 | } 103 | 104 | $insert = true; 105 | $regex = ['#^(\?|\:|,|\.|{|}|\)|\])#']; 106 | 107 | foreach ($regex as $r) 108 | { 109 | $insert = $insert && (bool) !preg_match($r, $c); 110 | } 111 | 112 | if ($insert) 113 | { 114 | if (preg_match("#(?:\\})$#", trim($line)) && preg_match("#^(else|elseif|else\s*if|catch)#", $c)) 115 | { 116 | $insert = false; 117 | } 118 | } 119 | 120 | break; 121 | } 122 | } 123 | 124 | if ($insert) 125 | { 126 | $result[] = sprintf("%s;", $line); 127 | } 128 | else 129 | { 130 | $result[] = $line; 131 | } 132 | 133 | } 134 | 135 | return join("\n", $result); 136 | } 137 | 138 | protected function replace($value) 139 | { 140 | if (static::$allowInsertSemicolon) 141 | { 142 | $value = $this->insertSemicolon($value); 143 | } 144 | 145 | return trim(preg_replace([ 146 | '#\s*("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')\s*|\s*\/\*(?!\!|@cc_on)(?>[\s\S]*?\*\/)\s*|\s*(?.*?\*\/)|\/(?!\/)[^\n\r]*?\/(?=[\s.,;]|[gimuy]|$))|\s*([!%&*\(\)\-=+\[\]\{\}|;:,.<>?\/])\s*#s', 149 | // Remove the last semicolon 150 | '#;+\}#', 151 | // Minify object attribute(s) except JSON attribute(s). From `{'foo':'bar'}` to `{foo:'bar'}` 152 | '#([\{,])([\'])(\d+|[a-z_][a-z0-9_]*)\2(?=\:)#i', 153 | // --ibid. From `foo['bar']` to `foo.bar` 154 | '#([a-z0-9_\)\]])\[([\'"])([a-z_][a-z0-9_]*)\2\]#i' 155 | ],[ 156 | '$1', 157 | '$1$2', 158 | '}', 159 | '$1$3', 160 | '$1.$3' 161 | ], $value)); 162 | } 163 | 164 | protected function obfuscate($value) 165 | { 166 | $ords = []; 167 | 168 | for ($i = 0; $i < strlen($value); $i++) 169 | { 170 | $ords[] = ord($value[$i]); 171 | } 172 | 173 | $template = sprintf(" 174 | eval(((_, __, ___, ____, _____, ______, _______) => { 175 | ______[___](x => _______[__](String[____](x))); 176 | return _______[_](_____) 177 | })('join', 'push', 'forEach', 'fromCharCode', '', %s, [])) 178 | 179 | ", json_encode($ords)); 180 | 181 | return $this->replace($template); 182 | } 183 | } 184 | --------------------------------------------------------------------------------