🐘
console_monitor.php
← Back
πŸ“ Php ⚑ Executable Ctrl+S: Save β€’ Ctrl+R: Run β€’ Ctrl+F: Find
<?php // console_monitor.php β€” logs-first viewer. // β€’ Manual-only by default. Optional one-shot autorun with ?autorun=1 // β€’ Buttons: Refresh HTML (clears + runs), Run PHP (clears + runs via AJAX), Copy Visible Log // β€’ Default tab: Errors // β€’ PHP run happens via AJAX (action=run_php) so logs/Viewer update inside the monitor // β€’ Captures runtime JS errors, unhandledrejection, console; PHP errors via set_error_handler error_reporting(E_ALL); ini_set('display_errors', 1); session_start(); /* --------------------------- Params & Flags --------------------------- */ $executeFile = $_GET['file'] ?? ''; $autorun = isset($_GET['autorun']) && $_GET['autorun'] !== '0'; // ?autorun=1 if ($executeFile === basename(__FILE__)) $executeFile = ''; /* ------------------------- Session log buckets ------------------------ */ if (!isset($_SESSION['logs'])) { $_SESSION['logs'] = [ 'console' => [], 'errors' => [], 'php_errors' => [], 'network' => [], ]; } /* --------------------------- PHP error hook -------------------------- */ set_error_handler(function($errno, $errstr, $errfile, $errline) { $_SESSION['logs']['php_errors'][] = [ 'message' => $errstr, 'filename' => $errfile, 'lineno' => $errline, 'timestamp' => date('c') ]; }); /* ------------------------------ AJAX API ----------------------------- */ if (isset($_POST['action'])) { header('Content-Type: application/json; charset=utf-8'); switch ($_POST['action']) { case 'log': $_SESSION['logs']['console'][] = [ 'level' => $_POST['level'] ?? 'log', 'message' => $_POST['message'] ?? '', 'timestamp' => date('c') ]; echo json_encode(['success' => true]); break; case 'error': $_SESSION['logs']['errors'][] = [ 'message' => $_POST['message'] ?? '', 'filename' => $_POST['filename'] ?? '', 'lineno' => $_POST['lineno'] ?? '', 'colno' => $_POST['colno'] ?? '', 'stack' => $_POST['stack'] ?? '', 'timestamp' => date('c') ]; echo json_encode(['success' => true]); break; case 'net': $_SESSION['logs']['network'][] = [ 'method' => $_POST['method'] ?? '', 'url' => $_POST['url'] ?? '', 'status' => $_POST['status'] ?? '', 'ok' => $_POST['ok'] ?? '', 'duration' => $_POST['duration'] ?? '', 'error' => $_POST['error'] ?? '', 'timestamp' => date('c') ]; echo json_encode(['success' => true]); break; case 'get_logs': echo json_encode($_SESSION['logs']); break; case 'clear_logs': $_SESSION['logs'] = ['console'=>[],'errors'=>[],'php_errors'=>[],'network'=>[]]; echo json_encode(['success' => true]); break; case 'run_php': { // Run a PHP file only via AJAX (keeps console/Viewer in sync) $file = $_POST['file'] ?? ''; $safe = basename($file); $path = __DIR__ . '/' . $safe; $ext = strtolower(pathinfo($safe, PATHINFO_EXTENSION)); if (!$file || !file_exists($path) || $ext !== 'php') { echo json_encode(['success'=>false, 'error'=>'Invalid PHP file']); break; } // Clear logs first (same behavior as Refresh HTML) $_SESSION['logs'] = ['console'=>[],'errors'=>[],'php_errors'=>[],'network'=>[]]; ob_start(); try { include $path; } catch (Throwable $t) { // Also push a php_error entry (in case handler didn’t catch) $_SESSION['logs']['php_errors'][] = [ 'message' => $t->getMessage(), 'filename' => $t->getFile(), 'lineno' => $t->getLine(), 'timestamp' => date('c') ]; echo "Fatal error: " . htmlspecialchars($t->getMessage(), ENT_QUOTES, 'UTF-8'); } $output = ob_get_clean(); echo json_encode(['success'=>true, 'output'=>$output]); break; } } exit; } /* --------------------- File type detection (view) -------------------- */ $output = ''; $fileContent = ''; $isHtmlFile = false; $isPhpFile = false; if ($executeFile) { $path = __DIR__ . '/' . basename($executeFile); $ext = strtolower(pathinfo($executeFile, PATHINFO_EXTENSION)); $allowed = ['php','html','htm']; if (!file_exists($path) || !in_array($ext, $allowed, true)) { $output = "Error: Invalid or inaccessible file."; $executeFile = ''; } else { $fileContent = file_get_contents($path); if ($ext === 'php') { // Don’t execute now β€” only via AJAX Run PHP $isPhpFile = true; $output = ''; // no placeholder } else { $isHtmlFile = true; $output = ''; // no placeholder } } } $backUrl = 'editor.php' . ($executeFile ? ('?view=' . urlencode($executeFile)) : ''); $fileLabel = $executeFile; ?> <!doctype html> <html> <head> <meta charset="utf-8" /> <title>Console Monitor</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style> *{margin:0;padding:0;box-sizing:border-box} body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;background:#f5f5f5;color:#111} .header{background:#2c3e50;color:#fff;padding:12px 16px;display:flex;justify-content:space-between;align-items:center} .back-btn{background:rgba(255,255,255,.1);color:#fff;padding:8px 12px;text-decoration:none;border-radius:6px;border:1px solid rgba(255,255,255,.2)} .controls{padding:10px 16px;background:#f8f9fa;border-bottom:1px solid #ddd;display:flex;gap:10px;flex-wrap:wrap;position:sticky;top:0;z-index:10} .btn{padding:10px 16px;border:none;border-radius:8px;cursor:pointer;font-size:14px} .btn-success{background:#27ae60;color:#fff} .btn-secondary{background:#6c757d;color:#fff} .main{display:flex;flex-direction:column;gap:12px;padding:12px 16px} .panel{background:#fff;border:1px solid #ddd;border-radius:10px;overflow:hidden} .panel-header{padding:10px 14px;background:#f8f9fa;border-bottom:1px solid #eee;font-weight:600} .panel-content{padding:0} .tabs{display:flex;background:#f8f9fa;border-bottom:1px solid #eee} .tab{padding:10px 14px;cursor:pointer;border-bottom:3px solid transparent;font-size:12px} .tab.active{background:#fff;border-bottom-color:#3498db} .logs-content{max-height:360px;min-height:240px;padding:14px;background:#1e1e1e;color:#fff;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;overflow:auto} .log-entry{margin-bottom:8px;padding:6px;border-radius:4px} .log-entry.error{background:rgba(231,76,60,.1);color:#ff6b6b} .log-entry.warn{background:rgba(241,196,15,.1);color:#feca57} .log-entry.info{color:#74b9ff} .log-entry.log{color:#fff} .timestamp{color:#95a5a6;font-size:11px} .hidden{display:none} .stack pre{white-space:pre-wrap;word-break:break-word} .viewer-wrap{padding:14px} .output-content{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;white-space:pre-wrap;background:#fff;margin-bottom:10px;border:1px solid #eee;border-radius:8px;padding:10px;min-height:40px} #htmlPreview{width:100%;height:520px;border:1px solid #ddd;border-radius:8px;background:#fff} .code-wrap{padding:0} .code-editor{width:100%;height:620px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.4;border:none;background:#1e1e1e;color:#fff;padding:16px;resize:none;outline:none} </style> </head> <body> <div class="header"> <h1>Console Monitor <?= $executeFile ? ('- ' . htmlspecialchars($executeFile, ENT_QUOTES, 'UTF-8')) : '' ?></h1> <a class="back-btn" href="<?= htmlspecialchars($backUrl, ENT_QUOTES, 'UTF-8') ?>">← Back to Editor</a> </div> <div class="controls"> <?php if ($isHtmlFile): ?> <button class="btn btn-success" onclick="refreshHtml()">πŸ”„ Refresh HTML</button> <?php endif; ?> <?php if ($isPhpFile): ?> <button class="btn btn-success" onclick="runPhp()">β–Ά Run PHP</button> <?php endif; ?> <?php if ($isHtmlFile || $isPhpFile): ?> <button class="btn btn-secondary" onclick="copyCurrentLog()">πŸ“‹ Copy Visible Log</button> <?php endif; ?> </div> <div class="main"> <!-- Logs --> <div class="panel"> <div class="panel-header">Logs</div> <div class="panel-content"> <div class="tabs"> <div class="tab" onclick="switchTab('console', this)">Console</div> <div class="tab active" onclick="switchTab('errors', this)">Errors</div> <div class="tab" onclick="switchTab('php_errors', this)">PHP Errors</div> <div class="tab" onclick="switchTab('network', this)">Network</div> </div> <div id="console-logs" class="logs-content hidden"></div> <div id="errors-logs" class="logs-content"></div> <div id="php_errors-logs" class="logs-content hidden"></div> <div id="network-logs" class="logs-content hidden"></div> </div> </div> <!-- Viewer --> <div class="panel"> <div class="panel-header">Viewer</div> <div class="panel-content viewer-wrap"> <?php if (!$isHtmlFile && !$isPhpFile): ?> <div class="output-content"><?= htmlspecialchars($output, ENT_QUOTES, 'UTF-8') ?></div> <?php endif; ?> <?php if ($isHtmlFile): ?> <iframe id="htmlPreview" sandbox="allow-scripts allow-same-origin"></iframe> <?php endif; ?> <?php if ($isPhpFile): ?> <div id="phpOutput" class="output-content"></div> <?php endif; ?> </div> </div> <!-- Code --> <?php if ($fileContent): ?> <div class="panel"> <div class="panel-header">Source Code</div> <div class="panel-content code-wrap"> <textarea class="code-editor" readonly><?= htmlspecialchars($fileContent, ENT_QUOTES, 'UTF-8') ?></textarea> </div> </div> <?php endif; ?> </div> <?php if ($isHtmlFile || $isPhpFile): ?> <!-- JSON payload with file info/content --> <script id="filePayload" type="application/json"> <?= json_encode( ['content' => $fileContent, 'filename' => $fileLabel, 'isHtml' => $isHtmlFile, 'isPhp' => $isPhpFile, 'autorun' => $autorun], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) . "\n" ?> </script> <script> (function(){ try{ const payload = JSON.parse(document.getElementById('filePayload').textContent || '{}'); window.htmlContent = payload.content || ''; window.filename = payload.filename || ''; window.isHtml = !!payload.isHtml; window.isPhp = !!payload.isPhp; window.autorun = !!payload.autorun; }catch(e){} })(); </script> <?php endif; ?> <script> let currentTab = 'errors'; let logs = { console:[], errors:[], php_errors:[], network:[] }; let _blobUrl = null; // Optional one-shot autorun (only if you call ?autorun=1) document.addEventListener('DOMContentLoaded', function(){ displayLogs(); if (window.autorun) { if (window.isHtml) refreshHtml(); else if (window.isPhp) runPhp(); } }); /* ------------------------- HTML workflow ------------------------- */ async function refreshHtml(){ // Clear logs on server + UI try{ await fetch('console_monitor.php', { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, body:'action=clear_logs' }); }catch(e){} logs = { console:[], errors:[], php_errors:[], network:[] }; displayLogs(); // Run HTML fresh runHtml(true); // Pull first posts shortly after (no timer loop) setTimeout(refreshLogs, 350); } function runHtml(fresh){ if (!window.htmlContent) return; const frame = document.getElementById('htmlPreview'); if (!frame) return; var monitorScript = '<script>(function(){' + ' var FILE = ' + JSON.stringify(<?= json_encode($fileLabel, JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT) ?>) + ';' + ' window.addEventListener("error", function(e){try{var t=e.target||null;var isRes=t&&(t.src||t.href);var msg=String(e.message||(isRes?"Resource load error":"Error"));var file=e.filename||(t&&(t.src||t.href))||FILE||"";var line=e.lineno||"";var col=e.colno||"";var stack=(e.error&&e.error.stack)?String(e.error.stack):"";parent.postMessage({type:"error",message:msg,filename:file,lineno:line,colno:col,stack:stack},"*");}catch(_){ }}, true);' + ' window.addEventListener("unhandledrejection", function(ev){try{var r=ev.reason;var msg=r&&(r.message||String(r))||"Unhandled rejection";var stack=r&&r.stack?String(r.stack):"";parent.postMessage({type:"error",message:msg,filename:FILE,lineno:"",colno:"",stack:stack},"*");}catch(_){ }});' + ' ["log","error","warn","info"].forEach(function(m){var o=console[m];console[m]=function(){try{o&&o.apply(console,arguments);}catch(_){ }try{var text=Array.from(arguments).map(function(a){try{return typeof a==="string"?a:JSON.stringify(a);}catch(_){return String(a);}}).join(" ");parent.postMessage({type:"console",level:m,message:text},"*");}catch(_){ }}});' + '})();<\/script>'; let html = String(window.htmlContent); if (html.includes('<head>')) html = html.replace('<head>', '<head>' + monitorScript); else if (html.includes('</body>')) html = html.replace('</body>', monitorScript + '</body>'); else if (html.includes('<html')) html = html + monitorScript; else html = '<!doctype html><html><head>' + monitorScript + '</head><body>' + html + '</body></html>'; if (_blobUrl && fresh) { try{ URL.revokeObjectURL(_blobUrl); }catch{} _blobUrl = null; } _blobUrl = _blobUrl || URL.createObjectURL(new Blob([html], {type:'text/html'})); frame.src = _blobUrl; } /* -------------------------- PHP workflow ------------------------- */ async function runPhp(){ if (!window.filename) return; // Clear logs on server + UI (to mirror Refresh HTML behavior) try{ await fetch('console_monitor.php', { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, body:'action=clear_logs' }); }catch(e){} logs = { console:[], errors:[], php_errors:[], network:[] }; displayLogs(); // Run PHP via AJAX so php_errors flow into the logs panel const p = new URLSearchParams({ action:'run_php', file: window.filename }); const res = await fetch('console_monitor.php', { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, body:p.toString() }).then(r=>r.json()).catch(()=>({success:false,error:'request failed'})); const out = document.getElementById('phpOutput'); if (out) out.textContent = res && res.output ? res.output : (res.error || ''); // Pull the logs now that PHP has run refreshLogs(); } /* --------------------------- Messaging --------------------------- */ window.addEventListener('message', e => { const d = e.data || {}; if (d.type === 'error') { sendError(d); refreshLogs(); } if (d.type === 'console'){ sendLog(d.level, d.message); refreshLogs(); } if (d.type === 'net') { sendNet(d); refreshLogs(); } }); /* ------------------------- Server helpers ------------------------ */ function sendLog(level, message){ fetch('console_monitor.php', { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, body:'action=log&level='+encodeURIComponent(level)+'&message='+encodeURIComponent(message) }).catch(()=>{}); } function sendError(d){ const p = new URLSearchParams({ action:'error', message:d.message||'', filename:d.filename||'', lineno:String(d.lineno||''), colno:String(d.colno||''), stack:d.stack||'' }); fetch('console_monitor.php', { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, body:p.toString() }).catch(()=>{}); } function sendNet(d){ const p = new URLSearchParams({ action:'net', method:d.method||'', url:d.url||'', status:String(d.status||''), ok:String(d.ok||0), duration:String(d.duration||''), error:d.error||'' }); fetch('console_monitor.php', { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, body:p.toString() }).catch(()=>{}); } /* ------------------------ Tabs & Rendering ----------------------- */ function switchTab(tab, el){ document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active')); if (el) el.classList.add('active'); document.querySelectorAll('.logs-content').forEach(c=>c.classList.add('hidden')); const pane = document.getElementById(tab + '-logs'); if (pane) pane.classList.remove('hidden'); currentTab = tab; displayLogs(); } function esc(s){ return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;'); } function renderMessage(msg){ const MAX = 600, s = String(msg || ''); if (s.length <= MAX) return esc(s); const head = esc(s.slice(0, MAX)); return head + '… ' + '<a href="#" onclick="this.nextElementSibling.classList.remove(\'hidden\'); this.remove(); return false;">show more</a>' + '<div class="hidden stack"><pre>' + esc(s) + '</pre></div>'; } function displayLogs(){ // Console (function(){ const c = document.getElementById('console-logs'); c.innerHTML = ''; const arr = logs.console || []; if (arr.length === 0) { c.innerHTML = '<div class="log-entry info">No console logs</div>'; return; } arr.forEach(l=>{ const ts = l.timestamp ? new Date(l.timestamp).toLocaleTimeString() : ''; const e = document.createElement('div'); e.className = 'log-entry ' + (l.level||'log'); e.innerHTML = `<span class="timestamp">[${ts}]</span> [${(l.level||'LOG').toUpperCase()}] ${esc(l.message)}`; c.appendChild(e); }); c.scrollTop = c.scrollHeight; })(); // Errors (function(){ const c = document.getElementById('errors-logs'); c.innerHTML = ''; const arr = logs.errors || []; if (arr.length === 0) { c.innerHTML = '<div class="log-entry info">No errors</div>'; return; } arr.forEach(l=>{ const ts = l.timestamp ? new Date(l.timestamp).toLocaleTimeString() : ''; const file = esc(l.filename||''); const line = l.lineno ? (':'+esc(l.lineno)) : ''; const col = l.colno ? (':'+esc(l.colno)) : ''; const stack= l.stack ? `<div class="stack"><strong>Stack:</strong><pre>${renderMessage(l.stack)}</pre></div>` : ''; const e = document.createElement('div'); e.className = 'log-entry error'; e.innerHTML = `<span class="timestamp">[${ts}]</span> <div><strong>Error:</strong> ${renderMessage(l.message)}</div> <div><strong>File:</strong> ${file}${line}${col}</div> ${stack}`; c.appendChild(e); }); c.scrollTop = c.scrollHeight; })(); // PHP Errors (function(){ const c = document.getElementById('php_errors-logs'); c.innerHTML = ''; const arr = logs.php_errors || []; if (arr.length === 0) { c.innerHTML = '<div class="log-entry info">No PHP errors</div>'; return; } arr.forEach(l=>{ const ts = l.timestamp ? new Date(l.timestamp).toLocaleTimeString() : ''; const e = document.createElement('div'); e.className = 'log-entry error'; e.innerHTML = `<span class="timestamp">[${ts}]</span> <div><strong>PHP:</strong> ${renderMessage(l.message)}</div> <div><strong>File:</strong> ${esc(l.filename||'')}:${esc(l.lineno||'')}</div>`; c.appendChild(e); }); c.scrollTop = c.scrollHeight; })(); // Network (if enabled server messages) (function(){ const c = document.getElementById('network-logs'); c.innerHTML = ''; const arr = logs.network || []; if (arr.length === 0) { c.innerHTML = '<div class="log-entry info">No network events</div>'; return; } arr.forEach(n=>{ const ts = n.timestamp ? new Date(n.timestamp).toLocaleTimeString() : ''; const e = document.createElement('div'); e.className = 'log-entry info'; e.innerHTML = `<span class="timestamp">[${ts}]</span> <div><strong>${esc(n.method||'')}</strong> ${esc(n.url||'')}</div> <div>Status: ${esc(n.status||'')} β€” OK: ${n.ok ? 'βœ”' : 'βœ–'} β€” ${esc(n.duration||'')}ms</div> ${n.error ? `<div>Error: ${esc(n.error)}</div>` : ''}`; c.appendChild(e); }); c.scrollTop = c.scrollHeight; })(); // keep the currentTab visible const pane = document.getElementById(currentTab+'-logs'); if (pane) pane.classList.remove('hidden'); } function refreshLogs(){ fetch('console_monitor.php', { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, body:'action=get_logs' }).then(r=>r.json()).then(data => { logs = data || logs; displayLogs(); }).catch(()=>{}); } async function copyCurrentLog(){ const pane = document.getElementById(currentTab + '-logs'); if (!pane) return; const text = pane.innerText || ''; try{ await navigator.clipboard.writeText(text); } catch(e){ alert('Copy failed (clipboard unavailable).'); } } </script> </body> </html>