PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.9.1
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.9.1
4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Framework / ErrorHandler.php
wp-staging / Framework Last commit date
Adapter 2 months ago Analytics 1 day ago Assets 1 day ago BackgroundProcessing 1 week ago CloningProcess 1 day ago Collection 3 years ago Command 5 years ago Component 8 months ago DI 6 months ago Database 1 week ago DependencyResolver 2 years ago Exceptions 2 years ago Facades 2 months ago Filesystem 1 day ago Interfaces 5 years ago Job 1 week ago Language 3 months ago Logger 1 month ago Mails 3 months ago Network 1 month ago Newsfeed 4 months ago Notices 1 week ago Performance 2 months ago Permalinks 11 months ago Queue 4 months ago Rest 2 months ago Security 1 month ago Settings 1 month ago TemplateEngine 6 months ago ThirdParty 5 months ago Traits 1 week ago Upgrade 1 month ago Utils 1 week ago AnalyticsServiceProvider.php 1 day ago AssetServiceProvider.php 1 year ago CommonServiceProvider.php 1 month ago ErrorHandler.php 1 week ago NoticeServiceProvider.php 11 months ago SettingsServiceProvider.php 2 months ago SiteInfo.php 5 months ago Url.php 4 months ago
ErrorHandler.php
164 lines
1 <?php
2
3 namespace WPStaging\Framework;
4
5 use WPStaging\Core\WPStaging;
6 use WPStaging\Framework\Job\JobTransientCache;
7 use WPStaging\Framework\Job\ProcessLock;
8 use WPStaging\Framework\Logger\SseEventCache;
9
10 /**
11 * @package WPStaging\Framework
12 */
13 class ErrorHandler
14 {
15 /** @var string */
16 const ERROR_FILE_EXTENSION = '.wpstgerror';
17
18 public function registerShutdownHandler()
19 {
20 register_shutdown_function([$this, 'shutdownHandler']);
21 }
22
23 public function shutdownHandler()
24 {
25 if (!defined('WPSTG_REQUEST')) {
26 return;
27 }
28
29 if (!defined('WPSTG_UPLOADS_DIR')) {
30 return;
31 }
32
33 /**
34 * Requests for which to check for memory exhaustion
35 * Using hardcoded values below to avoid loading all classes
36 * @var array $wpStagingRequests
37 */
38 $wpStagingRequests = [
39 'wpstg_backup', // @see WPStaging\Backup\Ajax\Backup::WPSTG_REQUEST
40 'wpstg_restore', // @see WPStaging\Backup\Ajax\Restore::WPSTG_REQUEST
41 'wpstg_cloning', // @see WPStaging\Backend\Modules\Jobs\Cloning::WPSTG_REQUEST,
42 'wpstg_remote_sync_pull', // @see WPStaging\Pro\RemoteSync\BackgroundProcessing\PreparePull::WPSTG_REQUEST
43 'wpstg_staging_create', // @see WPStaging\Staging\Ajax\Create::WPSTG_REQUEST
44 'wpstg_staging_update', // @see WPStaging\Staging\Ajax\Update::WPSTG_REQUEST
45 'wpstg_staging_reset', // @see WPStaging\Staging\Ajax\Reset::WPSTG_REQUEST
46 'wpstg_staging_push', // @see WPStaging\Pro\Push\Ajax\Push::WPSTG_REQUEST
47 ];
48
49 $wpStagingRequest = WPSTG_REQUEST;
50 if (!in_array($wpStagingRequest, $wpStagingRequests, true)) {
51 return;
52 }
53
54 $error = error_get_last();
55 if (!is_array($error)) {
56 return;
57 }
58
59 if ($error['type'] !== E_ERROR) {
60 return;
61 }
62
63 preg_match('/Allowed memory size of (\d+) bytes exhausted \(tried to allocate (\d+) bytes\)/', $error['message'], $data);
64 if (!is_array($data) || count($data) !== 3) {
65 $data['time'] = date('Y/m/d H:i:s', time()); // @see WPStaging\Core\Utils\Logger::LOG_DATETIME_FORMAT, use hardcoded value to avoid loading class
66 $this->logSseEvent($data, false);
67 $this->releaseProcessLock();
68 return;
69 }
70
71 // Temporary file to store the error message
72 $errorTmpFile = WPSTG_UPLOADS_DIR . $wpStagingRequest . self::ERROR_FILE_EXTENSION;
73
74 $fileHandler = fopen($errorTmpFile, 'w');
75
76 $data = [
77 'memoryUsage' => memory_get_usage(true),
78 'peakMemoryUsage' => memory_get_peak_usage(true),
79 'phpMemoryLimit' => ini_get('memory_limit'),
80 'wpMemoryLimit' => defined('WP_MEMORY_LIMIT') ? WP_MEMORY_LIMIT : '',
81 'allowedMemoryLimit' => $data[1],
82 'exhaustedMemorySize' => $data[2],
83 'time' => date('Y/m/d H:i:s', time()), // @see WPStaging\Core\Utils\Logger::LOG_DATETIME_FORMAT, use hardcoded value to avoid loading class
84 ];
85
86 if (is_resource($fileHandler)) {
87 fwrite($fileHandler, json_encode($data));
88 fclose($fileHandler);
89 }
90
91 $this->logSseEvent($data);
92 $this->releaseProcessLock();
93 }
94
95 /**
96 * Release the process lock when a fatal error terminates PHP before the job
97 * can clean up. Without this, the next request stays blocked by the stale
98 * lock until ProcessLock's 120s timeout elapses.
99 *
100 * @return void
101 */
102 private function releaseProcessLock()
103 {
104 try {
105 WPStaging::make(ProcessLock::class)->unlockProcess();
106 } catch (\Throwable $e) {
107 // No-op: shutdown handler must not throw.
108 }
109 }
110
111 private function logSseEvent(array $data, bool $isMemoryExhaust = true)
112 {
113 /**
114 * @var JobTransientCache $jobTransientCache
115 */
116 $jobTransientCache = WPStaging::make(JobTransientCache::class);
117
118 $jobId = $jobTransientCache->getJobId();
119 if (empty($jobId)) {
120 return;
121 }
122
123 if ($isMemoryExhaust) {
124 $exhaustedMemorySize = isset($data['exhaustedMemorySize']) ? (int)$data['exhaustedMemorySize'] : 0;
125 $memoryUsage = isset($data['memoryUsage']) ? (int)$data['memoryUsage'] : 0;
126 $peakMemoryUsage = isset($data['peakMemoryUsage']) ? (int)$data['peakMemoryUsage'] : 0;
127 $allowedMemoryLimit = isset($data['allowedMemoryLimit']) ? (int)$data['allowedMemoryLimit'] : 0;
128 $phpMemoryLimit = isset($data['phpMemoryLimit']) ? $data['phpMemoryLimit'] : ini_get('memory_limit');
129 $wpMemoryLimit = isset($data['wpMemoryLimit']) ? $data['wpMemoryLimit'] : (defined('WP_MEMORY_LIMIT') ? WP_MEMORY_LIMIT : '');
130
131 $message = sprintf(
132 esc_html__('Memory exhaust issue is detected during the process. Error occurred when allocating more %s on top of current usage of %s. Peak memory usage: %s, Allowed memory limit: %s, PHP memory limit: %s, WP memory limit: %s', 'wp-staging'),
133 size_format($exhaustedMemorySize),
134 size_format($memoryUsage),
135 size_format($peakMemoryUsage),
136 size_format($allowedMemoryLimit),
137 $phpMemoryLimit,
138 $wpMemoryLimit
139 );
140 } else {
141 $message = sprintf(
142 esc_html__('Job failed due to a fatal error! Error data: %s', 'wp-staging'),
143 esc_html(print_r($data, true))
144 );
145 }
146
147 $data['jobId'] = $jobId;
148 $data['message'] = $message;
149
150 /**
151 * @var SseEventCache $sseEventCache
152 */
153 $sseEventCache = WPStaging::make(SseEventCache::class);
154 $sseEventCache->setJobId($jobId);
155 $sseEventCache->load();
156 $sseEventCache->push([
157 'type' => $isMemoryExhaust ? SseEventCache::EVENT_TYPE_MEMORY_EXHAUST : SseEventCache::EVENT_TYPE_FATAL_ERROR,
158 'data' => $data,
159 ]);
160
161 $jobTransientCache->failJob('', $message);
162 }
163 }
164