Changeset 3672525
- Timestamp:
- 08/30/2026 12:44:07 PM (8 weeks ago)
- Location:
- masterspeed-cache/trunk
- Files:
-
- 7 edited
-
languages/masterspeed-cache-en_US.po (modified) (1 diff)
-
languages/masterspeed-cache.pot (modified) (1 diff)
-
masterspeed-cache.php (modified) (1 diff)
-
modules/class-wpsc-image-optimizer.php (modified) (5 diffs)
-
modules/class-wpsc-optimizer.php (modified) (12 diffs)
-
readme.txt (modified) (1 diff)
-
server/class-wpsc-htaccess.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
masterspeed-cache/trunk/languages/masterspeed-cache-en_US.po
r3670502 r3672525 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: MasterSpeed Cache 1. 1\n"5 "Project-Id-Version: MasterSpeed Cache 1.2\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/masterspeed-cache\n" 7 7 "POT-Creation-Date: 2026-08-28T00:00:00+00:00\n" -
masterspeed-cache/trunk/languages/masterspeed-cache.pot
r3670502 r3672525 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: MasterSpeed Cache 1. 1\n"5 "Project-Id-Version: MasterSpeed Cache 1.2\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/masterspeed-cache\n" 7 7 "POT-Creation-Date: 2026-08-28T00:00:00+00:00\n" -
masterspeed-cache/trunk/masterspeed-cache.php
r3671949 r3672525 24 24 // KONSTANTA PLUGIN 25 25 // ============================================================================ 26 define( 'NAZEMIWPSC_VERSION', '1. 1' );26 define( 'NAZEMIWPSC_VERSION', '1.2' ); 27 27 define( 'NAZEMIWPSC_PLUGIN_FILE', __FILE__ ); 28 28 define( 'NAZEMIWPSC_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); -
masterspeed-cache/trunk/modules/class-wpsc-image-optimizer.php
r3670502 r3672525 1219 1219 $placeholders = implode( ',', array_fill( 0, count( self::SUPPORTED_MIMES ), '%s' ) ); 1220 1220 1221 // phpcs: ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $placeholders murni string '%s' berulang sebanyak count(SUPPORTED_MIMES) (konstanta tetap di kelas ini, bukan input pengguna); semua nilai sesungguhnya tetap lewat larik kedua $wpdb->prepare() di bawah.1221 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $placeholders murni string '%s' berulang sebanyak count(SUPPORTED_MIMES) (konstanta tetap di kelas ini, bukan input pengguna); semua nilai sesungguhnya tetap lewat larik kedua $wpdb->prepare() di bawah. Dipakai gaya blok (disable/enable), bukan ignore satu baris, karena statement-nya berjalan lintas beberapa baris (baris query sesungguhnya beberapa baris di bawah baris ini) dan ignore satu baris tidak menjangkau sejauh itu. 1222 1222 $ids = $wpdb->get_col( 1223 1223 $wpdb->prepare( … … 1233 1233 ) 1234 1234 ); 1235 // phpcs:enable 1235 1236 1236 1237 return array_map( 'absint', $ids ); … … 1245 1246 global $wpdb; 1246 1247 $placeholders = implode( ',', array_fill( 0, count( self::SUPPORTED_MIMES ), '%s' ) ); 1247 // phpcs: ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $placeholders murni string '%s' berulang sebanyak count(SUPPORTED_MIMES) (konstanta tetap di kelas ini, bukan input pengguna); semua nilai sesungguhnya tetap lewat larik kedua $wpdb->prepare() di bawah.1248 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $placeholders murni string '%s' berulang sebanyak count(SUPPORTED_MIMES) (konstanta tetap di kelas ini, bukan input pengguna); semua nilai sesungguhnya tetap lewat larik kedua $wpdb->prepare() di bawah. Dipakai gaya blok (disable/enable), bukan ignore satu baris, karena statement-nya berjalan lintas beberapa baris. 1248 1249 return (int) $wpdb->get_var( 1249 1250 $wpdb->prepare( … … 1252 1253 ) 1253 1254 ); 1255 // phpcs:enable 1254 1256 } 1255 1257 … … 1848 1850 global $wpdb; 1849 1851 $placeholders = implode( ',', array_fill( 0, count( $to_query ), '%s' ) ); 1850 // phpcs: ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $placeholders murni string '%s' berulang sebanyak count($to_query); semua nilai sesungguhnya (termasuk isi $to_query) lewat larik kedua $wpdb->prepare() di bawah, bukan disisipkan mentah ke teks query.1852 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $placeholders murni string '%s' berulang sebanyak count($to_query); semua nilai sesungguhnya (termasuk isi $to_query) lewat larik kedua $wpdb->prepare() di bawah, bukan disisipkan mentah ke teks query. Dipakai gaya blok (disable/enable), bukan ignore satu baris, karena baris query sesungguhnya ada di baris berikutnya (statement lintas beberapa baris). 1851 1853 $query = $wpdb->prepare( 1852 1854 "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value IN ($placeholders)", 1853 1855 array_merge( array( '_nazemiwpsc_img_original_url' ), $to_query ) 1854 1856 ); 1857 // phpcs:enable 1855 1858 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- $query dibangun via $wpdb->prepare() tepat di atas (bukan konkatenasi manual); query kecil & tertarget (IN pada URL spesifik), bukan full-table scan. 1856 1859 $rows = $wpdb->get_results( $query ); -
masterspeed-cache/trunk/modules/class-wpsc-optimizer.php
r3670502 r3672525 63 63 || ! empty( $this->options['js_defer'] ) 64 64 || ! empty( $this->options['google_fonts_optimizer'] ) 65 || ! empty( $this->options['combine_assets'] )66 65 || ! empty( $this->options['asset_preload'] ) 67 66 || ! empty( $this->options['cdn_photon'] ) … … 97 96 // jalankan ini kalau togglenya aktif, tanpa perlu cek toggle lain. 98 97 add_action( 'wp_head', array( $this, 'inject_google_fonts_preconnect' ), 1 ); 98 } 99 100 if ( ! empty( $this->options['combine_assets'] ) ) { 101 // Lihat blok komentar panjang di atas combine_enqueued_styles() 102 // untuk penjelasan lengkap. Mekanisme ini SAMA SEKALI TIDAK 103 // butuh output buffering (beda dari kebanyakan fitur lain di 104 // kelas ini) — makanya 'combine_assets' TIDAK ada di 105 // $needs_buffer di atas. Prioritas SENGAJA lebih kecil dari 106 // callback cetak bawaan WordPress supaya kita sempat mendaftarkan 107 // bundle (wp_register_style()/wp_enqueue_style() ASLI) dan 108 // men-dequeue file sumbernya SEBELUM WordPress mencetak antrean 109 // $wp_styles/$wp_scripts miliknya sendiri: 110 // wp_print_styles() @ wp_head prioritas 8 → kita 7 111 // wp_print_head_scripts() @ wp_head prioritas 9 → kita 8 112 // wp_print_footer_scripts() @ wp_footer prioritas 20 → kita 19 113 add_action( 'wp_head', array( $this, 'combine_enqueued_styles' ), 7 ); 114 add_action( 'wp_head', array( $this, 'combine_enqueued_head_scripts' ), 8 ); 115 add_action( 'wp_footer', array( $this, 'combine_enqueued_footer_scripts' ), 19 ); 99 116 } 100 117 … … 214 231 } 215 232 216 if ( ! empty( $this->options['combine_assets'] ) ) { 217 $html = $this->combine_css( $html ); 218 $html = $this->combine_js( $html ); 219 } 233 // combine_css()/combine_js() TIDAK dipanggil di sini — pemindaian & 234 // penyuntikan tag di HTML jadi (Opsi A, sudah dihapus total dari 235 // plugin ini) digantikan mekanisme lewat hook wp_head/wp_footer 236 // (lihat combine_enqueued_styles() dkk., didaftarkan di init()) 237 // yang berjalan SEBELUM HTML ini bahkan tercetak, lewat 238 // wp_register_style()/wp_enqueue_style() asli. 220 239 221 240 if ( ! empty( $this->options['asset_preload'] ) ) { … … 1152 1171 1153 1172 // ========================================================================= 1154 // TRANSFORMASI: GABUNGKAN CSS & JS(mengurangi jumlah permintaan HTTP)1173 // GABUNGKAN CSS/JS VIA SIKLUS ENQUEUE ASLI (mengurangi jumlah permintaan HTTP) 1155 1174 // ========================================================================= 1156 1175 // 1157 // Fitur ini menjawab saran umum di tes kecepatan halaman ("Kurangi jumlah 1158 // permintaan HTTP") dengan menggabungkan beberapa file CSS/JS lokal yang 1159 // BERURUTAN di HTML menjadi satu file, sehingga browser hanya perlu satu 1160 // koneksi/request untuk semuanya alih-alih satu per file. 1176 // [RIWAYAT] Dua percobaan sebelumnya SUDAH DIHAPUS TOTAL dari plugin ini 1177 // (bukan sekadar tidak dipanggil lagi): 1178 // 1. Tag <link>/<script> dibangun manual lewat konkatenasi string PHP 1179 // di HTML yang sudah jadi — di-flag Plugin Check WordPress.org 1180 // sebagai NonEnqueuedStylesheet/NonEnqueuedScript, karena alat 1181 // pemeriksa mendeteksi lewat pencarian string "<link"/"<script" di 1182 // source code, terlepas apakah string itu lewat filter resmi 1183 // 'style_loader_tag'/'script_loader_tag' atau tidak. 1184 // 2. Perbaikan atas #1: tetap memindai HTML jadi, tapi tag dicetak 1185 // lewat instance WP_Styles/WP_Scripts TERISOLASI (bukan 1186 // $wp_styles/$wp_scripts global situs) + do_item() asli WordPress 1187 // core. Ini memang menghilangkan masalah Plugin Check, TAPI 1188 // terbukti (diuji langsung di situs nyata) tetap membuat sebagian 1189 // halaman DINAMIS gagal dimuat — kemungkinan karena instantiasi 1190 // WP_Styles/WP_Scripts baru memicu ulang action 'wp_default_styles'/ 1191 // 'wp_default_scripts' di tengah request yang sedang berjalan, 1192 // berinteraksi buruk dengan plugin/tema lain yang juga nge-hook 1193 // action itu (mis. mendaftarkan style/script dengan asumsi hook 1194 // itu HANYA tertembak sekali per request, saat bootstrap normal). 1161 1195 // 1162 // PRINSIP KEAMANAN (supaya tampilan/fungsi halaman TIDAK PERNAH berubah): 1163 // 1. Hanya file SATU HOST dengan situs ini yang digabung. File dari domain 1164 // lain (mis. fonts.googleapis.com) dibiarkan apa adanya — domain lain 1165 // memang selalu butuh koneksi terpisah, tidak ada untungnya dipaksakan, 1166 // dan API pihak ketiga umumnya tidak mengizinkan file-nya di-proxy. 1167 // 2. Hanya tag yang BERURUTAN (tidak ada elemen lain di antaranya) yang 1168 // digabung dalam satu "run" — urutan pemuatan CSS/JS TIDAK PERNAH 1169 // berubah, krusial karena cascade CSS dan dependensi JS bergantung 1170 // pada urutan asli. 1171 // 3. CSS: dilewati kalau media berbeda (mis. "print") atau dibungkus 1172 // conditional comment IE (<!--[if ...]-->) — keduanya sengaja spesifik 1173 // dan tidak boleh disatukan dengan CSS lain. 1174 // 4. JS: HANYA script polos (tanpa async/defer/module/nomodule, dan tanpa 1175 // isi inline apa pun di antara tag buka-tutupnya) yang digabung. 1176 // Script async/defer/module sengaja tidak disentuh karena 1177 // menggabungkannya bisa mengubah urutan/waktu eksekusi. 1178 // 5. url() relatif di dalam CSS (gambar/font) ditulis ulang jadi URL 1179 // absolut berdasarkan lokasi file ASLI-nya — tetap valid walau file 1180 // gabungan disimpan di folder cache yang berbeda. 1181 // 6. Bundle di-cache di disk dengan nama berbasis hash (path + waktu ubah 1182 // tiap file sumber) — kalau salah satu file berubah, key-nya otomatis 1183 // berubah dan bundle baru dibuat; bundle lama jadi tak terpakai 1184 // (dibersihkan lewat aksi "Hapus Cache"). 1185 // 1186 // Karena transformasi ini berjalan DI DALAM buffer Optimizer yang (sejak 1187 // perbaikan urutan buffer) diproses SEBELUM CacheBuilder menyimpan ke 1188 // disk, hasil gabungannya ikut tersimpan permanen di index.html — kunjungan 1189 // berikutnya yang disajikan langsung dari cache statis otomatis mendapat 1190 // HTML yang sudah lebih sedikit request-nya, tanpa biaya proses tambahan. 1191 // ========================================================================= 1192 1193 /** 1194 * Gabungkan <link rel="stylesheet"> lokal yang berurutan di dalam <head>. 1195 * 1196 * @param string $html HTML asli. 1197 * @return string HTML dengan CSS yang bisa digabung sudah disatukan. 1198 */ 1199 /** 1200 * Render tag <link> untuk satu bundel CSS gabungan. 1201 * 1202 * [BUG FIX #1] Sebelumnya tag <link> dibangun manual lewat konkatenasi 1203 * string PHP — walau URL-nya sudah di-esc_url() dengan benar, ini 1204 * tidak melalui API enqueue bawaan WordPress yang diwajibkan pedoman 1205 * WordPress.org. 1206 * 1207 * [BUG FIX #2 — regresi dari perbaikan #1] Percobaan pertama memakai 1208 * wp_register_style()+wp_enqueue_style()+WP_Styles::do_item() persis 1209 * seperti cara WordPress mencetak style yang di-enqueue normal. Ini 1210 * TERNYATA menyebabkan halaman dinamis (yang belum ada di cache HTML 1211 * statis, sehingga combine_css() benar-benar berjalan live) gagal 1212 * tampil/tidak selesai dimuat. Penyebabnya: do_item() menjalankan 1213 * seluruh mesin internal WP_Styles/WP_Scripts (resolusi loading 1214 * strategy async/defer sejak WP 6.3, pengecekan dependents, dsb.) yang 1215 * DIRANCANG untuk dipanggil pada siklus normal wp_head/wp_footer — 1216 * memanggilnya di titik yang sangat tidak lazim ini (di tengah 1217 * pemrosesan buffer, JAUH SETELAH wp_head/wp_footer asli selesai) 1218 * berisiko konflik dengan state $wp_styles/$wp_scripts yang sudah 1219 * "selesai" dan bisa membuat proses macet/fatal di sebagian 1220 * konfigurasi server (khususnya kalau ada plugin/tema lain yang juga 1221 * memfilter atau membaca isi antrean itu). 1222 * 1223 * Sekarang: TIDAK menyentuh antrean $wp_styles/$wp_scripts global 1224 * sama sekali (tidak ada register/enqueue/dequeue/deregister). Tag 1225 * dibangun langsung, lalu dilewatkan ke filter resmi WordPress 1226 * 'style_loader_tag' — filter YANG SAMA yang dijalankan 1227 * WP_Styles::do_item() secara internal untuk setiap style yang 1228 * dicetak. Ini tetap "menggunakan mekanisme enqueue resmi WordPress" 1229 * (tema/plugin lain yang menambah atribut lewat filter ini tetap 1230 * berlaku di sini) tanpa risiko mengganggu siklus enqueue yang 1231 * sesungguhnya sudah selesai berjalan. 1232 * 1233 * @param string $handle Handle unik untuk bundel ini (dipakai sebagai id filter, bukan didaftarkan ke antrean). 1234 * @param string $bundle_url URL bundel. 1235 * @param string $media Atribut media (default 'all'). 1236 * @return string Markup <link> lengkap. 1237 */ 1238 private function render_enqueued_style_tag( $handle, $bundle_url, $media = 'all' ) { 1239 // [PENTING] Guard jaga-jaga (defense-in-depth) — init() sudah 1240 // return lebih awal kalau is_admin() (jadi start_optimizer_buffer() 1241 // TIDAK PERNAH ter-hook di wp-admin), tapi tetap dipasang di sini 1242 // supaya kalau suatu saat ada refactor yang keliru memanggil 1243 // fungsi ini dari konteks admin, tidak ada efek samping sama 1244 // sekali — bundel gabungan ini murni untuk output HTML front-end. 1245 if ( is_admin() ) { 1246 return ''; 1247 } 1248 $media = $media ? $media : 'all'; 1249 $href = esc_url( $bundle_url ); 1250 $tag = "<link rel='stylesheet' id='" . esc_attr( $handle ) . "-css' href='" . $href . "' media='" . esc_attr( $media ) . "' type='text/css' />\n"; 1251 /** Filter resmi WordPress — sama seperti yang dipakai WP_Styles::do_item(). */ 1252 return (string) apply_filters( 'style_loader_tag', $tag, $handle, $href, $media ); 1253 } 1254 1255 /** 1256 * Render tag <script> untuk satu bundel JS gabungan. Lihat catatan 1257 * lengkap di render_enqueued_style_tag() — pola & alasan yang sama 1258 * persis. Memakai wp_get_script_tag() (fungsi resmi WordPress sejak 1259 * 5.7 khusus untuk membangun tag <script> dengan atribut yang benar, 1260 * termasuk menjalankan filter 'wp_script_attributes') dan filter 1261 * 'script_loader_tag' — TANPA register/enqueue ke $wp_scripts global. 1262 * 1263 * @param string $handle Handle unik untuk bundel ini. 1264 * @param string $bundle_url URL bundel. 1265 * @return string Markup <script> lengkap. 1266 */ 1267 private function render_enqueued_script_tag( $handle, $bundle_url ) { 1268 // [PENTING] Sama seperti render_enqueued_style_tag() — guard 1269 // jaga-jaga, lihat catatan lengkap di sana. 1270 if ( is_admin() ) { 1271 return ''; 1272 } 1273 $src = esc_url( $bundle_url ); 1274 if ( function_exists( 'wp_get_script_tag' ) ) { 1275 $tag = wp_get_script_tag( 1276 array( 1277 'src' => $src, 1278 'id' => $handle . '-js', 1279 ) 1280 ); 1281 } else { 1282 // Fallback untuk WP < 5.7 (wp_get_script_tag() belum ada). 1283 $tag = "<script src='" . $src . "' id='" . esc_attr( $handle ) . "-js'></script>\n"; 1284 } 1285 /** Filter resmi WordPress — sama seperti yang dipakai WP_Scripts::do_item(). */ 1286 return (string) apply_filters( 'script_loader_tag', $tag, $handle, $src ); 1287 } 1288 1289 1290 private function combine_css( $html ) { 1291 if ( false === stripos( $html, 'stylesheet' ) ) { 1292 return $html; 1293 } 1294 if ( ! preg_match( '/<head[^>]*>.*?<\/head>/is', $html, $head_match ) ) { 1295 return $html; 1296 } 1297 $head = $head_match[0]; 1298 1299 if ( ! preg_match_all( '/<link\s+[^>]*rel=[\'"]stylesheet[\'"][^>]*>/i', $head, $matches, PREG_OFFSET_CAPTURE ) ) { 1300 return $html; 1301 } 1302 $tags = $matches[0]; 1303 if ( count( $tags ) < 2 ) { 1304 return $html; 1305 } 1306 1307 $conditional_ranges = $this->find_conditional_comment_ranges( $head ); 1308 1196 // SOLUSI YANG DIPAKAI SEKARANG menghindari KEDUA masalah itu sekaligus 1197 // dengan pindah TITIK WAKTU eksekusi: bukan lagi "setelah semua 1198 // tercetak" (memindai HTML jadi, baik manual maupun lewat instance 1199 // terisolasi), tapi "SEBELUM WordPress mencetak antrean $wp_styles/ 1200 // $wp_scripts miliknya sendiri" — lewat hook wp_head/wp_footer dengan 1201 // prioritas LEBIH RENDAH dari callback cetak bawaan WordPress: 1202 // - wp_print_styles() @ wp_head prioritas 8 → kita di 7 1203 // - wp_print_head_scripts() @ wp_head prioritas 9 → kita di 8 1204 // - wp_print_footer_scripts()@ wp_footer prioritas 20 → kita di 19 1205 // Di titik ini antrean BELUM dicetak sama sekali (bukan "belum 1206 // selesai", tapi benar-benar belum mulai) — kita baca urutan cetak yang 1207 // AKAN terjadi (lewat WP_Dependencies::all_deps(), fungsi resmi yang 1208 // sama dipakai do_items() secara internal), gabungkan file yang layak, 1209 // lalu daftarkan hasilnya lewat wp_register_style()/wp_enqueue_style() 1210 // (atau versi script-nya) — FUNGSI ENQUEUE ASLI, bukan tag manual, dan 1211 // TIDAK PERNAH menyentuh instance WP_Styles/WP_Scripts baru — dan 1212 // wp_dequeue_style()/wp_dequeue_script() untuk file asli yang sudah 1213 // digabung. WordPress SENDIRI yang mencetak semuanya sesudah ini, 1214 // lewat do_items() aslinya, tanpa kita pernah membangun satu pun tag 1215 // <link>/<script> secara manual maupun memicu ulang action bootstrap 1216 // apa pun. 1217 1218 /** 1219 * Hook 'wp_head' prioritas 7 — gabungkan CSS yang di-enqueue, sebelum 1220 * WordPress mencetaknya sendiri (wp_print_styles() @ prioritas 8). 1221 */ 1222 public function combine_enqueued_styles() { 1223 if ( empty( $this->options['combine_assets'] ) ) { 1224 return; 1225 } 1226 global $wp_styles; 1227 if ( ! ( $wp_styles instanceof WP_Styles ) || empty( $wp_styles->queue ) ) { 1228 return; 1229 } 1230 1231 // Urutan cetak SEBENARNYA (mengikuti dependensi antar-handle) — 1232 // fungsi resmi yang sama dipakai WP_Styles::do_items() secara 1233 // internal sebelum mencetak. 1234 $wp_styles->all_deps( $wp_styles->queue ); 1235 $ordered = $wp_styles->to_do; 1236 if ( count( $ordered ) < 2 ) { 1237 return; 1238 } 1239 1240 // [DIOPTIMALKAN] Sebelumnya, handle yang jadi dependensi handle LAIN 1241 // (di mana pun posisinya) dikecualikan TOTAL dari penggabungan — 1242 // terlalu konservatif. Sebenarnya TIDAK PERLU: kita tidak pernah 1243 // mengubah URUTAN cetak, hanya menyatukan beberapa handle 1244 // BERURUTAN jadi satu bundel yang menempati posisi yang SAMA di 1245 // urutan cetak (posisi handle PERTAMA dalam rangkaian itu). Baik 1246 // dependent-nya ikut tergabung dalam bundel yang sama (tetap 1247 // berurutan benar di dalam isi bundel) MAUPUN dependent-nya 1248 // berada di rangkaian/posisi LAIN setelahnya (tetap tercetak 1249 // setelah bundel ini, sama seperti sebelum digabung) — urutan 1250 // "dependensi harus tercetak sebelum dependent-nya" otomatis 1251 // tetap terjaga tanpa perlu pengecualian dependensi sama sekali. 1252 // Yang membatasi penggabungan sekarang murni kelayakan PER-HANDLE 1253 // (lihat get_combinable_style_info(): media cocok, bukan 1254 // conditional comment, file lokal & terbaca) dan keharusan 1255 // BERURUTAN di urutan cetak (lihat pemutus run di bawah). 1309 1256 $runs = array(); 1310 1257 $current_run = array(); 1311 $last_end = 0; 1312 1313 foreach ( $tags as $t ) { 1314 $tag = $t[0]; 1315 $start = $t[1]; 1316 $end = $start + strlen( $tag ); 1317 1318 if ( ! empty( $current_run ) && '' !== trim( substr( $head, $last_end, $start - $last_end ) ) ) { 1319 if ( count( $current_run ) >= 2 ) { 1320 $runs[] = $current_run; 1321 } 1322 $current_run = array(); 1323 } 1324 1325 $info = $this->parse_stylesheet_tag( $tag, $start, $end, $conditional_ranges ); 1258 foreach ( $ordered as $handle ) { 1259 $info = $this->get_combinable_style_info( $handle, $wp_styles ); 1326 1260 if ( false === $info ) { 1327 1261 if ( count( $current_run ) >= 2 ) { … … 1329 1263 } 1330 1264 $current_run = array(); 1331 } else { 1332 $current_run[] = $info; 1333 } 1334 1335 $last_end = $end; 1265 continue; 1266 } 1267 $current_run[] = $info; 1336 1268 } 1337 1269 if ( count( $current_run ) >= 2 ) { 1338 1270 $runs[] = $current_run; 1339 1271 } 1340 1341 1272 if ( empty( $runs ) ) { 1342 return $html; 1343 } 1344 1345 // Proses dari run TERAKHIR ke PERTAMA supaya offset yang belum 1346 // diproses tidak bergeser akibat penggantian pada run sebelumnya. 1347 foreach ( array_reverse( $runs ) as $run ) { 1273 return; 1274 } 1275 1276 foreach ( $runs as $run ) { 1348 1277 $bundle_url = $this->build_asset_bundle( 'css', $run ); 1349 1278 if ( false === $bundle_url ) { 1350 1279 continue; 1351 1280 } 1352 $first = $run[0]; 1353 $last = $run[ count( $run ) - 1 ]; 1354 $media_attr = ( '' !== $first['media'] && 'all' !== $first['media'] ) ? $first['media'] : 'all'; 1355 // [BUG FIX] Handle unik per-bundel (bukan cuma satu handle 1356 // tetap) — halaman bisa punya lebih dari satu run gabungan 1357 // (mis. dipisah oleh <script> conditional di antaranya), dan 1358 // setiap bundel punya URL berbeda. 1359 $handle = 'nazemiwpsc-combined-css-' . substr( md5( $bundle_url ), 0, 12 ); 1360 $replacement = $this->render_enqueued_style_tag( $handle, $bundle_url, $media_attr ); 1361 if ( '' === $replacement ) { 1362 continue; // WP_Styles gagal mencetak (seharusnya tidak pernah terjadi) — biarkan tag asli, jangan hapus aset. 1363 } 1364 1365 $head = substr_replace( $head, $replacement, $first['start'], $last['end'] - $first['start'] ); 1366 } 1367 1368 return substr_replace( $html, $head, strpos( $html, $head_match[0] ), strlen( $head_match[0] ) ); 1369 } 1370 1371 /** 1372 * Analisis satu tag <link rel="stylesheet">. Mengembalikan array info 1373 * (path lokal, mtime, media, posisi) jika layak digabung, atau false. 1374 */ 1375 private function parse_stylesheet_tag( $tag, $start, $end, $conditional_ranges ) { 1376 foreach ( $conditional_ranges as $range ) { 1377 if ( $start >= $range[0] && $start < $range[1] ) { 1378 return false; // Di dalam conditional comment IE — jangan disentuh. 1379 } 1380 } 1381 1382 if ( ! preg_match( '/href=[\'"]([^\'"]+)[\'"]/i', $tag, $m ) ) { 1383 return false; 1384 } 1385 $href = html_entity_decode( $m[1], ENT_QUOTES ); 1386 1387 $media = ''; 1388 if ( preg_match( '/media=[\'"]([^\'"]+)[\'"]/i', $tag, $mm ) ) { 1389 $media = strtolower( trim( $mm[1] ) ); 1390 } 1281 $first = $run[0]; 1282 $media = ( '' !== $first['media'] && 'all' !== $first['media'] ) ? $first['media'] : 'all'; 1283 $handle = 'nazemiwpsc-combined-css-' . substr( md5( $bundle_url ), 0, 12 ); 1284 // Versi EKSPLISIT wajib (Plugin Check: NoExplicitVersion) — pakai 1285 // nama file bundel itu sendiri (16 karakter hash md5(path|mtime) 1286 // tiap sumber, lihat build_asset_bundle()), bukan false/null yang 1287 // membuat WordPress diam-diam pakai versi WP core sebagai fallback. 1288 // Otomatis berubah kalau isi bundel berubah — cache-busting browser 1289 // tetap benar tanpa perlu query string ?ver= terpisah. 1290 $ver = pathinfo( $bundle_url, PATHINFO_FILENAME ); 1291 1292 // Fungsi ENQUEUE ASLI WordPress — bukan tag manual. 1293 wp_register_style( $handle, $bundle_url, array(), $ver, $media ); 1294 wp_enqueue_style( $handle ); 1295 1296 foreach ( $run as $item ) { 1297 wp_dequeue_style( $item['handle'] ); 1298 } 1299 } 1300 } 1301 1302 /** 1303 * Analisis satu handle CSS terdaftar. Mengembalikan array info (handle, 1304 * href, media, path, mtime) jika layak digabung, atau false. 1305 */ 1306 private function get_combinable_style_info( $handle, $wp_styles ) { 1307 if ( isset( $wp_styles->done[ $handle ] ) ) { 1308 return false; // Sudah tercetak — jaga-jaga, seharusnya tidak pernah terjadi di titik ini. 1309 } 1310 $dep = isset( $wp_styles->registered[ $handle ] ) ? $wp_styles->registered[ $handle ] : null; 1311 if ( ! $dep || empty( $dep->src ) ) { 1312 return false; // Tanpa src (mis. hanya pembawa style inline) — tidak ada file untuk digabung. 1313 } 1314 if ( ! empty( $dep->extra['conditional'] ) ) { 1315 return false; // Dibungkus <!--[if ...]--> (wp_style_add_data 'conditional') — jangan sentuh. 1316 } 1317 if ( ! empty( $dep->extra['alt'] ) ) { 1318 return false; // rel="alternate stylesheet". 1319 } 1320 1321 $media = isset( $dep->args ) ? strtolower( trim( (string) $dep->args ) ) : 'all'; 1391 1322 if ( '' !== $media && 'all' !== $media && 'screen' !== $media ) { 1392 1323 return false; // Media selain all/screen (mis. print) — jangan gabung. 1393 1324 } 1394 1325 1395 $path = $this->url_to_local_path( $href ); 1326 $src = $this->resolve_dep_src( $dep->src, $wp_styles->base_url ); 1327 $path = $this->url_to_local_path( $src ); 1396 1328 if ( false === $path || ! is_readable( $path ) ) { 1397 return false; // Bukan file lokal, atau tidak terbaca. 1398 } 1399 1329 return false; 1330 } 1400 1331 $mtime = filemtime( $path ); 1401 1332 if ( false === $mtime ) { … … 1404 1335 1405 1336 return array( 1406 'tag' => $tag, 1407 'href' => $href, 1408 'media' => $media, 1409 'path' => $path, 1410 'mtime' => $mtime, 1411 'start' => $start, 1412 'end' => $end, 1337 'handle' => $handle, 1338 'href' => $src, 1339 'media' => $media, 1340 'path' => $path, 1341 'mtime' => $mtime, 1413 1342 ); 1414 1343 } 1415 1344 1416 1345 /** 1417 * Gabungkan <script src="..."> POLOS (tanpa async/defer/module) yang 1418 * berurutan — dipisah antara yang di <head> dan yang di <body>/footer 1419 * supaya posisi asli (head vs footer) tidak ikut berubah. 1420 * 1421 * @param string $html HTML asli. 1422 * @return string HTML dengan JS yang bisa digabung sudah disatukan. 1423 */ 1424 private function combine_js( $html ) { 1425 if ( false === stripos( $html, '<script' ) ) { 1426 return $html; 1427 } 1428 1429 $pattern = '/<script\s+[^>]*\bsrc=[\'"][^\'"]+[\'"][^>]*>\s*<\/script>/i'; 1430 if ( ! preg_match_all( $pattern, $html, $matches, PREG_OFFSET_CAPTURE ) ) { 1431 return $html; 1432 } 1433 $tags = $matches[0]; 1434 if ( count( $tags ) < 2 ) { 1435 return $html; 1436 } 1437 1346 * Hook 'wp_head' prioritas 8 — gabungkan JS grup HEAD (in_footer=false) 1347 * yang di-enqueue, sebelum WordPress mencetaknya sendiri 1348 * (wp_print_head_scripts() @ prioritas 9). 1349 */ 1350 public function combine_enqueued_head_scripts() { 1351 $this->combine_enqueued_scripts_by_group( 0 ); 1352 } 1353 1354 /** 1355 * Hook 'wp_footer' prioritas 19 — gabungkan JS grup FOOTER (in_footer=true) 1356 * yang di-enqueue, sebelum WordPress mencetaknya sendiri 1357 * (wp_print_footer_scripts() @ prioritas 20). 1358 */ 1359 public function combine_enqueued_footer_scripts() { 1360 $this->combine_enqueued_scripts_by_group( 1 ); 1361 } 1362 1363 /** 1364 * Logika bersama untuk kedua grup (head/footer) JS — lihat dua fungsi 1365 * publik di atas. $group: 0 = head, 1 = footer (sama seperti nilai 1366 * $dep->extra['group'] milik WP_Scripts, diisi WordPress sendiri 1367 * berdasarkan parameter $in_footer saat wp_enqueue_script() dipanggil). 1368 * 1369 * Handle yang BUKAN milik $group yang sedang diproses SENGAJA dianggap 1370 * "pemutus run" (bukan diabaikan begitu saja) — supaya file head tidak 1371 * pernah ikut tergabung ke bundle footer, atau sebaliknya; posisi 1372 * head-vs-footer aslinya harus tetap terjaga. 1373 * 1374 * @param int $group 0 (head) atau 1 (footer). 1375 */ 1376 private function combine_enqueued_scripts_by_group( $group ) { 1377 if ( empty( $this->options['combine_assets'] ) ) { 1378 return; 1379 } 1380 global $wp_scripts; 1381 if ( ! ( $wp_scripts instanceof WP_Scripts ) || empty( $wp_scripts->queue ) ) { 1382 return; 1383 } 1384 1385 $wp_scripts->all_deps( $wp_scripts->queue ); 1386 $ordered = $wp_scripts->to_do; 1387 if ( count( $ordered ) < 2 ) { 1388 return; 1389 } 1390 1391 // [DIOPTIMALKAN] Lihat catatan lengkap di combine_enqueued_styles() 1392 // — pengecualian dependensi TIDAK DIPERLUKAN untuk keamanan 1393 // (kita tidak pernah mengubah urutan cetak, hanya menyatukan 1394 // handle BERURUTAN jadi satu bundel di posisi yang sama), jadi 1395 // dihapus di sini juga supaya lebih banyak JS bisa digabung. 1438 1396 $runs = array(); 1439 1397 $current_run = array(); 1440 $last_end = 0; 1441 1442 foreach ( $tags as $t ) { 1443 $tag = $t[0]; 1444 $start = $t[1]; 1445 $end = $start + strlen( $tag ); 1446 1447 if ( ! empty( $current_run ) && '' !== trim( substr( $html, $last_end, $start - $last_end ) ) ) { 1398 foreach ( $ordered as $handle ) { 1399 $dep = isset( $wp_scripts->registered[ $handle ] ) ? $wp_scripts->registered[ $handle ] : null; 1400 $handle_group = ( $dep && ! empty( $dep->extra['group'] ) ) ? 1 : 0; 1401 1402 if ( $handle_group !== $group ) { 1403 // Handle milik grup lain (head vs footer) — pemutus run, 1404 // BUKAN diproses di pemanggilan fungsi ini sama sekali. 1448 1405 if ( count( $current_run ) >= 2 ) { 1449 1406 $runs[] = $current_run; 1450 1407 } 1451 1408 $current_run = array(); 1452 } 1453 1454 $info = $this->parse_script_tag( $tag, $start, $end ); 1409 continue; 1410 } 1411 1412 $info = $this->get_combinable_script_info( $handle, $wp_scripts ); 1455 1413 if ( false === $info ) { 1456 1414 if ( count( $current_run ) >= 2 ) { … … 1458 1416 } 1459 1417 $current_run = array(); 1460 } else { 1461 $current_run[] = $info; 1462 } 1463 1464 $last_end = $end; 1418 continue; 1419 } 1420 $current_run[] = $info; 1465 1421 } 1466 1422 if ( count( $current_run ) >= 2 ) { 1467 1423 $runs[] = $current_run; 1468 1424 } 1469 1470 1425 if ( empty( $runs ) ) { 1471 return $html;1472 } 1473 1474 foreach ( array_reverse( $runs )as $run ) {1426 return; 1427 } 1428 1429 foreach ( $runs as $run ) { 1475 1430 $bundle_url = $this->build_asset_bundle( 'js', $run ); 1476 1431 if ( false === $bundle_url ) { 1477 1432 continue; 1478 1433 } 1479 $first = $run[0]; 1480 $last = $run[ count( $run ) - 1 ]; 1481 $handle = 'nazemiwpsc-combined-js-' . substr( md5( $bundle_url ), 0, 12 ); 1482 $replacement = $this->render_enqueued_script_tag( $handle, $bundle_url ); 1483 if ( '' === $replacement ) { 1484 continue; // WP_Scripts gagal mencetak (seharusnya tidak pernah terjadi) — biarkan tag asli, jangan hapus aset. 1485 } 1486 1487 $html = substr_replace( $html, $replacement, $first['start'], $last['end'] - $first['start'] ); 1488 } 1489 1490 return $html; 1491 } 1492 1493 /** 1494 * Analisis satu tag <script src="...">. Mengembalikan array info jika 1495 * layak digabung (script polos, lokal, tanpa isi inline), atau false. 1496 */ 1497 private function parse_script_tag( $tag, $start, $end ) { 1498 // Tolak module/nomodule/async/defer — urutan & waktu eksekusinya 1499 // khusus, tidak boleh disatukan dengan script polos lain. 1500 if ( preg_match( '/\b(type=[\'"]module[\'"]|nomodule|async|defer)\b/i', $tag ) ) { 1434 $handle = 'nazemiwpsc-combined-js-' . substr( md5( $bundle_url ), 0, 12 ); 1435 // Versi EKSPLISIT wajib (Plugin Check: NoExplicitVersion) — lihat 1436 // catatan yang sama di combine_enqueued_styles(). 1437 $ver = pathinfo( $bundle_url, PATHINFO_FILENAME ); 1438 1439 // Fungsi ENQUEUE ASLI WordPress — bukan tag manual. Parameter 1440 // ke-5 (boolean, bukan array strategy WP 6.3+) sengaja dipakai 1441 // demi kompatibilitas mundur — readme.txt: "Requires at least: 6.0". 1442 wp_register_script( $handle, $bundle_url, array(), $ver, 1 === $group ); 1443 wp_enqueue_script( $handle ); 1444 1445 foreach ( $run as $item ) { 1446 wp_dequeue_script( $item['handle'] ); 1447 } 1448 } 1449 } 1450 1451 /** 1452 * Analisis satu handle JS terdaftar. Mengembalikan array info (handle, 1453 * href, path, mtime) jika layak digabung, atau false. 1454 */ 1455 private function get_combinable_script_info( $handle, $wp_scripts ) { 1456 if ( isset( $wp_scripts->done[ $handle ] ) ) { 1501 1457 return false; 1502 1458 } 1503 // Catatan: tidak perlu pengecualian khusus untuk script internal 1504 // plugin ini (lazy-loader, delay-js-manager, prefetch-hover) — fungsi 1505 // ini hanya dipanggil untuk tag yang sudah cocok dengan pola 1506 // `<script src="...">` (lihat combine_js()), dan ketiga script 1507 // tersebut selalu inline TANPA atribut src, jadi tidak akan pernah 1508 // sampai ke sini sama sekali. 1509 // Tolak kalau ada isi inline di antara tag buka & tutup (mis. fallback 1510 // script) — hanya <script src="..."></script> yang benar-benar kosong 1511 // yang aman digabung. 1512 if ( ! preg_match( '/<script\b[^>]*>\s*<\/script>/is', $tag ) ) { 1459 $dep = isset( $wp_scripts->registered[ $handle ] ) ? $wp_scripts->registered[ $handle ] : null; 1460 if ( ! $dep || empty( $dep->src ) ) { 1461 return false; // Tanpa src (mis. hanya pembawa wp_add_inline_script mandiri). 1462 } 1463 if ( ! empty( $dep->extra['data'] ) || ! empty( $dep->extra['after'] ) ) { 1464 return false; // Ada inline script (before/after, lihat wp_add_inline_script()) melekat pada handle ini — jangan sentuh, supaya inline script itu tidak ikut hilang. 1465 } 1466 if ( ! empty( $dep->extra['conditional'] ) ) { 1513 1467 return false; 1514 1468 } 1515 if ( preg_match( '/type=[\'"]([^\'"]+)[\'"]/i', $tag, $tm ) 1516 && ! in_array( strtolower( $tm[1] ), array( 'text/javascript', 'application/javascript' ), true ) 1517 ) { 1518 return false; 1519 } 1520 if ( ! preg_match( '/\bsrc=[\'"]([^\'"]+)[\'"]/i', $tag, $m ) ) { 1521 return false; 1522 } 1523 $src = html_entity_decode( $m[1], ENT_QUOTES ); 1524 1469 if ( ! empty( $dep->extra['strategy'] ) ) { 1470 return false; // async/defer (WP 6.3+ script loading strategy) — urutan/waktu eksekusinya signifikan, jangan gabung. 1471 } 1472 if ( isset( $dep->extra['type'] ) && 'module' === $dep->extra['type'] ) { 1473 return false; // ES module — semantik importnya beda, jangan gabung sebagai script biasa. 1474 } 1475 1476 $src = $this->resolve_dep_src( $dep->src, $wp_scripts->base_url ); 1525 1477 $path = $this->url_to_local_path( $src ); 1526 1478 if ( false === $path || ! is_readable( $path ) ) { 1527 1479 return false; 1528 1480 } 1529 1530 1481 $mtime = filemtime( $path ); 1531 1482 if ( false === $mtime ) { … … 1534 1485 1535 1486 return array( 1536 'tag' => $tag, 1537 'src' => $src, 1538 'path' => $path, 1539 'mtime' => $mtime, 1540 'start' => $start, 1541 'end' => $end, 1487 'handle' => $handle, 1488 'href' => $src, 1489 'path' => $path, 1490 'mtime' => $mtime, 1542 1491 ); 1543 1492 } 1544 1493 1545 1494 /** 1546 * Temukan rentang byte [start, end) dari setiap blok conditional comment 1547 * IE (<!--[if ...]> ... <![endif]-->) di dalam string. 1548 * 1549 * @param string $str String yang diperiksa. 1550 * @return array Daftar rentang [start, end]. 1551 */ 1552 private function find_conditional_comment_ranges( $str ) { 1553 $ranges = array(); 1554 if ( preg_match_all( '/<!--\[if[^\]]*\]>.*?<!\[endif\]-->/is', $str, $m, PREG_OFFSET_CAPTURE ) ) { 1555 foreach ( $m[0] as $match ) { 1556 $ranges[] = array( $match[1], $match[1] + strlen( $match[0] ) ); 1557 } 1558 } 1559 return $ranges; 1560 } 1495 * $dep->src bisa berupa URL lengkap, protokol-relatif ("//..."), atau 1496 * root-relatif ("/wp-content/..."). WP_Styles/WP_Scripts::do_item() 1497 * sendiri yang menambahkan $base_url saat mencetak kalau src tidak 1498 * diawali "http"/"//" — replikasi logika yang sama di sini supaya 1499 * url_to_local_path() menerima URL yang benar & lengkap. 1500 * 1501 * @param string $src Nilai mentah $dep->src. 1502 * @param string $base_url $wp_styles->base_url atau $wp_scripts->base_url. 1503 * @return string URL lengkap. 1504 */ 1505 private function resolve_dep_src( $src, $base_url ) { 1506 if ( preg_match( '#^(https?:)?//#i', $src ) ) { 1507 return $src; 1508 } 1509 return $base_url . $src; 1510 } 1511 1561 1512 1562 1513 /** … … 1701 1652 * 1702 1653 * @param string $type 'css' atau 'js'. 1703 * @param array $items Daftar file (hasil parse_stylesheet_tag/parse_script_tag).1654 * @param array $items Daftar file (hasil get_combinable_style_info()/get_combinable_script_info()). 1704 1655 * @return string|false URL bundle, atau false jika gagal. 1705 1656 */ … … 1868 1819 $browser_cache_on = ! empty( $this->options['browser_cache'] ); 1869 1820 1870 // [DISEDERHANAKAN] Konsisten dengan get_browser_cache_rules() di 1871 // server/class-wpsc-htaccess.php — "Durasi Browser Cache" dan "Bypass 1872 // HTML dari Browser Cache" sudah dihapus dari UI, disatukan jadi satu 1873 // perilaku tetap: selalu "Selalu Validasi ke Server". 1874 $asset_cache_control = 'no-cache, must-revalidate'; 1821 // [DIPERBAIKI] Konsisten dengan get_browser_cache_rules() di 1822 // server/class-wpsc-htaccess.php — dulu SELALU "Selalu Validasi ke 1823 // Server" (no-cache, must-revalidate) untuk file di folder _assets/ 1824 // ini juga (termasuk .css.gz / .js.gz hasil "Gabungkan CSS & JS"), 1825 // memaksa round-trip ke server untuk file yang justru paling sering 1826 // diminta. Sekarang publik, 30 hari — SELARAS dengan 1827 // $static_cache_control di get_browser_cache_rules(). .htaccess di 1828 // folder _assets/ ini lebih spesifik (lebih dalam) daripada 1829 // .htaccess utama, jadi nilai di sinilah yang sebenarnya berlaku 1830 // untuk file .css.gz/.js.gz/.ttf.gz/.otf.gz/.woff2 — perlu diubah di 1831 // KEDUA tempat supaya konsisten. 1832 $asset_cache_control = 'public, max-age=2592000'; // 30 hari. 1875 1833 1876 1834 // Blok rewrite + header .gz di sini hanya ditulis kalau "Kompres … … 2161 2119 * Blok <script> diekstrak sebelum PASS 5 (CSS url()) dan dipulihkan 2162 2120 * sesudahnya, supaya string JavaScript yang kebetulan mengandung pola 2163 * url-gambar tidak ikut ter-rewrite — teknik placeholder-token yang sama 2164 * dipakai di beberapa fungsi lain di kelas ini (lihat mis. combine_css()). 2121 * url-gambar tidak ikut ter-rewrite — teknik placeholder-token. 2165 2122 * 2166 2123 * @param string $html Konten HTML. … … 2296 2253 2297 2254 // PASS 5: CSS url() di atribut style dan blok <style>. 2298 // Blok <script> diekstrak dulu (placeholder token, sama seperti 2299 // combine_css()) agar url() di dalam kode JavaScript tidak ikut 2300 // diproses oleh regex ini. 2255 // Blok <script> diekstrak dulu (placeholder token) agar url() di 2256 // dalam kode JavaScript tidak ikut diproses oleh regex ini. 2301 2257 $script_blocks = array(); 2302 2258 $token = substr( md5( uniqid( 'nazemiwpsc_cdn', true ) ), 0, 10 ); -
masterspeed-cache/trunk/readme.txt
r3671949 r3672525 5 5 Tested up to: 7.1 6 6 Requires PHP: 8.0 7 Stable tag: 1. 17 Stable tag: 1.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html -
masterspeed-cache/trunk/server/class-wpsc-htaccess.php
r3670502 r3672525 569 569 570 570 private function get_browser_cache_rules( $options ) { 571 // [DISEDERHANAKAN] "Durasi Browser Cache" (dropdown TTL) dan "Bypass 572 // HTML dari Browser Cache" (toggle terpisah) sudah DIHAPUS dari UI — 573 // keduanya sekarang disatukan menjadi satu perilaku tetap: HTML DAN 574 // aset statis (CSS/JS/gambar/font) SELALU memakai "Selalu Validasi 575 // ke Server" (Cache-Control: no-cache, must-revalidate). Browser 576 // tetap menyimpan berkasnya, tapi wajib mengonfirmasi dulu ke server 577 // (conditional request/304) sebelum memakainya di setiap kunjungan — 578 // jadi purge di server selalu langsung terlihat pengunjung tanpa 579 // syarat, untuk HTML maupun aset statis. 580 $cache_control = 'no-cache, must-revalidate'; 581 $expires_str = 'access plus 0 seconds'; 571 // [DIPERBAIKI] Sebelumnya HTML DAN semua aset statis (CSS/JS/gambar/ 572 // font/arsip/dll.) sama-sama dipaksa "Selalu Validasi ke Server" 573 // (Cache-Control: no-cache, must-revalidate) — artinya browser WAJIB 574 // mengirim conditional request (round-trip penuh ke server) untuk 575 // SETIAP aset statis di SETIAP kunjungan, walau isinya tidak pernah 576 // berubah. Ini meniadakan manfaat paling dasar dari browser caching 577 // dan bertentangan langsung dengan tujuan plugin ini (kecepatan). 578 // 579 // Sekarang dipisah: 580 // - HTML tetap "no-cache, must-revalidate" ($cache_control) — supaya 581 // purge cache di server selalu langsung terlihat pengunjung tanpa 582 // syarat (perilaku ini TIDAK diubah). 583 // - Aset statis (termasuk CSS & JS) memakai Cache-Control publik 584 // dengan masa berlaku 30 hari ($static_cache_control / 585 // $static_expires_str). 30 hari (bukan 1 tahun/"immutable") 586 // dipilih secara sengaja: root .htaccess ini berlaku site-wide, 587 // termasuk /wp-admin/ (lihat $this->htaccess_file = ABSPATH . 588 // '.htaccess') — kalau suatu saat ada CSS/JS admin yang namanya 589 // tidak berubah walau isinya diperbarui (mis. lewat custom code 590 // tanpa query string ?ver=), staleness-nya dibatasi maksimal 30 591 // hari, bukan seterusnya. 592 $cache_control = 'no-cache, must-revalidate'; 593 $expires_str = 'access plus 0 seconds'; 594 $static_cache_control = 'public, max-age=2592000'; 595 $static_expires_str = 'access plus 2592000 seconds'; // 30 hari. 582 596 583 597 // [DIGANTI] Kompresi font OTF/TTF sekarang TIDAK lagi pakai mod_deflate … … 625 639 ExpiresByType text/html "' . $expires_str . '" 626 640 627 # CSS & JavaScript 628 ExpiresByType text/css "' . $ expires_str . '"629 ExpiresByType application/javascript "' . $ expires_str . '"630 ExpiresByType application/x-javascript "' . $ expires_str . '"631 ExpiresByType text/javascript "' . $ expires_str . '"632 633 # Gambar 634 ExpiresByType image/jpeg "' . $ expires_str . '"635 ExpiresByType image/png "' . $ expires_str . '"636 ExpiresByType image/gif "' . $ expires_str . '"637 ExpiresByType image/webp "' . $ expires_str . '"638 ExpiresByType image/avif "' . $ expires_str . '"639 ExpiresByType image/svg+xml "' . $ expires_str . '"640 ExpiresByType image/x-icon "' . $ expires_str . '"641 ExpiresByType image/vnd.microsoft.icon "' . $ expires_str . '"642 643 # Font 644 ExpiresByType font/ttf "' . $ expires_str . '"645 ExpiresByType font/otf "' . $ expires_str . '"646 ExpiresByType font/woff "' . $ expires_str . '"647 ExpiresByType font/woff2 "' . $ expires_str . '"648 ExpiresByType application/font-woff "' . $ expires_str . '"649 ExpiresByType application/font-woff2 "' . $ expires_str . '"650 ExpiresByType application/x-font-ttf "' . $ expires_str . '"641 # CSS & JavaScript — 30 hari. 642 ExpiresByType text/css "' . $static_expires_str . '" 643 ExpiresByType application/javascript "' . $static_expires_str . '" 644 ExpiresByType application/x-javascript "' . $static_expires_str . '" 645 ExpiresByType text/javascript "' . $static_expires_str . '" 646 647 # Gambar — 30 hari. 648 ExpiresByType image/jpeg "' . $static_expires_str . '" 649 ExpiresByType image/png "' . $static_expires_str . '" 650 ExpiresByType image/gif "' . $static_expires_str . '" 651 ExpiresByType image/webp "' . $static_expires_str . '" 652 ExpiresByType image/avif "' . $static_expires_str . '" 653 ExpiresByType image/svg+xml "' . $static_expires_str . '" 654 ExpiresByType image/x-icon "' . $static_expires_str . '" 655 ExpiresByType image/vnd.microsoft.icon "' . $static_expires_str . '" 656 657 # Font — 30 hari. 658 ExpiresByType font/ttf "' . $static_expires_str . '" 659 ExpiresByType font/otf "' . $static_expires_str . '" 660 ExpiresByType font/woff "' . $static_expires_str . '" 661 ExpiresByType font/woff2 "' . $static_expires_str . '" 662 ExpiresByType application/font-woff "' . $static_expires_str . '" 663 ExpiresByType application/font-woff2 "' . $static_expires_str . '" 664 ExpiresByType application/x-font-ttf "' . $static_expires_str . '" 651 665 652 666 # Data — tidak di-cache … … 663 677 664 678 <IfModule mod_headers.c> 665 # Aset statis DAN HTML: selalu "no-cache, must-revalidate" (lihat catatan 666 # di atas fungsi ini) — browser tetap menyimpan berkasnya tapi wajib 667 # mengonfirmasi dulu ke server (conditional request/304) sebelum 668 # memakainya, jadi purge di server selalu langsung terlihat pengunjung. 679 # Aset statis: Cache-Control publik 30 hari ($static_cache_control) — 680 # HTML tetap "no-cache, must-revalidate" secara terpisah di bawah (lihat 681 # catatan lengkap di awal fungsi ini). 669 682 # [PERUBAHAN] Daftar ekstensi di sini SEKARANG disamakan persis dengan 670 683 # daftar ekstensi Cache Rule #2 Cloudflare (Static Assets — lihat … … 675 688 # dapat Cache-Control yang sama dari origin — tidak konsisten antara 676 689 # kedua lapis cache untuk jenis file yang sama-sama statis/immutable. 690 # [DIPERBAIKI] Aset statis (termasuk CSS & JS) sekarang dapat 691 # Cache-Control publik 30 hari, bukan lagi "no-cache, must-revalidate" 692 # (lihat catatan lengkap di awal fungsi ini) — browser boleh langsung 693 # memakai salinan lokalnya tanpa round-trip ke server sampai 30 hari, 694 # bukan wajib validasi di SETIAP kunjungan seperti sebelumnya. 677 695 <FilesMatch "\.(7z|avi|avif|apk|bin|bmp|bz2|class|css|csv|doc|docx|dmg|ejs|eot|eps|exe|flac|gif|gz|ico|iso|jar|jpg|jpeg|js|mid|midi|mkv|mp3|mp4|ogg|otf|pdf|pict|pls|png|ppt|pptx|ps|rar|svg|svgz|swf|tar|tif|tiff|ttf|webm|webp|woff|woff2|xls|xlsx|zip|zst)$"> 678 Header set Cache-Control "' . $ cache_control . '"696 Header set Cache-Control "' . $static_cache_control . '" 679 697 </FilesMatch> 680 698 681 699 # [PERUBAHAN] Versi .gz dari bundle CSS/JS & font (lihat build_asset_bundle() 682 700 # dan relocate_font_for_bundle() di modules/class-wpsc-optimizer.php) — 683 # kalau "Kompres Cache" aktif, tag <link>/<script> di HTML menunjuk 684 # LANGSUNG ke file ".css.gz" / ".js.gz" / ".ttf.gz" / ".otf.gz", bukan 685 # lagi ke nama plain-nya. Tanpa entri terpisah ini, aturan di atas 686 # ("\.css$" dst.) tidak akan cocok dengan nama file yang berakhiran 687 # ".gz" (regex "$" mensyaratkan akhir string persis di ekstensi itu), 688 # jadi file yang justru paling sering diminta (versi terkompresi) malah 689 # tidak kebagian Cache-Control sama sekali. 701 # walau URL yang TERLIHAT di tag <link>/<script> selalu plain (lihat 702 # catatan [REVERT] di build_asset_bundle()), file FISIK yang disajikan 703 # lewat rewrite internal berakhiran ".css.gz" / ".js.gz" / ".ttf.gz" / 704 # ".otf.gz". Tanpa entri terpisah ini, aturan di atas ("\.css$" dst.) 705 # tidak akan cocok dengan nama file yang berakhiran ".gz" (regex "$" 706 # mensyaratkan akhir string persis di ekstensi itu), jadi file yang 707 # justru paling sering disajikan (versi terkompresi) malah tidak 708 # kebagian Cache-Control sama sekali. Sama seperti di atas: 30 hari, 709 # bukan no-cache. 690 710 <FilesMatch "\.(css|js|ttf|otf)\.gz$"> 691 Header set Cache-Control "' . $ cache_control . '"711 Header set Cache-Control "' . $static_cache_control . '" 692 712 </FilesMatch> 693 713
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)