« Back to History
extract_unique_styles.php
|
20260722_120325.php
Initial Domain Snapshot
Copy Code
<?php /** * extract_unique_styles.php * * Purpose: * - Scan ERP project for <style> blocks and inline style="..." attributes. * - Normalize inline style strings (parse properties, sort keys) to get unique style rules. * - Create a consolidated CSS file with generated class names for each unique inline style. * - Produce an HTML report listing files, found styles, and the class mapping. * * IMPORTANT: * - This script DOES NOT modify your page files. It's read-only by default. * - Generated CSS is saved to: /public/assets/css/extracted_styles.css (relative to ERP root). * - Run from your ERP root (where this file is placed). Access via browser or CLI. * * Usage: * - Place this file in ERP root (same level as 'public', 'modules', etc). * - Visit: https://yourdomain/erp/extract_unique_styles.php * * Security note: * - Do not leave this file publicly accessible on production without restricting access. */ set_time_limit(0); error_reporting(E_ALL); // ----------------- CONFIG ----------------- $root = __DIR__; // ERP project root (where this script is placed) $web_css_dir = $root . '/public/assets/css'; $output_css_file = $web_css_dir . '/extracted_styles.css'; $scan_extensions = ['php','html','htm']; $ignore_dirs = ['vendor','.git','node_modules','storage','cache']; // adjust if needed // ------------------------------------------ if (!is_dir($web_css_dir)) { @mkdir($web_css_dir, 0777, true); } // Helpers /** * Normalize a style string: * - Parse into key => value * - Trim and lowercase keys * - Sort keys alphabetically * - Rebuild normalized style string "k1: v1; k2: v2;" */ function normalize_style_string(string $style): string { // remove comments, excessive whitespace $style = trim(preg_replace('#/\*.*?\*/#s','', $style)); // split on semicolon but be careful with data URLs (rare in inline) $parts = preg_split('/;(?![^()]*\))/',$style); $props = []; foreach ($parts as $p) { $p = trim($p); if ($p === '') continue; // split on first colon $pos = strpos($p,':'); if ($pos === false) continue; $k = strtolower(trim(substr($p,0,$pos))); $v = trim(substr($p,$pos+1)); if ($k === '') continue; // unify whitespace in value $v = preg_replace('/\s+/', ' ', $v); $props[$k] = $v; } if (empty($props)) return ''; ksort($props); $out_parts = []; foreach ($props as $k=>$v) { $out_parts[] = $k . ': ' . $v; } return implode('; ', $out_parts) . ';'; } /** Short hash for class names */ function short_hash(string $s) { return substr(md5($s), 0, 8); } /** Safe CSS class name from hash */ function class_from_hash(string $hash) { return 'es-' . $hash; } /** Recursively iterate files, skip ignore dirs */ function iter_files($root, $exts, $ignore_dirs) { $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root)); foreach ($rii as $file) { if (!$file->isFile()) continue; $path = $file->getPathname(); // skip script itself if (basename($path) === basename(__FILE__)) continue; // skip ignored dirs foreach ($ignore_dirs as $ign) { if (stripos($path, DIRECTORY_SEPARATOR . $ign . DIRECTORY_SEPARATOR) !== false) { continue 2; } } $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); if (!in_array($ext, $exts)) continue; yield $path; } } // Storage $unique_inline_styles = []; // normalized_style => ['class'=>..., 'examples' => [ ['file'=>..., 'original'=>...], ... ] ] $extracted_style_blocks = []; // [ ['file'=>..., 'css'=>...], ... ] $file_reports = []; // file => ['style_blocks'=>[], 'inline_styles'=>[]] // Scan files foreach (iter_files($root, $scan_extensions, $ignore_dirs) as $filepath) { $content = file_get_contents($filepath); if ($content === false) continue; $rel = ltrim(str_replace($root, '', $filepath), '/\\'); $file_reports[$rel] = ['style_blocks'=>[], 'inline_styles'=>[]]; // 1) Extract <style>...</style> blocks (including <style ...>) if (stripos($content, '<style') !== false) { preg_match_all('#<style\b[^>]*>(.*?)</style>#is', $content, $matches, PREG_SET_ORDER); foreach ($matches as $m) { $css = trim($m[1]); if ($css === '') continue; $extracted_style_blocks[] = ['file'=>$rel, 'css'=>$css]; $file_reports[$rel]['style_blocks'][] = $css; } } // 2) Find inline style="..." if (stripos($content, 'style=') !== false) { // regex to find style attributes (both single and double quotes) preg_match_all('/\bstyle\s*=\s*(["\'])(.*?)\1/is', $content, $smatches, PREG_SET_ORDER); foreach ($smatches as $sm) { $orig = trim($sm[2]); if ($orig === '') continue; $normalized = normalize_style_string($orig); if ($normalized === '') continue; // register if (!isset($unique_inline_styles[$normalized])) { $h = short_hash($normalized); $cls = class_from_hash($h); $unique_inline_styles[$normalized] = [ 'class' => $cls, 'hash' => $h, 'examples' => [], ]; } $unique_inline_styles[$normalized]['examples'][] = ['file'=>$rel, 'original'=>$orig]; $file_reports[$rel]['inline_styles'][] = $orig; } } } // Build CSS file content $css_out = "/* Extracted styles generated by extract_unique_styles.php on " . date('c') . " */\n\n"; /* Add style blocks with provenance comments */ if (!empty($extracted_style_blocks)) { $css_out .= "/* --- Extracted <style> blocks (per-file) --- */\n"; foreach ($extracted_style_blocks as $b) { $css_out .= "/* from: {$b['file']} */\n"; $css_out .= rtrim($b['css']) . "\n\n"; } $css_out .= "\n"; } /* Inline style classes */ if (!empty($unique_inline_styles)) { $css_out .= "/* --- Generated classes for unique inline styles --- */\n"; foreach ($unique_inline_styles as $norm => $info) { $cls = $info['class']; // Use the normalized style (already ends with ;) $css_out .= ".$cls { $norm }\n"; } $css_out .= "\n"; } // Write CSS file $written = false; if ($css_out !== '') { // backup existing file if exists if (file_exists($output_css_file)) { copy($output_css_file, $output_css_file . '.bak.' . date('YmdHis')); } file_put_contents($output_css_file, $css_out); $written = file_exists($output_css_file); } // Prepare HTML report ?><!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Extracted Styles Report — ERP</title> <style> body{font-family:Inter,Roboto,Arial;margin:24px;background:#f7f8fb;color:#0f172a} .container{max-width:1100px;margin:0 auto} .card{background:#fff;border:1px solid #e6e9ef;padding:16px;border-radius:8px;margin-bottom:16px} pre{background:#0b1220;color:#dbeafe;padding:12px;border-radius:6px;overflow:auto} table{width:100%;border-collapse:collapse} th,td{padding:8px;border-bottom:1px solid #eee;text-align:left;font-size:14px} th{background:#f3f4f6} .small{font-size:13px;color:#6b7280} .btn{display:inline-block;padding:8px 12px;background:#1f6feb;color:#fff;border-radius:6px;text-decoration:none} .badge{display:inline-block;padding:4px 8px;border-radius:6px;background:#eef2ff;color:#1f3bb8;font-weight:600;font-size:12px} </style> </head> <body> <div class="container"> <h1>Extracted Styles Report</h1> <p class="small">Scan root: <strong><?php echo htmlspecialchars($root); ?></strong></p> <div class="card"> <h3>Output CSS</h3> <p class="small">Generated CSS file: <strong><?php echo htmlspecialchars(str_replace($root,'',$output_css_file)); ?></strong> <?php if ($written): ?> <span class="badge">written</span> <?php else: ?> <span class="badge" style="background:#fdecea;color:#b91c1c">not written</span> <?php endif; ?> </p> <p class="small">Note: This script does <strong>not</strong> modify your page files. It only generates the consolidated CSS and a report showing which inline styles were found and the mapping to generated classes.</p> <p> <a class="btn" href="<?php echo htmlspecialchars(str_replace($root, '', $output_css_file)); ?>" target="_blank">Open generated CSS</a> <a class="btn" href="<?php echo htmlspecialchars(str_replace($root, '', __FILE__)); ?>">Reload report</a> </p> </div> <div class="card"> <h3>Summary</h3> <table> <tr><th>Total files scanned</th><td><?php echo count($file_reports); ?></td></tr> <tr><th>Unique inline-style rules</th><td><?php echo count($unique_inline_styles); ?></td></tr> <tr><th>Extracted <style> blocks</th><td><?php echo count($extracted_style_blocks); ?></td></tr> </table> </div> <div class="card"> <h3>Unique inline-style → class mapping</h3> <p class="small">These classes are placed in <code><?php echo htmlspecialchars(str_replace($root,'',$output_css_file)); ?></code></p> <?php if (empty($unique_inline_styles)): ?> <p>No inline style attributes found.</p> <?php else: ?> <table> <thead> <tr><th>Class</th><th>Normalized style</th><th>Example files (count)</th></tr> </thead> <tbody> <?php foreach ($unique_inline_styles as $norm => $info): ?> <tr> <td><code><?php echo htmlspecialchars($info['class']); ?></code></td> <td><code><?php echo htmlspecialchars($norm); ?></code></td> <td> <?php // show up to 6 examples $examples = $info['examples']; $filesShown = []; foreach ($examples as $ex) { $filesShown[] = $ex['file']; } $filesShown = array_values(array_unique($filesShown)); $showList = array_slice($filesShown, 0, 6); echo htmlspecialchars(implode(', ', $showList)); if (count($filesShown) > 6) echo ' ... (+' . (count($filesShown)-6) . ' more)'; ?> </td> </tr> <?php endforeach; ?> </tbody> </table> <?php endif; ?> </div> <div class="card"> <h3>Per-file report (files that had styles)</h3> <?php if (empty($file_reports)): ?> <p>No files scanned.</p> <?php else: ?> <?php foreach ($file_reports as $file => $rep): ?> <?php if (empty($rep['style_blocks']) && empty($rep['inline_styles'])) continue; ?> <div style="margin-bottom:12px;"> <strong><?php echo htmlspecialchars($file); ?></strong> <div class="small" style="margin-top:6px;"> <?php if (!empty($rep['style_blocks'])): ?> <div><em><style> blocks:</em></div> <?php foreach ($rep['style_blocks'] as $sb): ?> <pre><?php echo htmlspecialchars($sb); ?></pre> <?php endforeach; ?> <?php endif; ?> <?php if (!empty($rep['inline_styles'])): ?> <div><em>inline style attributes found:</em></div> <ul> <?php foreach ($rep['inline_styles'] as $is): ?> <?php $norm = normalize_style_string($is); $cls = isset($unique_inline_styles[$norm]) ? $unique_inline_styles[$norm]['class'] : ''; ?> <li><code><?php echo htmlspecialchars($is); ?></code> <?php if ($cls): ?> → <code><?php echo htmlspecialchars($cls); ?></code><?php endif; ?> </li> <?php endforeach; ?> </ul> <?php endif; ?> </div> </div> <?php endforeach; ?> <?php endif; ?> </div> <div class="card"> <h3>Next steps (recommended)</h3> <ol> <li>Review the generated CSS file and the class mapping above.</li> <li>For each target page, replace inline attributes with the generated class names (e.g. <code><div class="es-abcdef12"></code>), or add the class alongside existing classes.</li> <li>Test pages on staging. When comfortable, perform replacements on production files (keep backups).</li> <li>Optionally, you can write a safe replace script that only updates files after your manual review — I can provide that if you want.</li> </ol> </div> </div> </body> </html>