class-wp-session.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. <?php
  2. /**
  3. * WordPress session managment.
  4. *
  5. * Standardizes WordPress session data using database-backed options for storage.
  6. * for storing user session information.
  7. *
  8. * @package WordPress
  9. * @subpackage Session
  10. * @since 3.7.0
  11. */
  12. // Exit if accessed directly
  13. if ( ! defined( 'ABSPATH' ) ) exit;
  14. /*
  15. * MODIFICATIONS
  16. *
  17. * - Remove `set_cooke()` from constructor
  18. * - Give `set_cookie()` public access
  19. */
  20. /**
  21. * WordPress Session class for managing user session data.
  22. *
  23. * @package WordPress
  24. * @since 3.7.0
  25. */
  26. final class WP_Session extends Recursive_ArrayAccess implements Iterator, Countable {
  27. /**
  28. * ID of the current session.
  29. *
  30. * @var string
  31. */
  32. public $session_id;
  33. /**
  34. * Unix timestamp when session expires.
  35. *
  36. * @var int
  37. */
  38. protected $expires;
  39. /**
  40. * Unix timestamp indicating when the expiration time needs to be reset.
  41. *
  42. * @var int
  43. */
  44. protected $exp_variant;
  45. /**
  46. * Singleton instance.
  47. *
  48. * @var bool|WP_Session
  49. */
  50. private static $instance = false;
  51. /**
  52. * Retrieve the current session instance.
  53. *
  54. * @param bool $session_id Session ID from which to populate data.
  55. *
  56. * @return bool|WP_Session
  57. */
  58. public static function get_instance() {
  59. if ( ! self::$instance ) {
  60. self::$instance = new self();
  61. }
  62. return self::$instance;
  63. }
  64. /**
  65. * Default constructor.
  66. * Will rebuild the session collection from the given session ID if it exists. Otherwise, will
  67. * create a new session with that ID.
  68. *
  69. * @param $session_id
  70. * @uses apply_filters Calls `wp_session_expiration` to determine how long until sessions expire.
  71. */
  72. protected function __construct() {
  73. if ( isset( $_COOKIE[WP_SESSION_COOKIE] ) ) {
  74. $cookie = stripslashes( $_COOKIE[WP_SESSION_COOKIE] );
  75. $cookie_crumbs = explode( '||', $cookie );
  76. if( $this->is_valid_md5( $cookie_crumbs[0] ) ) {
  77. $this->session_id = $cookie_crumbs[0];
  78. } else {
  79. $this->regenerate_id( true );
  80. }
  81. $this->expires = $cookie_crumbs[1];
  82. $this->exp_variant = $cookie_crumbs[2];
  83. // Update the session expiration if we're past the variant time
  84. if ( time() > $this->exp_variant ) {
  85. $this->set_expiration();
  86. delete_option( "_wp_session_expires_{$this->session_id}" );
  87. add_option( "_wp_session_expires_{$this->session_id}", $this->expires, '', 'no' );
  88. }
  89. } else {
  90. $this->session_id = $this->generate_id();
  91. $this->set_expiration();
  92. }
  93. $this->read_data();
  94. /*
  95. * MODIFICATION: Only set the cookie manually.
  96. */
  97. //$this->set_cookie();
  98. }
  99. /**
  100. * Set both the expiration time and the expiration variant.
  101. *
  102. * If the current time is below the variant, we don't update the session's expiration time. If it's
  103. * greater than the variant, then we update the expiration time in the database. This prevents
  104. * writing to the database on every page load for active sessions and only updates the expiration
  105. * time if we're nearing when the session actually expires.
  106. *
  107. * By default, the expiration time is set to 30 minutes.
  108. * By default, the expiration variant is set to 24 minutes.
  109. *
  110. * As a result, the session expiration time - at a maximum - will only be written to the database once
  111. * every 24 minutes. After 30 minutes, the session will have been expired. No cookie will be sent by
  112. * the browser, and the old session will be queued for deletion by the garbage collector.
  113. *
  114. * @uses apply_filters Calls `wp_session_expiration_variant` to get the max update window for session data.
  115. * @uses apply_filters Calls `wp_session_expiration` to get the standard expiration time for sessions.
  116. */
  117. protected function set_expiration() {
  118. $this->exp_variant = time() + (int) apply_filters( 'wp_session_expiration_variant', 24 * 60 );
  119. $this->expires = time() + (int) apply_filters( 'wp_session_expiration', 30 * 60 );
  120. }
  121. /**
  122. * Set the session cookie
  123. */
  124. /*
  125. * MODIFICATION: Change access to public for manually setting cookie.
  126. */
  127. public function set_cookie() {
  128. @setcookie( WP_SESSION_COOKIE, $this->session_id . '||' . $this->expires . '||' . $this->exp_variant , $this->expires, COOKIEPATH, COOKIE_DOMAIN );
  129. }
  130. /**
  131. * Generate a cryptographically strong unique ID for the session token.
  132. *
  133. * @return string
  134. */
  135. protected function generate_id() {
  136. require_once( ABSPATH . 'wp-includes/class-phpass.php');
  137. $hasher = new PasswordHash( 8, false );
  138. return md5( $hasher->get_random_bytes( 32 ) );
  139. }
  140. /**
  141. * Checks if is valid md5 string
  142. *
  143. * @param string $md5
  144. * @return int
  145. */
  146. protected function is_valid_md5( $md5 = '' ){
  147. return preg_match( '/^[a-f0-9]{32}$/', $md5 );
  148. }
  149. /**
  150. * Read data from a transient for the current session.
  151. *
  152. * Automatically resets the expiration time for the session transient to some time in the future.
  153. *
  154. * @return array
  155. */
  156. protected function read_data() {
  157. $this->container = get_option( "_wp_session_{$this->session_id}", array() );
  158. return $this->container;
  159. }
  160. /**
  161. * Write the data from the current session to the data storage system.
  162. */
  163. public function write_data() {
  164. $option_key = "_wp_session_{$this->session_id}";
  165. // Only write the collection to the DB if it's changed.
  166. if ( $this->dirty ) {
  167. if ( false === get_option( $option_key ) ) {
  168. add_option( "_wp_session_{$this->session_id}", $this->container, '', 'no' );
  169. add_option( "_wp_session_expires_{$this->session_id}", $this->expires, '', 'no' );
  170. } else {
  171. delete_option( "_wp_session_{$this->session_id}" );
  172. add_option( "_wp_session_{$this->session_id}", $this->container, '', 'no' );
  173. }
  174. }
  175. }
  176. /**
  177. * Output the current container contents as a JSON-encoded string.
  178. *
  179. * @return string
  180. */
  181. public function json_out() {
  182. return json_encode( $this->container );
  183. }
  184. /**
  185. * Decodes a JSON string and, if the object is an array, overwrites the session container with its contents.
  186. *
  187. * @param string $data
  188. *
  189. * @return bool
  190. */
  191. public function json_in( $data ) {
  192. $array = json_decode( $data );
  193. if ( is_array( $array ) ) {
  194. $this->container = $array;
  195. return true;
  196. }
  197. return false;
  198. }
  199. /**
  200. * Regenerate the current session's ID.
  201. *
  202. * @param bool $delete_old Flag whether or not to delete the old session data from the server.
  203. */
  204. public function regenerate_id( $delete_old = false ) {
  205. if ( $delete_old ) {
  206. delete_option( "_wp_session_{$this->session_id}" );
  207. }
  208. $this->session_id = $this->generate_id();
  209. $this->set_cookie();
  210. }
  211. /**
  212. * Check if a session has been initialized.
  213. *
  214. * @return bool
  215. */
  216. public function session_started() {
  217. return !!self::$instance;
  218. }
  219. /**
  220. * Return the read-only cache expiration value.
  221. *
  222. * @return int
  223. */
  224. public function cache_expiration() {
  225. return $this->expires;
  226. }
  227. /**
  228. * Flushes all session variables.
  229. */
  230. public function reset() {
  231. $this->container = array();
  232. }
  233. /*****************************************************************/
  234. /* Iterator Implementation */
  235. /*****************************************************************/
  236. /**
  237. * Current position of the array.
  238. *
  239. * @link http://php.net/manual/en/iterator.current.php
  240. *
  241. * @return mixed
  242. */
  243. public function current() {
  244. return current( $this->container );
  245. }
  246. /**
  247. * Key of the current element.
  248. *
  249. * @link http://php.net/manual/en/iterator.key.php
  250. *
  251. * @return mixed
  252. */
  253. public function key() {
  254. return key( $this->container );
  255. }
  256. /**
  257. * Move the internal point of the container array to the next item
  258. *
  259. * @link http://php.net/manual/en/iterator.next.php
  260. *
  261. * @return void
  262. */
  263. public function next() {
  264. next( $this->container );
  265. }
  266. /**
  267. * Rewind the internal point of the container array.
  268. *
  269. * @link http://php.net/manual/en/iterator.rewind.php
  270. *
  271. * @return void
  272. */
  273. public function rewind() {
  274. reset( $this->container );
  275. }
  276. /**
  277. * Is the current key valid?
  278. *
  279. * @link http://php.net/manual/en/iterator.rewind.php
  280. *
  281. * @return bool
  282. */
  283. public function valid() {
  284. return $this->offsetExists( $this->key() );
  285. }
  286. /*****************************************************************/
  287. /* Countable Implementation */
  288. /*****************************************************************/
  289. /**
  290. * Get the count of elements in the container array.
  291. *
  292. * @link http://php.net/manual/en/countable.count.php
  293. *
  294. * @return int
  295. */
  296. public function count() {
  297. return count( $this->container );
  298. }
  299. }