| 1 |
<?php |
| 2 |
/** |
| 3 |
* @package VikWP - Libraries |
| 4 |
* @subpackage adapter.session |
| 5 |
* @author E4J s.r.l. |
| 6 |
* @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved. |
| 7 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL |
| 8 |
* @link https://vikwp.com |
| 9 |
*/ |
| 10 |
|
| 11 |
// No direct access |
| 12 |
defined('ABSPATH') or die('No script kiddies please!'); |
| 13 |
|
| 14 |
/** |
| 15 |
* Class used to handle the PHP session. |
| 16 |
* Provides the methods to begin a new session and to destroy it. |
| 17 |
* |
| 18 |
* @since 10.0 |
| 19 |
*/ |
| 20 |
class JSessionHandler |
| 21 |
{ |
| 22 |
/** |
| 23 |
* Starts a new session, only if it doesn't already exist. |
| 24 |
* |
| 25 |
* @return void |
| 26 |
*/ |
| 27 |
public static function start() |
| 28 |
{ |
| 29 |
/** |
| 30 |
* Prevent the handler from starting a new session in case the process was launched |
| 31 |
* from wp-cron.php, which manually sends some headers at the beginning of the file. |
| 32 |
* |
| 33 |
* @since 10.1.65 |
| 34 |
*/ |
| 35 |
if (defined('DOING_CRON') && DOING_CRON) |
| 36 |
{ |
| 37 |
return; |
| 38 |
} |
| 39 |
|
| 40 |
/** |
| 41 |
* Do not go ahead in case the session has been already started, otherwise duplicate |
| 42 |
* calls to this method risks to register the same hook more than once. |
| 43 |
* |
| 44 |
* @since 10.1.70 |
| 45 |
*/ |
| 46 |
if (self::isStarted()) |
| 47 |
{ |
| 48 |
return; |
| 49 |
} |
| 50 |
|
| 51 |
/** |
| 52 |
* In case the headers have been already sent, trigger warning and abort. |
| 53 |
* |
| 54 |
* @since 10.1.70 |
| 55 |
*/ |
| 56 |
if (headers_sent()) |
| 57 |
{ |
| 58 |
trigger_error('Session cannot be started after headers have already been sent', E_USER_WARNING); |
| 59 |
return; |
| 60 |
} |
| 61 |
|
| 62 |
session_start(); |
| 63 |
|
| 64 |
/** |
| 65 |
* Filters whether to preempt an HTTP request's return value. |
| 66 |
* It is needed to support concurrent cURL requests (see Site Health). |
| 67 |
* |
| 68 |
* Returning a non-false value from the filter will short-circuit the HTTP request and return |
| 69 |
* early with that value. A filter should return either: |
| 70 |
* |
| 71 |
* - An array containing 'headers', 'body', 'response', 'cookies', and 'filename' elements |
| 72 |
* - A WP_Error instance |
| 73 |
* - boolean false (to avoid short-circuiting the response) |
| 74 |
* |
| 75 |
* Returning any other value may result in unexpected behaviour. |
| 76 |
* |
| 77 |
* @since 2.9.0 |
| 78 |
* |
| 79 |
* @param false|array|WP_Error $preempt Whether to preempt an HTTP request's return value. Default false. |
| 80 |
* @param array $r HTTP request arguments. |
| 81 |
* @param string $url The request URL. |
| 82 |
*/ |
| 83 |
add_filter('pre_http_request', function($preempt, $r, $url) |
| 84 |
{ |
| 85 |
$input = JFactory::getApplication()->input; |
| 86 |
|
| 87 |
$httpUrl = new JUri($url); |
| 88 |
$baseUrl = new JUri(site_url()); |
| 89 |
|
| 90 |
/** |
| 91 |
* As WP Site Health suggests, an active session might close timeouts for loopback connection. |
| 92 |
* For this reason, every time an HTTP request is made, we should try to detect whether the |
| 93 |
* provided end-point has the same root of the current website, meaning that we are performing |
| 94 |
* a self connection, probably through the REST API. |
| 95 |
* |
| 96 |
* @since 10.1.39 |
| 97 |
*/ |
| 98 |
if (strpos($httpUrl->toString(['host', 'path']), $baseUrl->toString(['host', 'path'])) !== false) |
| 99 |
{ |
| 100 |
// loopback detected, terminate the session before starting a request |
| 101 |
session_write_close(); |
| 102 |
} |
| 103 |
|
| 104 |
return $preempt; |
| 105 |
}, 10, 3); |
| 106 |
|
| 107 |
/** |
| 108 |
* Suppress "session active" critical error when Site Health performs its tests. |
| 109 |
* |
| 110 |
* Even if the session is active on Site Health, the plugin always call the |
| 111 |
* `session_write_close` method before making any HTTP requests. |
| 112 |
* |
| 113 |
* @since 5.5.0 |
| 114 |
*/ |
| 115 |
add_action('init', function() { |
| 116 |
global $pagenow; |
| 117 |
|
| 118 |
// check if the current page is Site Health |
| 119 |
if (preg_match("/^site-health/i", $pagenow)) |
| 120 |
{ |
| 121 |
// always write session to avoid receiving a critical issue |
| 122 |
session_write_close(); |
| 123 |
} |
| 124 |
}); |
| 125 |
} |
| 126 |
|
| 127 |
/** |
| 128 |
* Destroys the current active session, only if it already exists. |
| 129 |
* |
| 130 |
* @param boolean $restart True to immediately restart a new session. |
| 131 |
* |
| 132 |
* @return void |
| 133 |
*/ |
| 134 |
public static function destroy($restart = true) |
| 135 |
{ |
| 136 |
if (!self::isStarted()) |
| 137 |
{ |
| 138 |
return; |
| 139 |
} |
| 140 |
|
| 141 |
session_destroy(); |
| 142 |
|
| 143 |
if ($restart) |
| 144 |
{ |
| 145 |
self::start(); |
| 146 |
} |
| 147 |
} |
| 148 |
|
| 149 |
/** |
| 150 |
* Checks if a session has been started. |
| 151 |
* |
| 152 |
* @return boolean True if active, otherwise false. |
| 153 |
*/ |
| 154 |
public static function isStarted() |
| 155 |
{ |
| 156 |
/** |
| 157 |
* Make sure the session status is currently active. This because, |
| 158 |
* in case someone had manually closed the session, this method would |
| 159 |
* have improperly returned true. |
| 160 |
* |
| 161 |
* @since 10.1.43 |
| 162 |
*/ |
| 163 |
return session_status() === PHP_SESSION_ACTIVE && self::getId(); |
| 164 |
} |
| 165 |
|
| 166 |
/** |
| 167 |
* Returns the session ID, if any. |
| 168 |
* |
| 169 |
* @return string The unique session ID if active, otherwise an empty string. |
| 170 |
*/ |
| 171 |
public static function getId() |
| 172 |
{ |
| 173 |
return session_id(); |
| 174 |
} |
| 175 |
|
| 176 |
/** |
| 177 |
* Returns the session name, if any. |
| 178 |
* |
| 179 |
* @return string The session name. |
| 180 |
*/ |
| 181 |
public static function getName() |
| 182 |
{ |
| 183 |
return session_name(); |
| 184 |
} |
| 185 |
} |
| 186 |
|