<?php
/**
 * Part of the Joomla Framework Session Package
 *
 * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE
 */

namespace Joomla\Session\Handler;

use Joomla\Session\HandlerInterface;

/**
 * Memcached session storage handler
 *
 * @since  2.0.0
 */
class MemcachedHandler implements HandlerInterface
{
	/**
	 * Memcached driver
	 *
	 * @var    \Memcached
	 * @since  2.0.0
	 */
	private $memcached;

	/**
	 * Session ID prefix to avoid naming conflicts
	 *
	 * @var    string
	 * @since  2.0.0
	 */
	private $prefix;

	/**
	 * Time to live in seconds
	 *
	 * @var    integer
	 * @since  2.0.0
	 */
	private $ttl;

	/**
	 * Constructor
	 *
	 * @param   \Memcached  $memcached  A Memcached instance
	 * @param   array       $options    Associative array of options to configure the handler
	 *
	 * @since   2.0.0
	 */
	public function __construct(\Memcached $memcached, array $options = [])
	{
		$this->memcached = $memcached;

		// Set the default time-to-live based on the Session object's default configuration
		$this->ttl = isset($options['ttl']) ? (int) $options['ttl'] : 900;

		// Namespace our session IDs to avoid potential conflicts
		$this->prefix = $options['prefix'] ?? 'jfw';
	}

	/**
	 * Close the session
	 *
	 * @return  boolean  True on success, false otherwise
	 *
	 * @since   2.0.0
	 */
	#[\ReturnTypeWillChange]
	public function close()
	{
		return true;
	}

	/**
	 * Destroy a session
	 *
	 * @param   integer  $session_id  The session ID being destroyed
	 *
	 * @return  boolean  True on success, false otherwise
	 *
	 * @since   2.0.0
	 */
	public function destroy($session_id): bool
	{
		return $this->memcached->delete($this->prefix . $session_id);
	}

	/**
	 * Cleanup old sessions
	 *
	 * @param   integer  $maxlifetime  Sessions that have not updated for the last maxlifetime seconds will be removed
	 *
	 * @return  boolean  True on success, false otherwise
	 *
	 * @since   2.0.0
	 */
	#[\ReturnTypeWillChange]
	public function gc($maxlifetime)
	{
		// Memcached manages garbage collection on its own
		return true;
	}

	/**
	 * Test to see if the HandlerInterface is available
	 *
	 * @return  boolean  True on success, false otherwise
	 *
	 * @since   2.0.0
	 */
	public static function isSupported(): bool
	{
		/*
		 * GAE and HHVM have both had instances where Memcached the class was defined but no extension was loaded.
		 * If the class is there, we can assume it works.
		 */
		return class_exists('Memcached');
	}

	/**
	 * Initialize session
	 *
	 * @param   string  $save_path   The path where to store/retrieve the session
	 * @param   string  $session_id  The session id
	 *
	 * @return  boolean  True on success, false otherwise
	 *
	 * @since   2.0.0
	 */
	#[\ReturnTypeWillChange]
	public function open($save_path, $session_id)
	{
		return true;
	}

	/**
	 * Read session data
	 *
	 * @param   string  $session_id  The session id to read data for
	 *
	 * @return  string  The session data
	 *
	 * @since   2.0.0
	 */
	#[\ReturnTypeWillChange]
	public function read($session_id)
	{
		return $this->memcached->get($this->prefix . $session_id) ?: '';
	}

	/**
	 * Write session data
	 *
	 * @param   string  $session_id    The session id
	 * @param   string  $session_data  The encoded session data
	 *
	 * @return  boolean  True on success, false otherwise
	 *
	 * @since   2.0.0
	 */
	#[\ReturnTypeWillChange]
	public function write($session_id, $session_data)
	{
		return $this->memcached->set($this->prefix . $session_id, $session_data, time() + $this->ttl);
	}
}
