WEBD-325-45/week-03/project/libraries/src/Session/MetadataManager.php

326 lines
7.7 KiB
PHP

<?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Octoleo\CMS\Session;
use Octoleo\CMS\User\User;
use Joomla\Database\DatabaseInterface;
use Joomla\Database\Exception\ExecutionFailureException;
use Joomla\Database\ParameterType;
use Joomla\Session\SessionInterface;
/**
* Manager for optional session metadata.
*
* @since 3.8.6
* @internal
*/
final class MetadataManager
{
/**
* Internal variable indicating a session record exists.
*
* @var integer
* @since 4.0.0
* @note Once PHP 7.1 is the minimum supported version this should become a private constant
*/
private static $sessionRecordExists = 1;
/**
* Internal variable indicating a session record does not exist.
*
* @var integer
* @since 4.0.0
* @note Once PHP 7.1 is the minimum supported version this should become a private constant
*/
private static $sessionRecordDoesNotExist = 0;
/**
* Internal variable indicating an unknown session record statue.
*
* @var integer
* @since 4.0.0
* @note Once PHP 7.1 is the minimum supported version this should become a private constant
*/
private static $sessionRecordUnknown = -1;
/**
* Database driver.
*
* @var DatabaseInterface
* @since 3.8.6
*/
private $db;
/**
* MetadataManager constructor.
*
* @param DatabaseInterface $db Database driver.
*
* @since 3.8.6
*/
public function __construct(DatabaseInterface $db)
{
$this->db = $db;
}
/**
* Create the metadata record if it does not exist.
*
* @param SessionInterface $session The session to create the metadata record for.
* @param User $user The user to associate with the record.
*
* @return void
*
* @since 3.8.6
* @throws \RuntimeException
*/
public function createRecordIfNonExisting(SessionInterface $session, User $user)
{
$exists = $this->checkSessionRecordExists($session->getId());
// Only touch the database if the record does not already exist
if ($exists !== self::$sessionRecordExists)
{
return;
}
$this->createSessionRecord($session, $user);
}
/**
* Create the metadata record if it does not exist.
*
* @param SessionInterface $session The session to create or update the metadata record for.
* @param User $user The user to associate with the record.
*
* @return void
*
* @since 4.0.0
* @throws \RuntimeException
*/
public function createOrUpdateRecord(SessionInterface $session, User $user)
{
$exists = $this->checkSessionRecordExists($session->getId());
// Do not try to touch the database if we can't determine the record state
if ($exists === self::$sessionRecordUnknown)
{
return;
}
if ($exists === self::$sessionRecordDoesNotExist)
{
$this->createSessionRecord($session, $user);
return;
}
$this->updateSessionRecord($session, $user);
}
/**
* Delete records with a timestamp prior to the given time.
*
* @param integer $time The time records should be deleted if expired before.
*
* @return void
*
* @since 3.8.6
*/
public function deletePriorTo($time)
{
$query = $this->db->getQuery(true)
->delete($this->db->quoteName('#__session'))
->where($this->db->quoteName('time') . ' < :time')
->bind(':time', $time, ParameterType::INTEGER);
$this->db->setQuery($query);
try
{
$this->db->execute();
}
catch (ExecutionFailureException $exception)
{
// Since garbage collection does not result in a fatal error when run in the session API, we don't allow it here either.
}
}
/**
* Get session record exists
*
* @param string $sessionId The session ID to check
*
* @return mixed on success value for record presence
*
* @since 1.0.0
*/
public function getSessionRecord(string $sessionId)
{
$query = $this->db->getQuery(true)
->select('*')
->from($this->db->quoteName('#__session'))
->where($this->db->quoteName('session_id') . ' = :session_id')
->bind(':session_id', $sessionId)
->setLimit(1);
$this->db->setQuery($query);
return $this->db->loadObject();
}
/**
* Check if the session record exists
*
* @param string $sessionId The session ID to check
*
* @return integer Status value for record presence
*
* @since 4.0.0
*/
private function checkSessionRecordExists(string $sessionId): int
{
$query = $this->db->getQuery(true)
->select($this->db->quoteName('session_id'))
->from($this->db->quoteName('#__session'))
->where($this->db->quoteName('session_id') . ' = :session_id')
->bind(':session_id', $sessionId)
->setLimit(1);
$this->db->setQuery($query);
try
{
$exists = $this->db->loadResult();
}
catch (ExecutionFailureException $e)
{
return self::$sessionRecordUnknown;
}
if ($exists)
{
return self::$sessionRecordExists;
}
return self::$sessionRecordDoesNotExist;
}
/**
* Create the session record
*
* @param SessionInterface $session The session to create the metadata record for.
* @param User $user The user to associate with the record.
*
* @return void
*
* @since 4.0.0
*/
private function createSessionRecord(SessionInterface $session, User $user)
{
$query = $this->db->getQuery(true);
$time = $session->isNew() ? time() : $session->get('session.timer.start');
$columns = [
$this->db->quoteName('session_id'),
$this->db->quoteName('guest'),
$this->db->quoteName('time'),
$this->db->quoteName('userid'),
$this->db->quoteName('username'),
];
// Add query placeholders
$values = [
':session_id',
':guest',
':time',
':user_id',
':username',
];
// Bind query values
$sessionId = $session->getId();
$userIsGuest = $user->get('guest', 0);
$userId = $user->get('id', 0);
$username = $user->get('username', '');
$query->bind(':session_id', $sessionId)
->bind(':guest', $userIsGuest, ParameterType::INTEGER)
->bind(':time', $time)
->bind(':user_id', $userId, ParameterType::INTEGER)
->bind(':username', $username);
$query->insert($this->db->quoteName('#__session'))
->columns($columns)
->values(implode(', ', $values));
$this->db->setQuery($query);
try
{
$this->db->execute();
}
catch (ExecutionFailureException $e)
{
// This failure isn't critical, we can go on without the metadata
}
}
/**
* Update the session record
*
* @param SessionInterface $session The session to update the metadata record for.
* @param User $user The user to associate with the record.
*
* @return void
*
* @since 4.0.0
*/
private function updateSessionRecord(SessionInterface $session, User $user)
{
$query = $this->db->getQuery(true);
$time = time();
$setValues = [
$this->db->quoteName('guest') . ' = :guest',
$this->db->quoteName('time') . ' = :time',
$this->db->quoteName('userid') . ' = :user_id',
$this->db->quoteName('username') . ' = :username',
];
// Bind query values
$sessionId = $session->getId();
$userIsGuest = $user->get('guest', 0);
$userId = $user->get('id', 0);
$username = $user->get('username', '');
$query->bind(':session_id', $sessionId)
->bind(':guest', $userIsGuest, ParameterType::INTEGER)
->bind(':time', $time)
->bind(':user_id', $userId, ParameterType::INTEGER)
->bind(':username', $username);
$query->update($this->db->quoteName('#__session'))
->set($setValues)
->where($this->db->quoteName('session_id') . ' = :session_id');
$this->db->setQuery($query);
try
{
$this->db->execute();
}
catch (ExecutionFailureException $e)
{
// This failure isn't critical, we can go on without the metadata
}
}
}