Completed
Branch master (f114e9)
by
unknown
10:01 queued 03:45
created
core/services/session/SessionStartHandler.php 2 patches
Indentation   +383 added lines, -383 removed lines patch added patch discarded remove patch
@@ -29,387 +29,387 @@
 block discarded – undo
29 29
  */
30 30
 class SessionStartHandler
31 31
 {
32
-    const OPTION_NAME_SESSION_SAVE_HANDLER_STATUS = 'ee_session_save_handler_status';
33
-
34
-    const REQUEST_PARAM_RETRY_SESSION             = 'ee_retry_session';
35
-
36
-    const SESSION_SAVE_HANDLER_STATUS_FAILED      = 'session_save_handler_failed';
37
-
38
-    const SESSION_SAVE_HANDLER_STATUS_SUCCESS     = 'session_save_handler_success';
39
-
40
-    const SESSION_SAVE_HANDLER_STATUS_UNKNOWN     = 'session_save_handler_untested';
41
-
42
-
43
-    protected RequestInterface $request;
44
-
45
-
46
-    /**
47
-     * StartSession constructor.
48
-     *
49
-     * @param RequestInterface $request
50
-     */
51
-    public function __construct(RequestInterface $request)
52
-    {
53
-        $this->request = $request;
54
-    }
55
-
56
-
57
-    /**
58
-     * Check if a custom session save handler is in play
59
-     * and attempt to start the PHP session
60
-     *
61
-     * @since 4.9.68.p
62
-     */
63
-    public function startSession(): int
64
-    {
65
-        // check that session has started
66
-        if (session_id() === '') {
67
-            // clear any previous error
68
-            error_clear_last();
69
-
70
-            // convert warnings to ErrorException so we can catch them
71
-            $previous_handler = set_error_handler([$this, 'customErrorHandler'], E_WARNING);
72
-
73
-            try {
74
-                // starts a new session if one doesn't already exist, or re-initiates an existing one
75
-                if ($this->hasKnownCustomSessionSaveHandler()) {
76
-                    $this->checkCustomSessionSaveHandler();
77
-                } else {
78
-                    $this->verifySessionSavePath();
79
-                    $this->sessionStart();
80
-                }
81
-            } catch (Throwable $error) {
82
-                error_log(
83
-                    sprintf(
84
-                        '[SessionStartHandler] session_start() warning: %s in %s:%s',
85
-                        $error->getMessage(),
86
-                        $error->getFile(),
87
-                        $error->getLine()
88
-                    )
89
-                );
90
-                $this->displaySessionErrorNotice(
91
-                    $error->getMessage(),
92
-                    $error->getFile(),
93
-                    __FUNCTION__,
94
-                    $error->getLine()
95
-                );
96
-            } finally {
97
-                $this->restorePreviousErrorHandler($previous_handler);
98
-            }
99
-        }
100
-        return session_status();
101
-    }
102
-
103
-
104
-    /**
105
-     * @return void
106
-     * @throws Throwable
107
-     * @since 5.0.46
108
-     */
109
-    public function sessionStart(): void
110
-    {
111
-        session_start();
112
-        session_write_close();
113
-    }
114
-
115
-
116
-    /**
117
-     * @return void
118
-     * @throws ErrorException
119
-     * @since 5.0.46
120
-     */
121
-    private function verifySessionSavePath(): void
122
-    {
123
-        $session_save_path = session_save_path() ?: '';
124
-        // Normalize "N;/path" style values
125
-        if (strpos($session_save_path, ';') !== false) {
126
-            $parts             = explode(';', $session_save_path);
127
-            $session_save_path = end($parts);
128
-        }
129
-        $session_save_path = trim((string) $session_save_path);
130
-        if ($session_save_path === '') {
131
-            // fall back to a sane temp dir if PHP reports no explicit session_save_path
132
-            $session_save_path = sys_get_temp_dir();
133
-        }
134
-
135
-        // use the real filesystem path
136
-        $real_path = realpath($session_save_path) ?: $session_save_path;
137
-        if (! is_dir($real_path) || ! is_writable($real_path)) {
138
-            throw new ErrorException(
139
-                sprintf(
140
-                    esc_html__('Invalid or missing session save path: %s', 'event_espresso'),
141
-                    $session_save_path
142
-                ),
143
-                0,
144
-                E_WARNING,
145
-                __FILE__,
146
-                __LINE__
147
-            );
148
-        }
149
-    }
150
-
151
-
152
-    /**
153
-     * Returns `true` if the 'session.save_handler' ini setting matches a known custom handler
154
-     *
155
-     * @return bool
156
-     * @since 4.9.68.p
157
-     */
158
-    private function hasKnownCustomSessionSaveHandler(): bool
159
-    {
160
-        return strtolower((string) ini_get('session.save_handler')) === 'user';
161
-    }
162
-
163
-
164
-    /**
165
-     * Attempt to start the PHP session when a custom Session Save Handler is known to be set.
166
-     *
167
-     * @throws ErrorException
168
-     * @throws Throwable
169
-     * @since 4.9.68.p
170
-     */
171
-    private function checkCustomSessionSaveHandler(): void
172
-    {
173
-        // If we've already successfully tested the session save handler
174
-        // on a previous request then just start the session
175
-        if ($this->sessionSaveHandlerIsValid()) {
176
-            $this->sessionStart();
177
-            return;
178
-        }
179
-        // If not, then attempt to deal with any errors,
180
-        // otherwise, try to hobble along without the session
181
-        if (! $this->handleSessionSaveHandlerErrors()) {
182
-            return;
183
-        }
184
-        // there is no record of a fatal error while trying to start the session
185
-        // so let's see if there's a custom session save handler. Proceed with caution
186
-        if ($this->initializeSessionSaveHandlerStatus() === false) {
187
-            throw new ErrorException(
188
-                esc_html__('Failed to initialize session save handler status', 'event_espresso'),
189
-                0,
190
-                E_WARNING,
191
-                __FILE__,
192
-                __LINE__
193
-            );
194
-        }
195
-        // hold your breath, the custom session save handler might cause a fatal here...
196
-        $this->sessionStart();
197
-        // phew! we made it! the custom session handler is a-ok
198
-        if ($this->setSessionSaveHandlerStatusToValid() === false) {
199
-            throw new ErrorException(
200
-                esc_html__('Failed to set session save handler status to valid', 'event_espresso'),
201
-                0,
202
-                E_WARNING,
203
-                __FILE__,
204
-                __LINE__
205
-            );
206
-        }
207
-    }
208
-
209
-
210
-    /**
211
-     * retrieves the value for the 'ee_session_save_handler_status' WP option.
212
-     * default value = 'session_save_handler_untested'
213
-     *
214
-     * @return string
215
-     * @since 4.9.68.p
216
-     */
217
-    private function getSessionSaveHandlerStatus(): string
218
-    {
219
-        return get_option(
220
-            SessionStartHandler::OPTION_NAME_SESSION_SAVE_HANDLER_STATUS,
221
-            SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_UNKNOWN
222
-        );
223
-    }
224
-
225
-
226
-    /**
227
-     * Sets the 'ee_session_save_handler_status' WP option value to 'session_save_handler_failed'
228
-     * which can then be upgraded is everything works correctly
229
-     *
230
-     * @return bool
231
-     * @since 4.9.68.p
232
-     */
233
-    private function initializeSessionSaveHandlerStatus(): bool
234
-    {
235
-        return update_option(
236
-            SessionStartHandler::OPTION_NAME_SESSION_SAVE_HANDLER_STATUS,
237
-            SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_FAILED
238
-        );
239
-    }
240
-
241
-
242
-    /**
243
-     * Sets the 'ee_session_save_handler_status' WP option value to 'session_save_handler_success'
244
-     *
245
-     * @return bool
246
-     * @since 4.9.68.p
247
-     */
248
-    private function setSessionSaveHandlerStatusToValid(): bool
249
-    {
250
-        return update_option(
251
-            SessionStartHandler::OPTION_NAME_SESSION_SAVE_HANDLER_STATUS,
252
-            SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_SUCCESS
253
-        );
254
-    }
255
-
256
-
257
-    /**
258
-     * Sets the 'ee_session_save_handler_status' WP option value to 'session_save_handler_untested'
259
-     *
260
-     * @return bool
261
-     * @since 4.9.68.p
262
-     */
263
-    private function resetSessionSaveHandlerStatus(): bool
264
-    {
265
-        return update_option(
266
-            SessionStartHandler::OPTION_NAME_SESSION_SAVE_HANDLER_STATUS,
267
-            SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_UNKNOWN
268
-        );
269
-    }
270
-
271
-
272
-    /**
273
-     * Returns `true` if the 'ee_session_save_handler_status' WP option value
274
-     * is equal to 'session_save_handler_success'
275
-     *
276
-     * @return bool
277
-     * @since 4.9.68.p
278
-     */
279
-    private function sessionSaveHandlerIsValid(): bool
280
-    {
281
-        return $this->getSessionSaveHandlerStatus() === SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_SUCCESS;
282
-    }
283
-
284
-
285
-    /**
286
-     * Returns `true` if the 'ee_session_save_handler_status' WP option value
287
-     * is equal to 'session_save_handler_failed'
288
-     *
289
-     * @return bool
290
-     * @since 4.9.68.p
291
-     */
292
-    private function sessionSaveHandlerFailed(): bool
293
-    {
294
-        return $this->getSessionSaveHandlerStatus() === SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_FAILED;
295
-    }
296
-
297
-
298
-    /**
299
-     * @param int    $severity
300
-     * @param string $message
301
-     * @param string $file
302
-     * @param int    $line
303
-     * @return bool
304
-     * @throws ErrorException
305
-     * @since 5.0.46
306
-     */
307
-    public function customErrorHandler(int $severity, string $message, string $file, int $line): bool
308
-    {
309
-        // Only convert warnings we care about
310
-        if (($severity & E_WARNING) === E_WARNING) {
311
-            throw new ErrorException($message, 0, $severity, $file, $line);
312
-        }
313
-        // fallback to PHP's normal handler for other severities
314
-        return false;
315
-    }
316
-
317
-
318
-    /**
319
-     * @param callable|null $previous_handler
320
-     * @return void
321
-     * @since 5.0.46
322
-     */
323
-    private function restorePreviousErrorHandler(?callable $previous_handler): void
324
-    {
325
-        if ($previous_handler !== null) {
326
-            set_error_handler($previous_handler);
327
-        } else {
328
-            restore_error_handler();
329
-        }
330
-    }
331
-
332
-
333
-    /**
334
-     * Returns `true` if no errors were detected with the session save handler,
335
-     * otherwise attempts to work notify the appropriate authorities
336
-     * with a suggestion for how to fix the issue, and returns `false`.
337
-     *
338
-     * @return bool
339
-     * @throws ErrorException
340
-     * @throws Throwable
341
-     * @since 4.9.68.p
342
-     */
343
-    private function handleSessionSaveHandlerErrors(): bool
344
-    {
345
-        // Check if we had a fatal error last time while trying to start the session
346
-        if ($this->sessionSaveHandlerFailed()) {
347
-            // apparently, last time we tried using the custom session save handler there was a fatal
348
-            if ($this->request->requestParamIsSet(SessionStartHandler::REQUEST_PARAM_RETRY_SESSION)) {
349
-                if ($this->resetSessionSaveHandlerStatus() === false) {
350
-                    throw new ErrorException(
351
-                        esc_html__('Failed to reset session save handler status', 'event_espresso'),
352
-                        0,
353
-                        E_WARNING,
354
-                        __FILE__,
355
-                        __LINE__
356
-                    );
357
-                }
358
-                // remove "ee_retry_session", otherwise if the problem still isn't fixed,
359
-                // we'll just keep getting the fatal error over and over.
360
-                // Better to remove it and redirect, and try on the next request
361
-                EEH_URL::safeRedirectAndExit(
362
-                    remove_query_arg(
363
-                        [SessionStartHandler::REQUEST_PARAM_RETRY_SESSION],
364
-                        EEH_URL::current_url()
365
-                    )
366
-                );
367
-            }
368
-            // so the session is broken, don't try it again,
369
-            // just show a message to users that can fix it
370
-            $this->displaySessionSaveHandlerErrorNotice();
371
-            return false;
372
-        }
373
-        return true;
374
-    }
375
-
376
-
377
-    /**
378
-     * @since 4.9.68.p
379
-     */
380
-    private function displaySessionSaveHandlerErrorNotice(): void
381
-    {
382
-        $retry_session_url = add_query_arg(
383
-            [SessionStartHandler::REQUEST_PARAM_RETRY_SESSION => true],
384
-            EEH_URL::current_url()
385
-        );
386
-        $this->displaySessionErrorNotice(
387
-            sprintf(
388
-                esc_html__(
389
-                    'It appears there was a fatal error while starting the session, so Event Espresso is not able to process registrations normally. Some hosting companies, like Pantheon, require an extra plugin for Event Espresso to work. Please install the %1$sWordPress Native PHP Sessions plugin%2$s, then %3$sclick here to check if the problem is resolved.%2$s',
390
-                    'event_espresso'
391
-                ),
392
-                '<a href="https://wordpress.org/plugins/wp-native-php-sessions/">',
393
-                '</a>',
394
-                '<a href="' . $retry_session_url . '">'
395
-            ),
396
-            __FILE__,
397
-            __FUNCTION__,
398
-            __LINE__
399
-        );
400
-    }
401
-
402
-
403
-    /**
404
-     * Generates an EE_Error notice regarding the current session woes
405
-     * but only if the current user is an admin with permission to 'install_plugins'.
406
-     *
407
-     * @since 5.0.46
408
-     */
409
-    private function displaySessionErrorNotice(string $message, string $file, string $function, int $line): void
410
-    {
411
-        if (current_user_can('install_plugins')) {
412
-            EE_Error::add_error($message, $file, $function, $line);
413
-        }
414
-    }
32
+	const OPTION_NAME_SESSION_SAVE_HANDLER_STATUS = 'ee_session_save_handler_status';
33
+
34
+	const REQUEST_PARAM_RETRY_SESSION             = 'ee_retry_session';
35
+
36
+	const SESSION_SAVE_HANDLER_STATUS_FAILED      = 'session_save_handler_failed';
37
+
38
+	const SESSION_SAVE_HANDLER_STATUS_SUCCESS     = 'session_save_handler_success';
39
+
40
+	const SESSION_SAVE_HANDLER_STATUS_UNKNOWN     = 'session_save_handler_untested';
41
+
42
+
43
+	protected RequestInterface $request;
44
+
45
+
46
+	/**
47
+	 * StartSession constructor.
48
+	 *
49
+	 * @param RequestInterface $request
50
+	 */
51
+	public function __construct(RequestInterface $request)
52
+	{
53
+		$this->request = $request;
54
+	}
55
+
56
+
57
+	/**
58
+	 * Check if a custom session save handler is in play
59
+	 * and attempt to start the PHP session
60
+	 *
61
+	 * @since 4.9.68.p
62
+	 */
63
+	public function startSession(): int
64
+	{
65
+		// check that session has started
66
+		if (session_id() === '') {
67
+			// clear any previous error
68
+			error_clear_last();
69
+
70
+			// convert warnings to ErrorException so we can catch them
71
+			$previous_handler = set_error_handler([$this, 'customErrorHandler'], E_WARNING);
72
+
73
+			try {
74
+				// starts a new session if one doesn't already exist, or re-initiates an existing one
75
+				if ($this->hasKnownCustomSessionSaveHandler()) {
76
+					$this->checkCustomSessionSaveHandler();
77
+				} else {
78
+					$this->verifySessionSavePath();
79
+					$this->sessionStart();
80
+				}
81
+			} catch (Throwable $error) {
82
+				error_log(
83
+					sprintf(
84
+						'[SessionStartHandler] session_start() warning: %s in %s:%s',
85
+						$error->getMessage(),
86
+						$error->getFile(),
87
+						$error->getLine()
88
+					)
89
+				);
90
+				$this->displaySessionErrorNotice(
91
+					$error->getMessage(),
92
+					$error->getFile(),
93
+					__FUNCTION__,
94
+					$error->getLine()
95
+				);
96
+			} finally {
97
+				$this->restorePreviousErrorHandler($previous_handler);
98
+			}
99
+		}
100
+		return session_status();
101
+	}
102
+
103
+
104
+	/**
105
+	 * @return void
106
+	 * @throws Throwable
107
+	 * @since 5.0.46
108
+	 */
109
+	public function sessionStart(): void
110
+	{
111
+		session_start();
112
+		session_write_close();
113
+	}
114
+
115
+
116
+	/**
117
+	 * @return void
118
+	 * @throws ErrorException
119
+	 * @since 5.0.46
120
+	 */
121
+	private function verifySessionSavePath(): void
122
+	{
123
+		$session_save_path = session_save_path() ?: '';
124
+		// Normalize "N;/path" style values
125
+		if (strpos($session_save_path, ';') !== false) {
126
+			$parts             = explode(';', $session_save_path);
127
+			$session_save_path = end($parts);
128
+		}
129
+		$session_save_path = trim((string) $session_save_path);
130
+		if ($session_save_path === '') {
131
+			// fall back to a sane temp dir if PHP reports no explicit session_save_path
132
+			$session_save_path = sys_get_temp_dir();
133
+		}
134
+
135
+		// use the real filesystem path
136
+		$real_path = realpath($session_save_path) ?: $session_save_path;
137
+		if (! is_dir($real_path) || ! is_writable($real_path)) {
138
+			throw new ErrorException(
139
+				sprintf(
140
+					esc_html__('Invalid or missing session save path: %s', 'event_espresso'),
141
+					$session_save_path
142
+				),
143
+				0,
144
+				E_WARNING,
145
+				__FILE__,
146
+				__LINE__
147
+			);
148
+		}
149
+	}
150
+
151
+
152
+	/**
153
+	 * Returns `true` if the 'session.save_handler' ini setting matches a known custom handler
154
+	 *
155
+	 * @return bool
156
+	 * @since 4.9.68.p
157
+	 */
158
+	private function hasKnownCustomSessionSaveHandler(): bool
159
+	{
160
+		return strtolower((string) ini_get('session.save_handler')) === 'user';
161
+	}
162
+
163
+
164
+	/**
165
+	 * Attempt to start the PHP session when a custom Session Save Handler is known to be set.
166
+	 *
167
+	 * @throws ErrorException
168
+	 * @throws Throwable
169
+	 * @since 4.9.68.p
170
+	 */
171
+	private function checkCustomSessionSaveHandler(): void
172
+	{
173
+		// If we've already successfully tested the session save handler
174
+		// on a previous request then just start the session
175
+		if ($this->sessionSaveHandlerIsValid()) {
176
+			$this->sessionStart();
177
+			return;
178
+		}
179
+		// If not, then attempt to deal with any errors,
180
+		// otherwise, try to hobble along without the session
181
+		if (! $this->handleSessionSaveHandlerErrors()) {
182
+			return;
183
+		}
184
+		// there is no record of a fatal error while trying to start the session
185
+		// so let's see if there's a custom session save handler. Proceed with caution
186
+		if ($this->initializeSessionSaveHandlerStatus() === false) {
187
+			throw new ErrorException(
188
+				esc_html__('Failed to initialize session save handler status', 'event_espresso'),
189
+				0,
190
+				E_WARNING,
191
+				__FILE__,
192
+				__LINE__
193
+			);
194
+		}
195
+		// hold your breath, the custom session save handler might cause a fatal here...
196
+		$this->sessionStart();
197
+		// phew! we made it! the custom session handler is a-ok
198
+		if ($this->setSessionSaveHandlerStatusToValid() === false) {
199
+			throw new ErrorException(
200
+				esc_html__('Failed to set session save handler status to valid', 'event_espresso'),
201
+				0,
202
+				E_WARNING,
203
+				__FILE__,
204
+				__LINE__
205
+			);
206
+		}
207
+	}
208
+
209
+
210
+	/**
211
+	 * retrieves the value for the 'ee_session_save_handler_status' WP option.
212
+	 * default value = 'session_save_handler_untested'
213
+	 *
214
+	 * @return string
215
+	 * @since 4.9.68.p
216
+	 */
217
+	private function getSessionSaveHandlerStatus(): string
218
+	{
219
+		return get_option(
220
+			SessionStartHandler::OPTION_NAME_SESSION_SAVE_HANDLER_STATUS,
221
+			SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_UNKNOWN
222
+		);
223
+	}
224
+
225
+
226
+	/**
227
+	 * Sets the 'ee_session_save_handler_status' WP option value to 'session_save_handler_failed'
228
+	 * which can then be upgraded is everything works correctly
229
+	 *
230
+	 * @return bool
231
+	 * @since 4.9.68.p
232
+	 */
233
+	private function initializeSessionSaveHandlerStatus(): bool
234
+	{
235
+		return update_option(
236
+			SessionStartHandler::OPTION_NAME_SESSION_SAVE_HANDLER_STATUS,
237
+			SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_FAILED
238
+		);
239
+	}
240
+
241
+
242
+	/**
243
+	 * Sets the 'ee_session_save_handler_status' WP option value to 'session_save_handler_success'
244
+	 *
245
+	 * @return bool
246
+	 * @since 4.9.68.p
247
+	 */
248
+	private function setSessionSaveHandlerStatusToValid(): bool
249
+	{
250
+		return update_option(
251
+			SessionStartHandler::OPTION_NAME_SESSION_SAVE_HANDLER_STATUS,
252
+			SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_SUCCESS
253
+		);
254
+	}
255
+
256
+
257
+	/**
258
+	 * Sets the 'ee_session_save_handler_status' WP option value to 'session_save_handler_untested'
259
+	 *
260
+	 * @return bool
261
+	 * @since 4.9.68.p
262
+	 */
263
+	private function resetSessionSaveHandlerStatus(): bool
264
+	{
265
+		return update_option(
266
+			SessionStartHandler::OPTION_NAME_SESSION_SAVE_HANDLER_STATUS,
267
+			SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_UNKNOWN
268
+		);
269
+	}
270
+
271
+
272
+	/**
273
+	 * Returns `true` if the 'ee_session_save_handler_status' WP option value
274
+	 * is equal to 'session_save_handler_success'
275
+	 *
276
+	 * @return bool
277
+	 * @since 4.9.68.p
278
+	 */
279
+	private function sessionSaveHandlerIsValid(): bool
280
+	{
281
+		return $this->getSessionSaveHandlerStatus() === SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_SUCCESS;
282
+	}
283
+
284
+
285
+	/**
286
+	 * Returns `true` if the 'ee_session_save_handler_status' WP option value
287
+	 * is equal to 'session_save_handler_failed'
288
+	 *
289
+	 * @return bool
290
+	 * @since 4.9.68.p
291
+	 */
292
+	private function sessionSaveHandlerFailed(): bool
293
+	{
294
+		return $this->getSessionSaveHandlerStatus() === SessionStartHandler::SESSION_SAVE_HANDLER_STATUS_FAILED;
295
+	}
296
+
297
+
298
+	/**
299
+	 * @param int    $severity
300
+	 * @param string $message
301
+	 * @param string $file
302
+	 * @param int    $line
303
+	 * @return bool
304
+	 * @throws ErrorException
305
+	 * @since 5.0.46
306
+	 */
307
+	public function customErrorHandler(int $severity, string $message, string $file, int $line): bool
308
+	{
309
+		// Only convert warnings we care about
310
+		if (($severity & E_WARNING) === E_WARNING) {
311
+			throw new ErrorException($message, 0, $severity, $file, $line);
312
+		}
313
+		// fallback to PHP's normal handler for other severities
314
+		return false;
315
+	}
316
+
317
+
318
+	/**
319
+	 * @param callable|null $previous_handler
320
+	 * @return void
321
+	 * @since 5.0.46
322
+	 */
323
+	private function restorePreviousErrorHandler(?callable $previous_handler): void
324
+	{
325
+		if ($previous_handler !== null) {
326
+			set_error_handler($previous_handler);
327
+		} else {
328
+			restore_error_handler();
329
+		}
330
+	}
331
+
332
+
333
+	/**
334
+	 * Returns `true` if no errors were detected with the session save handler,
335
+	 * otherwise attempts to work notify the appropriate authorities
336
+	 * with a suggestion for how to fix the issue, and returns `false`.
337
+	 *
338
+	 * @return bool
339
+	 * @throws ErrorException
340
+	 * @throws Throwable
341
+	 * @since 4.9.68.p
342
+	 */
343
+	private function handleSessionSaveHandlerErrors(): bool
344
+	{
345
+		// Check if we had a fatal error last time while trying to start the session
346
+		if ($this->sessionSaveHandlerFailed()) {
347
+			// apparently, last time we tried using the custom session save handler there was a fatal
348
+			if ($this->request->requestParamIsSet(SessionStartHandler::REQUEST_PARAM_RETRY_SESSION)) {
349
+				if ($this->resetSessionSaveHandlerStatus() === false) {
350
+					throw new ErrorException(
351
+						esc_html__('Failed to reset session save handler status', 'event_espresso'),
352
+						0,
353
+						E_WARNING,
354
+						__FILE__,
355
+						__LINE__
356
+					);
357
+				}
358
+				// remove "ee_retry_session", otherwise if the problem still isn't fixed,
359
+				// we'll just keep getting the fatal error over and over.
360
+				// Better to remove it and redirect, and try on the next request
361
+				EEH_URL::safeRedirectAndExit(
362
+					remove_query_arg(
363
+						[SessionStartHandler::REQUEST_PARAM_RETRY_SESSION],
364
+						EEH_URL::current_url()
365
+					)
366
+				);
367
+			}
368
+			// so the session is broken, don't try it again,
369
+			// just show a message to users that can fix it
370
+			$this->displaySessionSaveHandlerErrorNotice();
371
+			return false;
372
+		}
373
+		return true;
374
+	}
375
+
376
+
377
+	/**
378
+	 * @since 4.9.68.p
379
+	 */
380
+	private function displaySessionSaveHandlerErrorNotice(): void
381
+	{
382
+		$retry_session_url = add_query_arg(
383
+			[SessionStartHandler::REQUEST_PARAM_RETRY_SESSION => true],
384
+			EEH_URL::current_url()
385
+		);
386
+		$this->displaySessionErrorNotice(
387
+			sprintf(
388
+				esc_html__(
389
+					'It appears there was a fatal error while starting the session, so Event Espresso is not able to process registrations normally. Some hosting companies, like Pantheon, require an extra plugin for Event Espresso to work. Please install the %1$sWordPress Native PHP Sessions plugin%2$s, then %3$sclick here to check if the problem is resolved.%2$s',
390
+					'event_espresso'
391
+				),
392
+				'<a href="https://wordpress.org/plugins/wp-native-php-sessions/">',
393
+				'</a>',
394
+				'<a href="' . $retry_session_url . '">'
395
+			),
396
+			__FILE__,
397
+			__FUNCTION__,
398
+			__LINE__
399
+		);
400
+	}
401
+
402
+
403
+	/**
404
+	 * Generates an EE_Error notice regarding the current session woes
405
+	 * but only if the current user is an admin with permission to 'install_plugins'.
406
+	 *
407
+	 * @since 5.0.46
408
+	 */
409
+	private function displaySessionErrorNotice(string $message, string $file, string $function, int $line): void
410
+	{
411
+		if (current_user_can('install_plugins')) {
412
+			EE_Error::add_error($message, $file, $function, $line);
413
+		}
414
+	}
415 415
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
 
135 135
         // use the real filesystem path
136 136
         $real_path = realpath($session_save_path) ?: $session_save_path;
137
-        if (! is_dir($real_path) || ! is_writable($real_path)) {
137
+        if ( ! is_dir($real_path) || ! is_writable($real_path)) {
138 138
             throw new ErrorException(
139 139
                 sprintf(
140 140
                     esc_html__('Invalid or missing session save path: %s', 'event_espresso'),
@@ -178,7 +178,7 @@  discard block
 block discarded – undo
178 178
         }
179 179
         // If not, then attempt to deal with any errors,
180 180
         // otherwise, try to hobble along without the session
181
-        if (! $this->handleSessionSaveHandlerErrors()) {
181
+        if ( ! $this->handleSessionSaveHandlerErrors()) {
182 182
             return;
183 183
         }
184 184
         // there is no record of a fatal error while trying to start the session
@@ -391,7 +391,7 @@  discard block
 block discarded – undo
391 391
                 ),
392 392
                 '<a href="https://wordpress.org/plugins/wp-native-php-sessions/">',
393 393
                 '</a>',
394
-                '<a href="' . $retry_session_url . '">'
394
+                '<a href="'.$retry_session_url.'">'
395 395
             ),
396 396
             __FILE__,
397 397
             __FUNCTION__,
Please login to merge, or discard this patch.
messages/message_type/newsletter/EE_Newsletter_message_type.class.php 2 patches
Indentation   +185 added lines, -185 removed lines patch added patch discarded remove patch
@@ -12,189 +12,189 @@
 block discarded – undo
12 12
  */
13 13
 class EE_Newsletter_message_type extends EE_message_type
14 14
 {
15
-    public function __construct()
16
-    {
17
-        $this->name              = 'newsletter';
18
-        $this->description       = esc_html__(
19
-            'Batch message type messages are triggered manually by the admin for sending notifications to a selected group of recipients. This should only be used for more general notification type messages that contain information specific for the recipients. For "newsletter" type messages we recommend using an email list service like MailChimp, because sending non-related mail-outs to contacts increases the risk of your site domain getting added to spam lists, which will prevent messages getting to users.',
20
-            'event_espresso'
21
-        );
22
-        $this->label             = [
23
-            'singular' => esc_html__('batch', 'event_espresso'),
24
-            'plural'   => esc_html__('batches', 'event_espresso'),
25
-        ];
26
-        $this->_master_templates = [
27
-            'email' => 'registration',
28
-        ];
29
-
30
-        parent::__construct();
31
-    }
32
-
33
-
34
-    /**
35
-     * Sets admin_registered_pages property
36
-     */
37
-    protected function _set_admin_pages()
38
-    {
39
-        $this->admin_registered_pages = []; // no admin pages to register this with.
40
-    }
41
-
42
-
43
-    /**
44
-     * Sets property related to data handler.
45
-     */
46
-    protected function _set_data_handler()
47
-    {
48
-        $this->_data_handler   = 'Registrations';
49
-        $this->_single_message = $this->_data instanceof EE_Registration;
50
-    }
51
-
52
-
53
-    /**
54
-     * Returns the data for the given context for this message type.
55
-     *
56
-     * @param string          $context
57
-     * @param EE_Registration $registration
58
-     * @param int             $id
59
-     * @return array
60
-     */
61
-    protected function _get_data_for_context($context, EE_Registration $registration, $id)
62
-    {
63
-        // newsletter message type data handler is 'Registrations' and it expects an array of EE_Registration objects.
64
-        return [$registration];
65
-    }
66
-
67
-
68
-    /**
69
-     * Sets the admin settings fields property for this message type.
70
-     */
71
-    protected function _set_admin_settings_fields()
72
-    {
73
-        $this->_admin_settings_fields = [];
74
-    }
75
-
76
-
77
-    /**
78
-     * Sets the contexts for this message type.
79
-     */
80
-    protected function _set_contexts()
81
-    {
82
-        $this->_context_label = [
83
-            'label'       => esc_html__('recipient', 'event_espresso'),
84
-            'plural'      => esc_html__('recipients', 'event_espresso'),
85
-            'description' => esc_html__('Recipient\'s are who will receive the message.', 'event_espresso'),
86
-        ];
87
-
88
-        $this->_contexts = [
89
-            'attendee' => [
90
-                'label'       => esc_html__('Registrant', 'event_espresso'),
91
-                'description' => esc_html__('This template goes to selected registrants.', 'event_espresso'),
92
-            ],
93
-        ];
94
-    }
95
-
96
-
97
-    /**
98
-     * used to set the valid shortcodes.
99
-     * For the newsletter message type we only have two valid shortcode libraries in use, recipient details and
100
-     * organization.  That's it!
101
-     *
102
-     * @return  void
103
-     * @since   4.3.0
104
-     */
105
-    protected function _set_valid_shortcodes()
106
-    {
107
-        parent::_set_valid_shortcodes();
108
-
109
-        $included_shortcodes = [
110
-            'recipient_details',
111
-            'organization',
112
-            'newsletter',
113
-        ];
114
-
115
-        foreach ($this->_valid_shortcodes as $context => $shortcodes) {
116
-            foreach ($shortcodes as $key => $shortcode) {
117
-                if (! in_array($shortcode, $included_shortcodes, true)) {
118
-                    unset($this->_valid_shortcodes[ $context ][ $key ]);
119
-                }
120
-            }
121
-            $this->_valid_shortcodes[ $context ][] = 'newsletter';
122
-        }
123
-    }
124
-
125
-
126
-    /**
127
-     * Override default _attendee_addressees in EE_message_type because we want to loop through the registrations
128
-     * for EE_message_type.
129
-     *
130
-     * @return array
131
-     * @throws EE_Error
132
-     * @throws ReflectionException
133
-     */
134
-    protected function _attendee_addressees()
135
-    {
136
-        $addressee = [];
137
-
138
-        // looping through registrations
139
-        foreach ($this->_data->registrations as $details) {
140
-            // set $attendee array to blank on each loop
141
-            $aee = [];
142
-
143
-            // need to get the attendee from this registration.
144
-            $attendee = isset($details['att_obj']) && $details['att_obj'] instanceof EE_Attendee
145
-                ? $details['att_obj']
146
-                : null;
147
-
148
-            if (! $attendee instanceof EE_Attendee) {
149
-                continue;
150
-            }
151
-
152
-            // set $aee from attendee object
153
-            $aee['att_obj']        = $attendee;
154
-            $aee['reg_objs']       = isset($this->_data->attendees[ $attendee->ID() ]['reg_objs'])
155
-                ? $this->_data->attendees[ $attendee->ID() ]['reg_objs']
156
-                : [];
157
-            $aee['attendee_email'] = $attendee->email();
158
-            $aee['tkt_objs']       = isset($this->_data->attendees[ $attendee->ID() ]['tkt_objs'])
159
-                ? $this->_data->attendees[ $attendee->ID() ]['tkt_objs']
160
-                : [];
161
-
162
-            if (isset($this->_data->attendees[ $attendee->ID() ]['evt_objs'])) {
163
-                $aee['evt_objs'] = $this->_data->attendees[ $attendee->ID() ]['evt_objs'];
164
-                $aee['events']   = $this->_data->attendees[ $attendee->ID() ]['evt_objs'];
165
-            } else {
166
-                $aee['evt_objs'] = $aee['events'] = [];
167
-            }
168
-
169
-            $aee['reg_obj']   = $details['reg_obj'] ?? null;
170
-            $aee['attendees'] = $this->_data->attendees;
171
-
172
-            // merge in the primary attendee data
173
-            $aee = array_merge($this->_default_addressee_data, $aee);
174
-
175
-            // make sure txn is set
176
-            if (empty($aee['txn']) && $aee['reg_obj'] instanceof EE_Registration) {
177
-                $aee['txn'] = $aee['reg_obj']->transaction();
178
-            }
179
-
180
-            $addressee[] = new EE_Messages_Addressee($aee);
181
-        }
182
-        return $addressee;
183
-    }
184
-
185
-
186
-    /**
187
-     * Allows a message type to specifically exclude template fields for the provided messenger.
188
-     * Filtered so this can be programmatically altered as well.
189
-     *
190
-     * @param string $messenger_name name of messenger
191
-     * @return array
192
-     */
193
-    public function excludedFieldsForMessenger($messenger_name)
194
-    {
195
-        $excluded_fields = [
196
-            'email' => ['cc'],
197
-        ];
198
-        return $excluded_fields[ $messenger_name ] ?? parent::excludedFieldsForMessenger($messenger_name);
199
-    }
15
+	public function __construct()
16
+	{
17
+		$this->name              = 'newsletter';
18
+		$this->description       = esc_html__(
19
+			'Batch message type messages are triggered manually by the admin for sending notifications to a selected group of recipients. This should only be used for more general notification type messages that contain information specific for the recipients. For "newsletter" type messages we recommend using an email list service like MailChimp, because sending non-related mail-outs to contacts increases the risk of your site domain getting added to spam lists, which will prevent messages getting to users.',
20
+			'event_espresso'
21
+		);
22
+		$this->label             = [
23
+			'singular' => esc_html__('batch', 'event_espresso'),
24
+			'plural'   => esc_html__('batches', 'event_espresso'),
25
+		];
26
+		$this->_master_templates = [
27
+			'email' => 'registration',
28
+		];
29
+
30
+		parent::__construct();
31
+	}
32
+
33
+
34
+	/**
35
+	 * Sets admin_registered_pages property
36
+	 */
37
+	protected function _set_admin_pages()
38
+	{
39
+		$this->admin_registered_pages = []; // no admin pages to register this with.
40
+	}
41
+
42
+
43
+	/**
44
+	 * Sets property related to data handler.
45
+	 */
46
+	protected function _set_data_handler()
47
+	{
48
+		$this->_data_handler   = 'Registrations';
49
+		$this->_single_message = $this->_data instanceof EE_Registration;
50
+	}
51
+
52
+
53
+	/**
54
+	 * Returns the data for the given context for this message type.
55
+	 *
56
+	 * @param string          $context
57
+	 * @param EE_Registration $registration
58
+	 * @param int             $id
59
+	 * @return array
60
+	 */
61
+	protected function _get_data_for_context($context, EE_Registration $registration, $id)
62
+	{
63
+		// newsletter message type data handler is 'Registrations' and it expects an array of EE_Registration objects.
64
+		return [$registration];
65
+	}
66
+
67
+
68
+	/**
69
+	 * Sets the admin settings fields property for this message type.
70
+	 */
71
+	protected function _set_admin_settings_fields()
72
+	{
73
+		$this->_admin_settings_fields = [];
74
+	}
75
+
76
+
77
+	/**
78
+	 * Sets the contexts for this message type.
79
+	 */
80
+	protected function _set_contexts()
81
+	{
82
+		$this->_context_label = [
83
+			'label'       => esc_html__('recipient', 'event_espresso'),
84
+			'plural'      => esc_html__('recipients', 'event_espresso'),
85
+			'description' => esc_html__('Recipient\'s are who will receive the message.', 'event_espresso'),
86
+		];
87
+
88
+		$this->_contexts = [
89
+			'attendee' => [
90
+				'label'       => esc_html__('Registrant', 'event_espresso'),
91
+				'description' => esc_html__('This template goes to selected registrants.', 'event_espresso'),
92
+			],
93
+		];
94
+	}
95
+
96
+
97
+	/**
98
+	 * used to set the valid shortcodes.
99
+	 * For the newsletter message type we only have two valid shortcode libraries in use, recipient details and
100
+	 * organization.  That's it!
101
+	 *
102
+	 * @return  void
103
+	 * @since   4.3.0
104
+	 */
105
+	protected function _set_valid_shortcodes()
106
+	{
107
+		parent::_set_valid_shortcodes();
108
+
109
+		$included_shortcodes = [
110
+			'recipient_details',
111
+			'organization',
112
+			'newsletter',
113
+		];
114
+
115
+		foreach ($this->_valid_shortcodes as $context => $shortcodes) {
116
+			foreach ($shortcodes as $key => $shortcode) {
117
+				if (! in_array($shortcode, $included_shortcodes, true)) {
118
+					unset($this->_valid_shortcodes[ $context ][ $key ]);
119
+				}
120
+			}
121
+			$this->_valid_shortcodes[ $context ][] = 'newsletter';
122
+		}
123
+	}
124
+
125
+
126
+	/**
127
+	 * Override default _attendee_addressees in EE_message_type because we want to loop through the registrations
128
+	 * for EE_message_type.
129
+	 *
130
+	 * @return array
131
+	 * @throws EE_Error
132
+	 * @throws ReflectionException
133
+	 */
134
+	protected function _attendee_addressees()
135
+	{
136
+		$addressee = [];
137
+
138
+		// looping through registrations
139
+		foreach ($this->_data->registrations as $details) {
140
+			// set $attendee array to blank on each loop
141
+			$aee = [];
142
+
143
+			// need to get the attendee from this registration.
144
+			$attendee = isset($details['att_obj']) && $details['att_obj'] instanceof EE_Attendee
145
+				? $details['att_obj']
146
+				: null;
147
+
148
+			if (! $attendee instanceof EE_Attendee) {
149
+				continue;
150
+			}
151
+
152
+			// set $aee from attendee object
153
+			$aee['att_obj']        = $attendee;
154
+			$aee['reg_objs']       = isset($this->_data->attendees[ $attendee->ID() ]['reg_objs'])
155
+				? $this->_data->attendees[ $attendee->ID() ]['reg_objs']
156
+				: [];
157
+			$aee['attendee_email'] = $attendee->email();
158
+			$aee['tkt_objs']       = isset($this->_data->attendees[ $attendee->ID() ]['tkt_objs'])
159
+				? $this->_data->attendees[ $attendee->ID() ]['tkt_objs']
160
+				: [];
161
+
162
+			if (isset($this->_data->attendees[ $attendee->ID() ]['evt_objs'])) {
163
+				$aee['evt_objs'] = $this->_data->attendees[ $attendee->ID() ]['evt_objs'];
164
+				$aee['events']   = $this->_data->attendees[ $attendee->ID() ]['evt_objs'];
165
+			} else {
166
+				$aee['evt_objs'] = $aee['events'] = [];
167
+			}
168
+
169
+			$aee['reg_obj']   = $details['reg_obj'] ?? null;
170
+			$aee['attendees'] = $this->_data->attendees;
171
+
172
+			// merge in the primary attendee data
173
+			$aee = array_merge($this->_default_addressee_data, $aee);
174
+
175
+			// make sure txn is set
176
+			if (empty($aee['txn']) && $aee['reg_obj'] instanceof EE_Registration) {
177
+				$aee['txn'] = $aee['reg_obj']->transaction();
178
+			}
179
+
180
+			$addressee[] = new EE_Messages_Addressee($aee);
181
+		}
182
+		return $addressee;
183
+	}
184
+
185
+
186
+	/**
187
+	 * Allows a message type to specifically exclude template fields for the provided messenger.
188
+	 * Filtered so this can be programmatically altered as well.
189
+	 *
190
+	 * @param string $messenger_name name of messenger
191
+	 * @return array
192
+	 */
193
+	public function excludedFieldsForMessenger($messenger_name)
194
+	{
195
+		$excluded_fields = [
196
+			'email' => ['cc'],
197
+		];
198
+		return $excluded_fields[ $messenger_name ] ?? parent::excludedFieldsForMessenger($messenger_name);
199
+	}
200 200
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -19,7 +19,7 @@  discard block
 block discarded – undo
19 19
             'Batch message type messages are triggered manually by the admin for sending notifications to a selected group of recipients. This should only be used for more general notification type messages that contain information specific for the recipients. For "newsletter" type messages we recommend using an email list service like MailChimp, because sending non-related mail-outs to contacts increases the risk of your site domain getting added to spam lists, which will prevent messages getting to users.',
20 20
             'event_espresso'
21 21
         );
22
-        $this->label             = [
22
+        $this->label = [
23 23
             'singular' => esc_html__('batch', 'event_espresso'),
24 24
             'plural'   => esc_html__('batches', 'event_espresso'),
25 25
         ];
@@ -114,11 +114,11 @@  discard block
 block discarded – undo
114 114
 
115 115
         foreach ($this->_valid_shortcodes as $context => $shortcodes) {
116 116
             foreach ($shortcodes as $key => $shortcode) {
117
-                if (! in_array($shortcode, $included_shortcodes, true)) {
118
-                    unset($this->_valid_shortcodes[ $context ][ $key ]);
117
+                if ( ! in_array($shortcode, $included_shortcodes, true)) {
118
+                    unset($this->_valid_shortcodes[$context][$key]);
119 119
                 }
120 120
             }
121
-            $this->_valid_shortcodes[ $context ][] = 'newsletter';
121
+            $this->_valid_shortcodes[$context][] = 'newsletter';
122 122
         }
123 123
     }
124 124
 
@@ -145,23 +145,23 @@  discard block
 block discarded – undo
145 145
                 ? $details['att_obj']
146 146
                 : null;
147 147
 
148
-            if (! $attendee instanceof EE_Attendee) {
148
+            if ( ! $attendee instanceof EE_Attendee) {
149 149
                 continue;
150 150
             }
151 151
 
152 152
             // set $aee from attendee object
153 153
             $aee['att_obj']        = $attendee;
154
-            $aee['reg_objs']       = isset($this->_data->attendees[ $attendee->ID() ]['reg_objs'])
155
-                ? $this->_data->attendees[ $attendee->ID() ]['reg_objs']
154
+            $aee['reg_objs']       = isset($this->_data->attendees[$attendee->ID()]['reg_objs'])
155
+                ? $this->_data->attendees[$attendee->ID()]['reg_objs']
156 156
                 : [];
157 157
             $aee['attendee_email'] = $attendee->email();
158
-            $aee['tkt_objs']       = isset($this->_data->attendees[ $attendee->ID() ]['tkt_objs'])
159
-                ? $this->_data->attendees[ $attendee->ID() ]['tkt_objs']
158
+            $aee['tkt_objs']       = isset($this->_data->attendees[$attendee->ID()]['tkt_objs'])
159
+                ? $this->_data->attendees[$attendee->ID()]['tkt_objs']
160 160
                 : [];
161 161
 
162
-            if (isset($this->_data->attendees[ $attendee->ID() ]['evt_objs'])) {
163
-                $aee['evt_objs'] = $this->_data->attendees[ $attendee->ID() ]['evt_objs'];
164
-                $aee['events']   = $this->_data->attendees[ $attendee->ID() ]['evt_objs'];
162
+            if (isset($this->_data->attendees[$attendee->ID()]['evt_objs'])) {
163
+                $aee['evt_objs'] = $this->_data->attendees[$attendee->ID()]['evt_objs'];
164
+                $aee['events']   = $this->_data->attendees[$attendee->ID()]['evt_objs'];
165 165
             } else {
166 166
                 $aee['evt_objs'] = $aee['events'] = [];
167 167
             }
@@ -195,6 +195,6 @@  discard block
 block discarded – undo
195 195
         $excluded_fields = [
196 196
             'email' => ['cc'],
197 197
         ];
198
-        return $excluded_fields[ $messenger_name ] ?? parent::excludedFieldsForMessenger($messenger_name);
198
+        return $excluded_fields[$messenger_name] ?? parent::excludedFieldsForMessenger($messenger_name);
199 199
     }
200 200
 }
Please login to merge, or discard this patch.