PluginProbe
WPIDE – File Manager & Code Editor / 3.4.1
WPIDE – File Manager & Code Editor v3.4.1
3.5.8 3.5.7 2.0.14 2.0.15 2.0.16 2.0.2 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1 2.2 2.3 2.3.1 2.3.2 2.4.0 2.5 2.6 3.0 3.1 3.2 3.3 3.4 All 54 releases
wpide / wp-content / fatal-error-handler.php

fatal-error-handler.php in WPIDE – File Manager & Code Editor 3.4.1, at wp-content/fatal-error-handler.php

882 lines 31.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * WPide - Fatal Error Handler - Drop-In
4 * Takes care of catching errors and exception after editing php files using WPide file editor and offers an easy way to recover erroneous files by restoring them from saved backups
5 */
6
7 if (!class_exists('WPIDE_Error_Handler')) {
8
9 class WPIDE_Error_Handler
10 {
11
12 protected static $slug = 'wpide';
13 protected static $backupDir = '/wpide/backups/';
14
15 public static function boot()
16 {
17
18 if (version_compare(PHP_VERSION, '7.4.0', '>=')) {
19
20 register_shutdown_function([__CLASS__, 'shutdownHandler']);
21 set_exception_handler([__CLASS__, 'exceptionHandler']);
22 set_error_handler([__CLASS__, 'errorHandler']);
23 }
24 }
25
26 public static function isPluginScreen(): bool
27 {
28
29 $screen = function_exists('get_current_screen') ? get_current_screen() : null;
30
31 if(!empty($screen)) {
32 return strpos($screen->base, 'page_' . self::$slug) !== false;
33 }
34
35 if(!empty($_GET['page'])) {
36 $page = sanitize_text_field($_GET['page']);
37 return strpos($page, self::$slug) !== false;
38 }
39
40 return false;
41 }
42
43 public static function shutdownHandler()
44 {
45
46 $error = error_get_last();
47
48 if ($error) {
49 self::errorHandler($error['type'], $error['message'], $error['file'], $error['line']);
50 }
51 }
52
53 public static function exceptionHandler(Throwable $exception)
54 {
55
56 self::errorHandler(-1, $exception->getMessage(), $exception->getFile(), $exception->getLine());
57 }
58
59 public static function errorHandler($error_type, $error_message, $error_file, $error_line)
60 {
61
62 if (!$error_type) {
63 return;
64 }
65
66 $fatal = false;
67
68 switch ($error_type) {
69
70 case -1:
71 case E_ERROR:
72 case E_USER_ERROR:
73 case E_PARSE:
74 case E_CORE_ERROR:
75 case E_COMPILE_ERROR:
76 case E_RECOVERABLE_ERROR:
77 $fatal = true;
78 break;
79 }
80
81 switch ($error_type) {
82 case -1: // -1 //
83 $typestr = 'EXCEPTION';
84 break;
85 case E_ERROR: // 1 //
86 $typestr = 'E_ERROR';
87 break;
88 case E_WARNING: // 2 //
89 $typestr = 'E_WARNING';
90 break;
91 case E_PARSE: // 4 //
92 $typestr = 'E_PARSE';
93 break;
94 case E_NOTICE: // 8 //
95 $typestr = 'E_NOTICE';
96 break;
97 case E_CORE_ERROR: // 16 //
98 $typestr = 'E_CORE_ERROR';
99 break;
100 case E_CORE_WARNING: // 32 //
101 $typestr = 'E_CORE_WARNING';
102 break;
103 case E_COMPILE_ERROR: // 64 //
104 $typestr = 'E_COMPILE_ERROR';
105 break;
106 case E_COMPILE_WARNING: // 128 //
107 $typestr = 'E_COMPILE_WARNING';
108 break;
109 case E_USER_ERROR: // 256 //
110 $typestr = 'E_USER_ERROR';
111 break;
112 case E_USER_WARNING: // 512 //
113 $typestr = 'E_USER_WARNING';
114 break;
115 case E_USER_NOTICE: // 1024 //
116 $typestr = 'E_USER_NOTICE';
117 break;
118 case E_STRICT: // 2048 //
119 $typestr = 'E_STRICT';
120 break;
121 case E_RECOVERABLE_ERROR: // 4096 //
122 $typestr = 'E_RECOVERABLE_ERROR';
123 break;
124 case E_DEPRECATED: // 8192 //
125 $typestr = 'E_DEPRECATED';
126 break;
127 case E_USER_DEPRECATED: // 16384 //
128 $typestr = 'E_USER_DEPRECATED';
129 break;
130
131 }
132
133 $message = '<strong>' . $typestr . ': </strong>' . $error_message . ' in <strong>' . $error_file . '</strong> on line <strong>' . $error_line . '</strong><br/><br/>';
134
135 //Logging error on php file error log...
136 if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
137 error_log(strip_tags($message), 0);
138 }
139
140 if ($fatal) {
141
142 $lockfile = self::backupDir().'.lock';
143 $backup_file = self::findBackupFile($error_file);
144
145 // If backup exists
146 if (!empty($backup_file) && file_exists($backup_file)) {
147
148 if (ob_get_length()) ob_end_clean();
149
150 // If on admin side, show file recovery wizard
151 if (is_admin()) {
152 $recover = !empty($_GET['recover']);
153 if ($recover) {
154
155 $success = self::recoverFile($error_file, $backup_file);
156
157 if(file_exists($lockfile)) {
158 @unlink($lockfile);
159 }
160
161 wp_send_json([
162 'success' => $success
163 ]);
164
165 } else {
166
167 self::recoverTemplate($typestr, $error_message, $error_file, $error_line, $backup_file);
168
169 file_put_contents($lockfile, '1');
170 }
171
172 // If on the frontend, try to recover the file silently
173 } else if(!file_exists($lockfile)){
174
175 if(file_exists($lockfile)) {
176 @unlink($lockfile);
177 }
178
179 $success = self::recoverFile($error_file, $backup_file);
180
181 // redirect home on success
182 if ($success) {
183
184 die('<meta http-equiv="refresh" content="0">');
185
186 } else {
187
188 // if failed, show error message
189 self::displayError($message);
190 }
191 }
192 die();
193 }
194 }
195
196 }
197
198 public static function displayError($message) {
199
200 // Display error only if WP_DEBUG & WP_DEBUG_DISPLAY are enabled and not DOING_AJAX
201 if(!self::isPluginScreen() && defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY && !defined('DOING_AJAX')) {
202 printf('%s', $message);
203 }
204 }
205
206 public static function backupDir(): string
207 {
208
209 $uploadsBaseDir = function_exists('wp_upload_dir') ? wp_upload_dir()['basedir'] : __DIR__.'/uploads/';
210
211 return $uploadsBaseDir.self::$backupDir;
212 }
213
214 public static function findBackupFile($error_file): ?string
215 {
216
217 $backupFolders = scandir(self::backupDir());
218
219 if(empty($backupFolders)) {
220 return null;
221 }
222
223 // Get backup folders
224 $backupFolders = array_filter($backupFolders, function ($folder) {
225
226 if ($folder === '.' || $folder === '..') {
227 return false;
228 }
229
230 $date = date_create_from_format('Y-m-d', $folder);
231
232 return $date !== false;
233 });
234
235 // Sort from latest to oldest
236 sort($backupFolders);
237 $backupFolders = array_reverse($backupFolders);
238
239 // Loop and find the latest backup file
240 foreach ($backupFolders as $folder) {
241
242 $backup_file = self::backupDir() . $folder . '/' . str_replace(ABSPATH, "", $error_file);
243
244 if (file_exists($backup_file)) {
245 return $backup_file;
246 }
247 }
248
249 return null;
250 }
251
252 public static function recoverFile($error_file, $backup_file): bool
253 {
254
255 ob_start();
256 $backup_file_content = file_get_contents($backup_file);
257 $result = file_put_contents($error_file, $backup_file_content);
258 ob_end_clean();
259
260 return $result !== false;
261 }
262
263 public static function recoverTemplate($error_type, $error_message, $error_file, $error_line, $backup_file)
264 {
265
266 ?>
267 <div class="container">
268 <div class="error">
269 <div class="error-inner">
270 <h2><?php echo $error_type; ?></h2>
271 <h3>Ooh-oh, something went wrong after modifying this file:</h3>
272 <p class="error-file">
273 <code><strong>File</strong>
274 <span><?php echo str_replace(__DIR__, "", $error_file); ?></span></code>
275 <code class="error-message"><strong>Error</strong>
276 <span><?php echo $error_message; ?></span></code>
277 <code><strong>Line</strong> <span><?php echo $error_line; ?></span></code>
278 <code><strong>Backup</strong>
279 <span><?php echo str_replace(__DIR__, "", $backup_file); ?></span></code>
280 </p>
281 <p class="recovering-message">
282 <strong>WPIDE</strong> will try to recover the file for you using the latest saved backup!
283 </p>
284 <p>
285 <button class="button recover-btn"><span>Recover</span></button>
286 <button class="button refresh-btn"><span>Refresh</span></button>
287 </p>
288 </div>
289 </div>
290 <div class="stack-container">
291 <div class="card-container">
292 <div class="perspec" style="--spreaddist: 125px; --scaledist: .75; --vertdist: -25px;">
293 <div class="card">
294 <div class="writing">
295 <div class="topbar">
296 <div class="red"></div>
297 <div class="yellow"></div>
298 <div class="green"></div>
299 </div>
300 <div class="code">
301 <ul></ul>
302 </div>
303 </div>
304 </div>
305 </div>
306 </div>
307 <div class="card-container">
308 <div class="perspec" style="--spreaddist: 100px; --scaledist: .8; --vertdist: -20px;">
309 <div class="card">
310 <div class="writing">
311 <div class="topbar">
312 <div class="red"></div>
313 <div class="yellow"></div>
314 <div class="green"></div>
315 </div>
316 <div class="code">
317 <ul></ul>
318 </div>
319 </div>
320 </div>
321 </div>
322 </div>
323 <div class="card-container">
324 <div class="perspec" style="--spreaddist:75px; --scaledist: .85; --vertdist: -15px;">
325 <div class="card">
326 <div class="writing">
327 <div class="topbar">
328 <div class="red"></div>
329 <div class="yellow"></div>
330 <div class="green"></div>
331 </div>
332 <div class="code">
333 <ul></ul>
334 </div>
335 </div>
336 </div>
337 </div>
338 </div>
339 <div class="card-container">
340 <div class="perspec" style="--spreaddist: 50px; --scaledist: .9; --vertdist: -10px;">
341 <div class="card">
342 <div class="writing">
343 <div class="topbar">
344 <div class="red"></div>
345 <div class="yellow"></div>
346 <div class="green"></div>
347 </div>
348 <div class="code">
349 <ul></ul>
350 </div>
351 </div>
352 </div>
353 </div>
354 </div>
355 <div class="card-container">
356 <div class="perspec" style="--spreaddist: 25px; --scaledist: .95; --vertdist: -5px;">
357 <div class="card">
358 <div class="writing">
359 <div class="topbar">
360 <div class="red"></div>
361 <div class="yellow"></div>
362 <div class="green"></div>
363 </div>
364 <div class="code">
365 <ul></ul>
366 </div>
367 </div>
368 </div>
369 </div>
370 </div>
371 <div class="card-container">
372 <div class="perspec" style="--spreaddist: 0px; --scaledist: 1; --vertdist: 0px;">
373 <div class="card">
374 <div class="writing">
375 <div class="topbar">
376 <div class="red"></div>
377 <div class="yellow"></div>
378 <div class="green"></div>
379 </div>
380 <div class="code">
381 <ul></ul>
382 </div>
383 </div>
384 </div>
385 </div>
386 </div>
387 </div>
388 </div>
389 <style>
390 body {
391 background: #f5f6fa;
392 -webkit-font-smoothing: antialiased;
393 -moz-osx-font-smoothing: grayscale;
394 }
395
396 body,
397 html {
398 padding: 0;
399 margin: 0;
400 font-family: 'Quicksand', sans-serif;
401 font-weight: 400;
402 overflow: hidden;
403 }
404
405 .writing {
406 width: 320px;
407 height: 200px;
408 background-color: #3f3f3f;
409 border: 1px solid #bbbbbb;
410 border-radius: 6px 6px 4px 4px;
411 position: relative;
412 }
413
414 .writing .topbar {
415 position: absolute;
416 width: 100%;
417 height: 12px;
418 background-color: #f1f1f1;
419 border-top-left-radius: 4px;
420 border-top-right-radius: 4px;
421 }
422
423 .writing .topbar div {
424 height: 6px;
425 width: 6px;
426 border-radius: 50%;
427 margin: 3px;
428 float: left;
429 }
430
431 .writing .topbar div.green {
432 background-color: #60d060;
433 }
434
435 .writing .topbar div.red {
436 background-color: red;
437 }
438
439 .writing .topbar div.yellow {
440 background-color: #e6c015;
441 }
442
443 .writing .code {
444 padding: 15px;
445 }
446
447 .writing .code ul {
448 list-style: none;
449 margin: 0;
450 padding: 0;
451 }
452
453 .writing .code ul li {
454 background-color: #9e9e9e;
455 width: 0;
456 height: 7px;
457 border-radius: 6px;
458 margin: 10px 0;
459 }
460
461 .container {
462 display: -webkit-box;
463 display: -ms-flexbox;
464 display: flex;
465 -webkit-box-align: center;
466 -ms-flex-align: center;
467 align-items: center;
468 -webkit-box-pack: center;
469 -ms-flex-pack: center;
470 justify-content: center;
471 height: 100vh;
472 width: 100%;
473 -webkit-transition: -webkit-transform .5s;
474 transition: -webkit-transform .5s;
475 transition: transform .5s;
476 transition: transform .5s, -webkit-transform .5s;
477 }
478
479 .stack-container {
480 position: relative;
481 -webkit-transition: width 1s, height 1s;
482 transition: width 1s, height 1s;
483 width: 420px;
484 height: 210px;
485 }
486
487 @media screen and (max-width: 930px) {
488 .stack-container {
489 height: 50vh;
490 }
491 }
492
493 .pokeup {
494 -webkit-transition: all .3s ease;
495 transition: all .3s ease;
496 }
497
498 .pokeup:hover {
499 -webkit-transform: translateY(-10px);
500 transform: translateY(-10px);
501 -webkit-transition: .3s ease;
502 transition: .3s ease;
503 }
504
505 .error {
506 width: 50vw;
507 height: 100vh;
508 text-align: left;
509 display: flex;
510 flex-direction: column;
511 justify-content: center;
512 }
513
514 @media screen and (max-width: 930px) {
515 .error {
516 width: 100%;
517 height: 50vh;
518 }
519 }
520
521 .error h2 {
522 margin: 0 0 15px 0;
523 padding: 0px;
524 font-size: 30px;
525 letter-spacing: 0px;
526 }
527
528 .error-inner {
529 padding: 5vw;
530 display: flex;
531 flex-direction: column;
532 justify-content: center;
533 }
534
535 .error-file {
536 border: 1px solid #ddd;
537 padding: 15px;
538 border-radius: 4px;
539 display: block;
540 position: relative;
541 }
542
543 .error-file:before {
544 content: '';
545 position: absolute;
546 top: 0;
547 left: 80px;
548 width: 1px;
549 height: 100%;
550 border-right: 1px dashed #ddd;
551 }
552
553 .error-file code {
554 display: flex;
555 }
556
557 .error-file code strong {
558 flex: 0 0 80px;
559 }
560
561 .error-file code:not(:last-child) {
562 margin-bottom: 10px;
563 padding-bottom: 10px;
564 border-bottom: 1px dashed #ddd;
565 }
566
567 .error-message span {
568 color: #b11212;
569 }
570
571 .button {
572 background: #7789fd;
573 color: #fff;
574 border: 0;
575 padding: 12px 20px;
576 font-size: 16px;
577 font-weight: 600;
578 border-radius: 2px;
579 transition: background 0.3s;
580 cursor: pointer;
581 position: relative;
582 display: inline-block;
583 margin-right: 5px;
584 }
585
586 .button:hover {
587 background-color: #5269fe;
588 }
589
590 .button.success {
591 background: #50a350;
592 cursor: initial;
593 }
594
595 .button.failed {
596 background: #a04646;
597 cursor: initial;
598 }
599
600 .button.loading span {
601 visibility: hidden;
602 opacity: 0;
603 }
604
605 .button.loading:after {
606 content: "";
607 position: absolute;
608 width: 16px;
609 height: 16px;
610 top: 0;
611 left: 0;
612 right: 0;
613 bottom: 0;
614 margin: auto;
615 border: 4px solid transparent;
616 border-top-color: #ffffff;
617 border-radius: 50%;
618 animation: button-loading-spinner 1s ease infinite;
619 }
620
621 .refresh-btn {
622 display: none;
623 }
624
625 @keyframes button-loading-spinner {
626 from {
627 transform: rotate(0turn);
628 }
629
630 to {
631 transform: rotate(1turn);
632 }
633 }
634
635 .perspec {
636 -webkit-perspective: 1000px;
637 perspective: 1000px;
638 }
639
640 .writeLine {
641 -webkit-animation: writeLine .4s linear forwards;
642 animation: writeLine .4s linear forwards;
643 }
644
645 .explode {
646 -webkit-animation: explode .5s ease-in-out forwards;
647 animation: explode .5s ease-in-out forwards;
648 }
649
650 .card {
651 -webkit-animation: tiltcard .5s ease-in-out 1s forwards;
652 animation: tiltcard .5s ease-in-out 1s forwards;
653 position: absolute;
654 }
655
656 @-webkit-keyframes tiltcard {
657 0% {
658 -webkit-transform: rotateY(0deg);
659 transform: rotateY(0deg);
660 }
661
662 100% {
663 -webkit-transform: rotateY(-30deg);
664 transform: rotateY(-30deg);
665 }
666 }
667
668 @keyframes tiltcard {
669 0% {
670 -webkit-transform: rotateY(0deg);
671 transform: rotateY(0deg);
672 }
673
674 100% {
675 -webkit-transform: rotateY(-30deg);
676 transform: rotateY(-30deg);
677 }
678 }
679
680 @-webkit-keyframes explode {
681 0% {
682 -webkit-transform: translate(0, 0) scale(1);
683 transform: translate(0, 0) scale(1);
684 }
685
686 100% {
687 -webkit-transform: translate(var(--spreaddist), var(--vertdist)) scale(var(--scaledist));
688 transform: translate(var(--spreaddist), var(--vertdist)) scale(var(--scaledist));
689 }
690 }
691
692 @keyframes explode {
693 0% {
694 -webkit-transform: translate(0, 0) scale(1);
695 transform: translate(0, 0) scale(1);
696 }
697
698 100% {
699 -webkit-transform: translate(var(--spreaddist), var(--vertdist)) scale(var(--scaledist));
700 transform: translate(var(--spreaddist), var(--vertdist)) scale(var(--scaledist));
701 }
702 }
703
704 @-webkit-keyframes writeLine {
705 0% {
706 width: 0;
707 }
708
709 100% {
710 width: var(--linelength);
711 }
712 }
713
714 @keyframes writeLine {
715 0% {
716 width: 0;
717 }
718
719 100% {
720 width: var(--linelength);
721 }
722 }
723
724 @media screen and (max-width: 1000px) {
725 .container {
726 -webkit-transform: scale(.85);
727 transform: scale(.85);
728 }
729 }
730
731 @media screen and (max-width: 850px) {
732 .container {
733 -webkit-transform: scale(.75);
734 transform: scale(.75);
735 }
736 }
737
738 @media screen and (max-width: 930px) {
739 .container {
740 -ms-flex-wrap: wrap-reverse;
741 flex-wrap: wrap-reverse;
742 }
743 }
744
745 @media screen and (max-width: 370px) {
746 .container {
747 -webkit-transform: scale(.6);
748 transform: scale(.6);
749 }
750 }
751 </style>
752 <script lang="js">
753 const stackContainer = document.querySelector('.stack-container');
754 const cardNodes = document.querySelectorAll('.card-container');
755 const perspecNodes = document.querySelectorAll('.perspec');
756 const perspec = document.querySelector('.perspec');
757 const card = document.querySelector('.card');
758 const recoverBtn = document.querySelector('.recover-btn');
759 const refreshBtn = document.querySelector('.refresh-btn');
760
761 let counter = stackContainer.children.length;
762
763 const onSuccess = () => {
764 recoverBtn.querySelector('span').innerText = 'File Recovered Successfully!';
765 recoverBtn.classList.add("success");
766 recoverBtn.classList.remove("loading");
767 refreshBtn.style.display = 'inline-block';
768 };
769
770 const onFailed = () => {
771 recoverBtn.querySelector('span').innerText = 'Failed Recovering File!';
772 recoverBtn.classList.add("failed");
773 recoverBtn.classList.remove("loading");
774 };
775
776 recoverBtn.addEventListener('click', (evt) => {
777 evt.preventDefault();
778 if (recoverBtn.classList.contains("success") || recoverBtn.classList.contains("loading")) {
779 return;
780 }
781 recoverBtn.classList.add("loading");
782 const recoverUrl = '<?php echo add_query_arg('recover', 1, admin_url('admin.php?page=' . self::$slug . '-filemanager'));?>';
783 fetch(recoverUrl)
784 .then(response => response.json())
785 .then(data => {
786 if (data.success) {
787
788 cardNodes.forEach(function (elem, index) {
789 setTimeout(() => {
790 animateCard(elem);
791
792 if (cardNodes.length === index + 1) {
793
794 onSuccess();
795 }
796
797 }, 500 * (index * 0.5) + index * 50);
798 });
799
800 } else {
801
802 onFailed();
803 }
804 })
805 .catch(() => onFailed());
806 });
807
808 refreshBtn.addEventListener('click', (evt) => {
809 evt.preventDefault();
810
811 if (refreshBtn.classList.contains("loading")) {
812 return;
813 }
814 refreshBtn.classList.add("loading");
815
816 location.reload();
817 });
818
819 function animateCard(elem) {
820 let updown = [800, -800];
821 let randomY = updown[Math.floor(Math.random() * updown.length)];
822 let randomX = Math.floor(Math.random() * 1000) - 1000;
823 elem.style.transform = `translate(${randomX}px, ${randomY}px) rotate(-540deg)`;
824 elem.style.transition = "transform 1s ease, opacity 2s";
825 elem.style.opacity = "0";
826 counter--;
827 if (counter === 0) {
828 stackContainer.style.width = "0";
829 stackContainer.style.height = "0";
830 }
831 }
832
833 //function to generate random number
834 function randomIntFromInterval(min, max) {
835 return Math.floor(Math.random() * (max - min + 1) + min);
836 }
837
838 //after tilt animation, fire the explode animation
839 card.addEventListener('animationend', function () {
840 perspecNodes.forEach(function (elem, index) {
841 elem.classList.add('explode');
842 });
843 });
844
845 //after explode animation do a bunch of stuff
846 perspec.addEventListener('animationend', function (e) {
847 if (e.animationName === 'explode') {
848 cardNodes.forEach(function (elem, index) {
849
850 //add hover animation class
851 elem.classList.add('pokeup');
852
853
854 //generate random number of lines of code between 4 and 10 and add to each card
855 let numLines = randomIntFromInterval(5, 10);
856
857 //loop through the lines and add them to the DOM
858 for (let index = 0; index < numLines; index++) {
859 let lineLength = randomIntFromInterval(25, 97);
860 var node = document.createElement("li");
861 node.classList.add('node-' + index);
862 elem.querySelector('.code ul').appendChild(node).setAttribute('style', '--linelength: ' + lineLength + '%;');
863
864 //draw lines of code 1 by 1
865 if (index == 0) {
866 elem.querySelector('.code ul .node-' + index).classList.add('writeLine');
867 } else {
868 elem.querySelector('.code ul .node-' + (index - 1)).addEventListener('animationend', function (e) {
869 elem.querySelector('.code ul .node-' + index).classList.add('writeLine');
870 });
871 }
872 }
873 });
874 }
875 });
876 </script>
877 <?php
878 }
879 }
880
881 WPIDE_Error_Handler::boot();
882 }