Session.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. <?php
  2. /**
  3. * This file is part of workerman.
  4. *
  5. * Licensed under The MIT License
  6. * For full copyright and license information, please see the MIT-LICENSE.txt
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @author walkor<walkor@workerman.net>
  10. * @copyright walkor<walkor@workerman.net>
  11. * @link http://www.workerman.net/
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Workerman\Protocols\Http;
  15. use Workerman\Properties;
  16. use Workerman\Protocols\Http\Session\FileSessionHandler;
  17. use Workerman\Protocols\Http\Session\SessionHandlerInterface;
  18. /**
  19. * Class Session
  20. * @package Workerman\Protocols\Http
  21. */
  22. class Session
  23. {
  24. /**
  25. * Session andler class which implements SessionHandlerInterface.
  26. *
  27. * @var string
  28. */
  29. protected static $handlerClass = FileSessionHandler::class;
  30. /**
  31. * Parameters of __constructor for session handler class.
  32. *
  33. * @var null
  34. */
  35. protected static $handlerConfig = null;
  36. /**
  37. * Session name.
  38. *
  39. * @var string
  40. */
  41. public static $name = 'PHPSID';
  42. /**
  43. * Auto update timestamp.
  44. *
  45. * @var bool
  46. */
  47. public static $autoUpdateTimestamp = false;
  48. /**
  49. * Session lifetime.
  50. *
  51. * @var int
  52. */
  53. public static $lifetime = 1440;
  54. /**
  55. * Cookie lifetime.
  56. *
  57. * @var int
  58. */
  59. public static $cookieLifetime = 1440;
  60. /**
  61. * Session cookie path.
  62. *
  63. * @var string
  64. */
  65. public static $cookiePath = '/';
  66. /**
  67. * Session cookie domain.
  68. *
  69. * @var string
  70. */
  71. public static $domain = '';
  72. /**
  73. * HTTPS only cookies.
  74. *
  75. * @var bool
  76. */
  77. public static $secure = false;
  78. /**
  79. * HTTP access only.
  80. *
  81. * @var bool
  82. */
  83. public static $httpOnly = true;
  84. /**
  85. * Same-site cookies.
  86. *
  87. * @var string
  88. */
  89. public static $sameSite = '';
  90. /**
  91. * Gc probability.
  92. *
  93. * @var int[]
  94. */
  95. public static $gcProbability = [1, 1000];
  96. /**
  97. * Session handler instance.
  98. *
  99. * @var SessionHandlerInterface
  100. */
  101. protected static $handler = null;
  102. /**
  103. * Session data.
  104. *
  105. * @var array
  106. */
  107. protected $data = [];
  108. /**
  109. * Session changed and need to save.
  110. *
  111. * @var bool
  112. */
  113. protected $needSave = false;
  114. /**
  115. * Session id.
  116. *
  117. * @var null
  118. */
  119. protected $sessionId = null;
  120. /**
  121. * Dynamic Properties。
  122. */
  123. use Properties;
  124. /**
  125. * Session constructor.
  126. *
  127. * @param string $sessionId
  128. */
  129. public function __construct($sessionId)
  130. {
  131. static::checkSessionId($sessionId);
  132. if (static::$handler === null) {
  133. static::initHandler();
  134. }
  135. $this->sessionId = $sessionId;
  136. if ($data = static::$handler->read($sessionId)) {
  137. $this->data = \unserialize($data);
  138. }
  139. }
  140. /**
  141. * Get session id.
  142. *
  143. * @return string
  144. */
  145. public function getId()
  146. {
  147. return $this->sessionId;
  148. }
  149. /**
  150. * Get session.
  151. *
  152. * @param string $name
  153. * @param mixed|null $default
  154. * @return mixed|null
  155. */
  156. public function get($name, $default = null)
  157. {
  158. return $this->data[$name] ?? $default;
  159. }
  160. /**
  161. * Store data in the session.
  162. *
  163. * @param string $name
  164. * @param mixed $value
  165. */
  166. public function set($name, $value)
  167. {
  168. $this->data[$name] = $value;
  169. $this->needSave = true;
  170. }
  171. /**
  172. * Delete an item from the session.
  173. *
  174. * @param string $name
  175. */
  176. public function delete($name)
  177. {
  178. unset($this->data[$name]);
  179. $this->needSave = true;
  180. }
  181. /**
  182. * Retrieve and delete an item from the session.
  183. *
  184. * @param string $name
  185. * @param mixed|null $default
  186. * @return mixed|null
  187. */
  188. public function pull($name, $default = null)
  189. {
  190. $value = $this->get($name, $default);
  191. $this->delete($name);
  192. return $value;
  193. }
  194. /**
  195. * Store data in the session.
  196. *
  197. * @param string|array $key
  198. * @param mixed|null $value
  199. */
  200. public function put($key, $value = null)
  201. {
  202. if (!\is_array($key)) {
  203. $this->set($key, $value);
  204. return;
  205. }
  206. foreach ($key as $k => $v) {
  207. $this->data[$k] = $v;
  208. }
  209. $this->needSave = true;
  210. }
  211. /**
  212. * Remove a piece of data from the session.
  213. *
  214. * @param string $name
  215. */
  216. public function forget($name)
  217. {
  218. if (\is_scalar($name)) {
  219. $this->delete($name);
  220. return;
  221. }
  222. if (\is_array($name)) {
  223. foreach ($name as $key) {
  224. unset($this->data[$key]);
  225. }
  226. }
  227. $this->needSave = true;
  228. }
  229. /**
  230. * Retrieve all the data in the session.
  231. *
  232. * @return array
  233. */
  234. public function all()
  235. {
  236. return $this->data;
  237. }
  238. /**
  239. * Remove all data from the session.
  240. *
  241. * @return void
  242. */
  243. public function flush()
  244. {
  245. $this->needSave = true;
  246. $this->data = [];
  247. }
  248. /**
  249. * Determining If An Item Exists In The Session.
  250. *
  251. * @param string $name
  252. * @return bool
  253. */
  254. public function has($name)
  255. {
  256. return isset($this->data[$name]);
  257. }
  258. /**
  259. * To determine if an item is present in the session, even if its value is null.
  260. *
  261. * @param string $name
  262. * @return bool
  263. */
  264. public function exists($name)
  265. {
  266. return \array_key_exists($name, $this->data);
  267. }
  268. /**
  269. * Save session to store.
  270. *
  271. * @return void
  272. */
  273. public function save()
  274. {
  275. if ($this->needSave) {
  276. if (empty($this->data)) {
  277. static::$handler->destroy($this->sessionId);
  278. } else {
  279. static::$handler->write($this->sessionId, \serialize($this->data));
  280. }
  281. } elseif (static::$autoUpdateTimestamp) {
  282. static::refresh();
  283. }
  284. $this->needSave = false;
  285. }
  286. /**
  287. * Refresh session expire time.
  288. *
  289. * @return bool
  290. */
  291. public function refresh()
  292. {
  293. return static::$handler->updateTimestamp($this->getId());
  294. }
  295. /**
  296. * Init.
  297. *
  298. * @return void
  299. */
  300. public static function init()
  301. {
  302. if (($gcProbability = (int)\ini_get('session.gc_probability')) && ($gcDivisor = (int)\ini_get('session.gc_divisor'))) {
  303. static::$gcProbability = [$gcProbability, $gcDivisor];
  304. }
  305. if ($gcMaxLifeTime = \ini_get('session.gc_maxlifetime')) {
  306. self::$lifetime = (int)$gcMaxLifeTime;
  307. }
  308. $sessionCookieParams = \session_get_cookie_params();
  309. static::$cookieLifetime = $sessionCookieParams['lifetime'];
  310. static::$cookiePath = $sessionCookieParams['path'];
  311. static::$domain = $sessionCookieParams['domain'];
  312. static::$secure = $sessionCookieParams['secure'];
  313. static::$httpOnly = $sessionCookieParams['httponly'];
  314. }
  315. /**
  316. * Set session handler class.
  317. *
  318. * @param mixed|null $className
  319. * @param mixed|null $config
  320. * @return string
  321. */
  322. public static function handlerClass($className = null, $config = null)
  323. {
  324. if ($className) {
  325. static::$handlerClass = $className;
  326. }
  327. if ($config) {
  328. static::$handlerConfig = $config;
  329. }
  330. return static::$handlerClass;
  331. }
  332. /**
  333. * Get cookie params.
  334. *
  335. * @return array
  336. */
  337. public static function getCookieParams()
  338. {
  339. return [
  340. 'lifetime' => static::$cookieLifetime,
  341. 'path' => static::$cookiePath,
  342. 'domain' => static::$domain,
  343. 'secure' => static::$secure,
  344. 'httponly' => static::$httpOnly,
  345. 'samesite' => static::$sameSite,
  346. ];
  347. }
  348. /**
  349. * Init handler.
  350. *
  351. * @return void
  352. */
  353. protected static function initHandler()
  354. {
  355. if (static::$handlerConfig === null) {
  356. static::$handler = new static::$handlerClass();
  357. } else {
  358. static::$handler = new static::$handlerClass(static::$handlerConfig);
  359. }
  360. }
  361. /**
  362. * GC sessions.
  363. *
  364. * @return void
  365. */
  366. public function gc()
  367. {
  368. static::$handler->gc(static::$lifetime);
  369. }
  370. /**
  371. * __destruct.
  372. *
  373. * @return void
  374. */
  375. public function __destruct()
  376. {
  377. $this->save();
  378. if (\random_int(1, static::$gcProbability[1]) <= static::$gcProbability[0]) {
  379. $this->gc();
  380. }
  381. }
  382. /**
  383. * Check session id.
  384. *
  385. * @param string $sessionId
  386. */
  387. protected static function checkSessionId($sessionId)
  388. {
  389. if (!\preg_match('/^[a-zA-Z0-9]+$/', $sessionId)) {
  390. throw new SessionException("session_id $sessionId is invalid");
  391. }
  392. }
  393. }
  394. /**
  395. * Class SessionException
  396. * @package Workerman\Protocols\Http
  397. */
  398. class SessionException extends \RuntimeException
  399. {
  400. }
  401. // Init session.
  402. Session::init();