mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-11-16 21:17:14 +01:00
316 lines
8.3 KiB
PHP
316 lines
8.3 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Xentral\Components\Http\Session;
|
||
|
|
||
|
use Xentral\Components\Http\Exception\InvalidArgumentException;
|
||
|
use Xentral\Components\Http\Exception\SessionSegmentException;
|
||
|
|
||
|
class Session
|
||
|
{
|
||
|
/** @var string FLASH_SEGMENTKEY */
|
||
|
const FLASH_SEGMENTKEY = 'flash_messages';
|
||
|
|
||
|
/** @var string CSRF_SEGMENTKEY */
|
||
|
const CSRF_SEGMENTKEY = 'csrf_tokens';
|
||
|
|
||
|
/** @var array $data */
|
||
|
protected $data;
|
||
|
|
||
|
/** @var Segment[] $segments */
|
||
|
protected $segments;
|
||
|
|
||
|
/** @var FlashMessageCollection $flashData */
|
||
|
protected $flashMessages;
|
||
|
|
||
|
/** @var CsrfTokenManager $csrfTokens */
|
||
|
protected $csrfTokens;
|
||
|
|
||
|
/**
|
||
|
* @param array $data
|
||
|
*/
|
||
|
public function __construct($data = [])
|
||
|
{
|
||
|
$this->data = (array)$data;
|
||
|
$this->segments = [];
|
||
|
$this->flashMessages = $this->createFlashMessageCollection();
|
||
|
$this->csrfTokens = $this->createCsrfTokenManager();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clears all Session data and flash messages
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function clearAll()
|
||
|
{
|
||
|
$this->data = [];
|
||
|
$this->segments = [];
|
||
|
$this->flashMessages = new FlashMessageCollection();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets a value with a specific key from the current Segment
|
||
|
*
|
||
|
* @param string $segment
|
||
|
* @param string $key
|
||
|
* @param null $default
|
||
|
* @param bool $clear true=remove entry from the session
|
||
|
*
|
||
|
* @return mixed|null
|
||
|
*/
|
||
|
public function getValue($segment, $key, $default = null, $clear = false)
|
||
|
{
|
||
|
|
||
|
return $this->getSegment($segment)->getValue($key, $default, $clear);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Makes an entry to the current Segment
|
||
|
*
|
||
|
* @param string $segment
|
||
|
* @param string $key
|
||
|
* @param string|int|float|array $value
|
||
|
*
|
||
|
* @throws InvalidArgumentException
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function setValue($segment, $key, $value)
|
||
|
{
|
||
|
$this->getSegment($segment)->setValue($key, $value);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes a single Entry from the current segment
|
||
|
*
|
||
|
* @param string $segment
|
||
|
* @param string $key
|
||
|
*
|
||
|
* @throws InvalidArgumentException
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function removeValue($segment, $key)
|
||
|
{
|
||
|
$this->getSegment($segment)->removeValue($key);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets a segment object by it's name
|
||
|
*
|
||
|
* @param string $name
|
||
|
*
|
||
|
* @throws InvalidArgumentException
|
||
|
*
|
||
|
* @return Segment
|
||
|
*/
|
||
|
public function getSegment($name = '')
|
||
|
{
|
||
|
if ($name === self::FLASH_SEGMENTKEY || $name === self::CSRF_SEGMENTKEY) {
|
||
|
throw new SessionSegmentException(
|
||
|
sprintf('"%s" is a reserved segment name.', self::FLASH_SEGMENTKEY)
|
||
|
);
|
||
|
}
|
||
|
$segmentId = $this->getSegmentKey($name);
|
||
|
if (array_key_exists($segmentId, $this->segments)) {
|
||
|
return $this->segments[$segmentId];
|
||
|
}
|
||
|
$data = [];
|
||
|
if (array_key_exists($segmentId, $this->data)) {
|
||
|
$data = $this->data[$segmentId];
|
||
|
}
|
||
|
$segment = new Segment($this, $name, $data);
|
||
|
$this->segments[$segmentId] = $segment;
|
||
|
|
||
|
return $segment;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Dumps the whole session into specific target variable.
|
||
|
*
|
||
|
* @param mixed $targetVariable
|
||
|
*/
|
||
|
public function dumpSession(&$targetVariable)
|
||
|
{
|
||
|
$this->mergeSession();
|
||
|
$targetVariable = $this->data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array
|
||
|
*/
|
||
|
public function __debugInfo()
|
||
|
{
|
||
|
$this->dumpSession($dump);
|
||
|
$this->csrfTokens->dumpTokens($tokendump);
|
||
|
$tokendump = array_keys($tokendump);
|
||
|
|
||
|
return [
|
||
|
'data' => $dump,
|
||
|
'tokens' => $tokendump
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds new flash message to specific segment.
|
||
|
*
|
||
|
* @param string $segment if empty: default segment will be used
|
||
|
* @param string $message
|
||
|
* @param string $type
|
||
|
* @param int $priority
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function addFlashMessage($segment, $message, $type = FlashMessageData::FLASHTYPE_DEFAULT, $priority = 0)
|
||
|
{
|
||
|
$flash = new FlashMessageData($message, $type, $segment, $priority);
|
||
|
$flashData = $flash->toSessionArray();
|
||
|
$key = (string)$this->getSegmentKey(self::FLASH_SEGMENTKEY);
|
||
|
$this->data[$key][] = $flashData;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets flash message(s) by specific filter conditions.
|
||
|
*
|
||
|
* The flash message will be cleared from the session after retrieving
|
||
|
*
|
||
|
* @param string|null $segment filter for segment name
|
||
|
* @param string|null $type filter for message type
|
||
|
*
|
||
|
* @return FlashMessageData[] flash messages sorted by priority
|
||
|
*/
|
||
|
public function getFlashMessages($segment = null, $type = null)
|
||
|
{
|
||
|
return $this->flashMessages->getMessages($segment, $type);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a CSRF Token and stores it in the Session
|
||
|
*
|
||
|
* @param string $tokenKey
|
||
|
*
|
||
|
* @throws InvalidArgumentException
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function createCsrfToken($tokenKey)
|
||
|
{
|
||
|
return $this->csrfTokens->createToken($tokenKey);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if specified Token is valid
|
||
|
*
|
||
|
* @param string $tokenKey
|
||
|
* @param string $tokenValue
|
||
|
* @param bool $remove true=remove token from session to mitigate second use
|
||
|
*
|
||
|
* @throws InvalidArgumentException
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function isCsrfTokenValid($tokenKey, $tokenValue, $remove = false)
|
||
|
{
|
||
|
return $this->csrfTokens->isTokenValid($tokenKey, $tokenValue, $remove);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $segmentName
|
||
|
*
|
||
|
* @throws InvalidArgumentException
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
private function getSegmentKey($segmentName)
|
||
|
{
|
||
|
$this->ensureSegmentNameFormat($segmentName);
|
||
|
|
||
|
return sprintf('segment_%s', $segmentName);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Merge all Segments and Flashmessages and CsrfTokens into the session array.
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
private function mergeSession()
|
||
|
{
|
||
|
foreach ($this->segments as $key => $segment) {
|
||
|
$segmentData = $segment->getAll();
|
||
|
if (count($segmentData) > 0) {
|
||
|
$this->data[$key] = $segmentData;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$flashKey = $this->getSegmentKey(self::FLASH_SEGMENTKEY);
|
||
|
$newFlashes = [];
|
||
|
if (array_key_exists($flashKey, $this->data)) {
|
||
|
$newFlashes = $this->data[$flashKey];
|
||
|
}
|
||
|
$oldFlashes = $this->flashMessages->toSessionArray();
|
||
|
$this->flashMessages = new FlashMessageCollection();
|
||
|
$allFlashes = array_merge($oldFlashes, $newFlashes);
|
||
|
if (count($allFlashes) > 0) {
|
||
|
$this->data[$flashKey] = $allFlashes;
|
||
|
} else {
|
||
|
unset($this->data[$flashKey]);
|
||
|
}
|
||
|
|
||
|
$this->csrfTokens->dumpTokens($tokens);
|
||
|
$tokenSegmentKey = $this->getSegmentKey(self::CSRF_SEGMENTKEY);
|
||
|
if (is_array($tokens) && count($tokens) > 0) {
|
||
|
$this->data[$tokenSegmentKey] = $tokens;
|
||
|
} else {
|
||
|
unset($this->data[$tokenSegmentKey]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return FlashMessageCollection
|
||
|
*/
|
||
|
private function createFlashMessageCollection()
|
||
|
{
|
||
|
$key = $this->getSegmentKey(self::FLASH_SEGMENTKEY);
|
||
|
if (!array_key_exists($key, $this->data)) {
|
||
|
return new FlashMessageCollection();
|
||
|
}
|
||
|
$messages = $this->data[$key];
|
||
|
$result = new FlashMessageCollection($messages);
|
||
|
unset($this->data[$key]);
|
||
|
$this->data[$key] = [];
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return CsrfTokenManager
|
||
|
*/
|
||
|
private function createCsrfTokenManager()
|
||
|
{
|
||
|
$key = $this->getSegmentKey(self::CSRF_SEGMENTKEY);
|
||
|
if (!array_key_exists($key, $this->data)) {
|
||
|
return new CsrfTokenManager();
|
||
|
}
|
||
|
$tokens = $this->data[$key];
|
||
|
$result = new CsrfTokenManager($tokens);
|
||
|
unset($this->data[$key]);
|
||
|
$this->data[$key] = [];
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $name
|
||
|
*
|
||
|
* @throws InvalidArgumentException
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
private function ensureSegmentNameFormat($name)
|
||
|
{
|
||
|
if (!preg_match('/^[a-z][a-z0-9_]*$/', $name)) {
|
||
|
throw new InvalidArgumentException('Invalid segment name format.');
|
||
|
}
|
||
|
}
|
||
|
}
|