| @@ -1,0 +1,185 @@ | ||
| 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 | +} | |