PluginProbe
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts / 2.7.6
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts v2.7.6
2.7.6 2.7.5 2.7.4 trunk 1.3 2.0.4 2.0.6 2.1.91 2.2.4 2.2.7 2.2.9 2.3.1 2.3.10 2.4.10 2.4.2 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.6.0 2.6.1 2.7.0 2.7.1 All 27 releases
insert-php / includes / class.error-handler.php

class.error-handler.php in Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts 2.7.6, at includes/class.error-handler.php

346 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Error handler for PHP snippets.
4 *
5 * @package WP_Plugin_Insert_PHP
6 */
7
8 // Exit if accessed directly.
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * WINP_Error_Handler class
15 */
16 class WINP_Error_Handler {
17
18 /**
19 * Holds the current snippet context for error handling.
20 *
21 * @var array<string, mixed>|null
22 */
23 private static $current_snippet = null;
24
25 /**
26 * Tracks whether the error handler has been initialized.
27 *
28 * @var bool
29 */
30 private static $initialized = false;
31
32 /**
33 * Initialize error handler.
34 *
35 * @return void
36 */
37 public static function init() {
38 if ( self::$initialized ) {
39 return;
40 }
41
42 add_filter( 'wp_php_error_message', [ __CLASS__, 'customize_error_message' ], 9, 1 );
43 self::$initialized = true;
44 }
45
46 /**
47 * Set the current snippet context for error handling.
48 *
49 * @param int $snippet_id Snippet ID.
50 * @param string $title Snippet title.
51 * @param string $content Optional snippet content. Accepted for backward compatibility and ignored.
52 *
53 * @return void
54 */
55 public static function set_current_snippet( $snippet_id, $title, $content = null ) {
56 self::$current_snippet = [
57 'id' => $snippet_id,
58 'title' => $title,
59 'content' => $content,
60 ];
61 }
62
63 /**
64 * Clear the current snippet context after execution.
65 *
66 * @return void
67 */
68 public static function clear_current_snippet() {
69 self::$current_snippet = null;
70 }
71
72 /**
73 * Customize the error message displayed.
74 *
75 * @param string $message Original error message.
76 * @return string Modified error message.
77 */
78 public static function customize_error_message( $message ) {
79 $error = error_get_last();
80
81 if ( ! $error || ! self::$current_snippet ) {
82 return $message;
83 }
84
85 if ( ! self::is_eval_error( $error['file'] ) ) {
86 return $message;
87 }
88
89 $snippet_title = self::$current_snippet['title'];
90 $error_message = self::strip_stack_trace( $error['message'] );
91 $error_line = (int) $error['line'];
92
93 // Build the custom error display.
94 $custom_message = self::build_error_display( $snippet_title, $error_message, $error_line );
95
96 return $message . $custom_message;
97 }
98
99 /**
100 * Check if error file indicates eval'd code.
101 *
102 * @param string $file Error file path.
103 * @return bool True if error is from eval'd code.
104 */
105 private static function is_eval_error( $file ) {
106 return strpos( $file, "eval()'d code" ) !== false ||
107 strpos( $file, 'eval()\'d code' ) !== false;
108 }
109
110 /**
111 * Strip stack trace from error message.
112 *
113 * @param string $error_message Raw error message that may contain stack trace.
114 * @return string Clean error message without stack trace.
115 */
116 private static function strip_stack_trace( $error_message ) {
117 // Remove file path and eval() references.
118 $error_message = preg_replace( '/\s+in\s+(?:\/|[A-Z]:[\/\\\\]).*?eval\(\)\'d code:\d+/i', '', $error_message );
119
120 // Remove generic "in /path/to/file" references.
121 if ( $error_message ) {
122 $error_message = preg_replace( '/\s+in\s+(?:\/[^\s]+|[A-Z]:[\/\\\\][^\s]+)/i', '', $error_message );
123 }
124
125 $patterns = [
126 '/\s*Stack trace:.*/is',
127 '/\s*#\d+\s+.*/is',
128 '/\s*thrown in .*/is',
129 ];
130
131 foreach ( $patterns as $pattern ) {
132 if ( $error_message ) {
133 $error_message = preg_replace( $pattern, '', $error_message );
134 }
135 }
136
137 if ( $error_message ) {
138 $error_message = trim( $error_message );
139 }
140
141 return $error_message ? $error_message : '';
142 }
143
144 /**
145 * Build the custom error display HTML.
146 *
147 * @param string $snippet_title Snippet title.
148 * @param string $error_message Error message.
149 * @param int $error_line Line number where error occurred.
150 * @return string|false HTML for error display.
151 */
152 private static function build_error_display( $snippet_title, $error_message, $error_line ) {
153 $title = $snippet_title
154 ? sprintf(
155 // translators: %s is snippet title.
156 esc_html__( 'Snippet: %s', 'insert-php' ),
157 $snippet_title
158 )
159 : '';
160
161 $line_info = $error_line
162 ? sprintf(
163 // translators: %d is line number.
164 esc_html__( 'Line %d', 'insert-php' ),
165 $error_line
166 )
167 : '';
168
169 $full_error_text = sprintf(
170 "%s\n%s: %s",
171 $title,
172 $line_info,
173 $error_message
174 );
175
176 ob_start();
177 ?>
178 <style>
179 .winp-error-container {
180 background: #fff;
181 border: 1px solid #c3c4c7;
182 border-left: 4px solid #d63638;
183 box-shadow: 0 1px 1px rgba(0,0,0,.04);
184 margin: 20px 0;
185 padding: 20px;
186 }
187 .winp-error-title {
188 color: #d63638;
189 font-size: 18px;
190 font-weight: 600;
191 margin: 0 0 15px 0;
192 display: flex;
193 align-items: center;
194 gap: 10px;
195 }
196 .winp-error-icon {
197 width: 24px;
198 height: 24px;
199 }
200 .winp-error-details {
201 position: relative;
202 background: #f6f7f7;
203 border: 1px solid #dcdcde;
204 border-radius: 4px;
205 padding: 15px;
206 margin: 15px 0;
207 }
208 .winp-error-row {
209 margin: 10px 0;
210 line-height: 1.6;
211 }
212 .winp-error-label {
213 font-weight: 600;
214 color: #1d2327;
215 display: inline-block;
216 min-width: 80px;
217 }
218 .winp-error-value {
219 color: #2c3338;
220 font-family: Consolas, Monaco, monospace;
221 }
222 .winp-error-message {
223 color: #d63638;
224 font-weight: 500;
225 flex: 1;
226 }
227 .winp-copy-icon {
228 position: absolute;
229 top: 15px;
230 right: 15px;
231 flex-shrink: 0;
232 cursor: pointer;
233 padding: 4px;
234 border-radius: 3px;
235 transition: background 0.2s;
236 display: inline-flex;
237 align-items: center;
238 justify-content: center;
239 }
240 .winp-copy-icon:hover {
241 background: #e0e0e0;
242 }
243 .winp-copy-icon svg {
244 width: 18px;
245 height: 18px;
246 }
247 .winp-copy-icon.copied {
248 background: #d5f5d5;
249 }
250 .winp-error-line {
251 color: #d63638;
252 font-weight: 600;
253 font-size: 16px;
254 }
255 </style>
256
257 <div class="winp-error-container">
258 <h2 class="winp-error-title">
259 <svg class="winp-error-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
260 <circle cx="12" cy="12" r="10" stroke="#d63638" stroke-width="2"/>
261 <path d="M12 8v4m0 4h.01" stroke="#d63638" stroke-width="2" stroke-linecap="round"/>
262 </svg>
263 <?php esc_html_e( 'PHP Snippet Error', 'insert-php' ); ?>
264 </h2>
265
266 <div class="winp-error-details">
267 <div class="winp-error-row">
268 <span class="winp-error-label"><?php esc_html_e( 'Snippet:', 'insert-php' ); ?></span>
269 <span class="winp-error-value">
270 <?php echo esc_html( $snippet_title ); ?>
271 </span>
272 </div>
273
274 <?php if ( $error_line ) : ?>
275 <div class="winp-error-row">
276 <span class="winp-error-label"><?php esc_html_e( 'Line:', 'insert-php' ); ?></span>
277 <span class="winp-error-line"><?php echo esc_html( (string) $error_line ); ?></span>
278 </div>
279 <?php endif; ?>
280
281 <div class="winp-error-row">
282 <span class="winp-error-label"><?php esc_html_e( 'Error:', 'insert-php' ); ?></span>
283 <span class="winp-error-value winp-error-message"><?php echo esc_html( $error_message ); ?></span>
284 </div>
285 <span class="winp-copy-icon" onclick="copyErrorToClipboard()" title="<?php esc_attr_e( 'Copy error details', 'insert-php' ); ?>" id="winp-copy-icon">
286 <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
287 <rect x="9" y="9" width="13" height="13" rx="2" stroke="#2c3338" stroke-width="2"/>
288 <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" stroke="#2c3338" stroke-width="2"/>
289 </svg>
290 </span>
291 </div>
292 </div>
293
294 <script>
295 function copyErrorToClipboard() {
296 const errorText = <?php echo wp_json_encode( $full_error_text ); ?>;
297 const icon = document.getElementById('winp-copy-icon');
298
299 if (navigator.clipboard && navigator.clipboard.writeText) {
300 navigator.clipboard.writeText(errorText).then(function() {
301 showCopySuccess(icon);
302 }).catch(function(err) {
303 fallbackCopy(errorText, icon);
304 });
305 } else {
306 fallbackCopy(errorText, icon);
307 }
308 }
309
310 function fallbackCopy(text, icon) {
311 const textarea = document.createElement('textarea');
312 textarea.value = text;
313 textarea.style.position = 'fixed';
314 textarea.style.opacity = '0';
315 document.body.appendChild(textarea);
316 textarea.select();
317 try {
318 document.execCommand('copy');
319 showCopySuccess(icon);
320 } catch (err) {
321 alert(<?php echo wp_json_encode( __( 'Failed to copy. Please copy manually.', 'insert-php' ) ); ?>);
322 }
323 document.body.removeChild(textarea);
324 }
325
326 function showCopySuccess(icon) {
327 icon.classList.add('copied');
328 const originalTitle = icon.getAttribute('title');
329 icon.setAttribute('title', <?php echo wp_json_encode( __( 'Copied!', 'insert-php' ) ); ?>);
330
331 // Change icon to checkmark
332 icon.innerHTML = '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M20 6L9 17l-5-5" stroke="#007017" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
333
334 setTimeout(function() {
335 icon.classList.remove('copied');
336 icon.setAttribute('title', originalTitle);
337 // Restore clipboard icon
338 icon.innerHTML = '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="9" y="9" width="13" height="13" rx="2" stroke="#2c3338" stroke-width="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" stroke="#2c3338" stroke-width="2"/></svg>';
339 }, 2000);
340 }
341 </script>
342 <?php
343 return ob_get_clean();
344 }
345 }
346