You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
5.4 KiB

<?php
namespace authkit2;
/**
* Internal class for providing a single interface integrating with native PHP,
* Laravel, and any other environment we need to support.
*
* @method static mixed cache(string $key, callable $generator)
* @method static mixed cache_get(string $key, mixed $default = null)
* @method static void cache_set(string $key, mixed $value)
* @method static mixed session_get(string $key)
* @method static void session_set(string $key, mixed $value)
*/
class Authkit2
{
/**
* All data we shove into the session/cache will have its key prefixed
* with this value.
* @var string
*/
private const LIB_PREFIX = 'authkit2.';
/**
* Functions this class provides
*
* @array<string,callable>
*/
protected $callbacks = [];
/**
* Try and detect if we recognize the environment the library is running
* in and adjust our implementations accordingly.
*
* Basically, if we see the LARAVEL_START constant we assume Laravel and
* use Laravel facades, otherwise we use native php implementations.
*
*/
protected function __construct()
{
if (defined('LARAVEL_START'))
{
$this->initializeLaravel();
}
else
{
$this->initializeNative();
}
}
/**
* Retrieve the instance of Authkit2 class
*
* @return Authkit2
*/
public static function get(): Authkit2
{
static $authkit2;
if (!isset($authkit2))
{
$authkit2 = new Authkit2();
}
return $authkit2;
}
/**
* Override any of the function implementations
*
* Name is the same as the callable function name, e.g.,
* Authkit2::cache_set() can be overriden with Authkit2->cache_set = function(...) {}
*
* @param string $name
* @param callable $value
* @return void
*/
public function __set(string $name, $value): void
{
if (!array_key_exists($name, $this->callbacks))
{
trigger_error('Undefined property: '.__CLASS__.'::$'.$name, E_USER_WARNING);
return;
}
if (!is_callable($value))
{
throw new \Exception('Authkit2::'.$name.' value must be callable');
}
$this->callbacks[$name] = $value;
}
/**
* Call any of the provided methods
*
* @param string $name
* @param mixed[] $arguments
* @return mixed
*/
public static function __callStatic(string $name, array $arguments)
{
$authkit2 = static::get();
if (!isset($authkit2->callbacks[$name]))
{
trigger_error('Call to undefined method '.__CLASS__.'::'.$name.'()', E_USER_ERROR);
}
return call_user_func_array($authkit2->callbacks[$name], $arguments);
}
/**
* Helper method for getting cache values, and generating and setting if
* they do not exist.
*
* @param string $key cache key
* @param callable $generator method that returns the value if we do not have it cached
* @return mixed
*/
protected function cache_helper(string $key, callable $generator)
{
$value = static::cache_get($key, null);
if (!isset($value))
{
$value = $generator();
static::cache_set($key, $value);
}
return $value;
}
/**
* Initialize the class by binding all the PHP native implementations of
* functions
*
* @return void
*/
protected function initializeNative(): void
{
$this->callbacks = [
'session_get' => [$this, 'native_session_get'],
'session_set' => [$this, 'native_session_set'],
'cache_get' => [$this, 'native_cache_get'],
'cache_set' => [$this, 'native_cache_set'],
'cache' => [$this, 'cache_helper']
];
}
/**
* Initialize the class by binding Laravel adapters as the implementation
* of all functions
*
* @return void
*/
protected function initializeLaravel(): void
{
$this->callbacks = [
'session_get' => function(string $key) { return \Session::get($key); },
'session_set' => function(string $key, $value) { \Session::put($key, $value); },
'cache_get' => function(string $key) { return \Cache::get($key); },
'cache_set' => function(string $key, $value) { \Cache::set($key, $value); },
'cache' => [$this, 'cache_helper']
];
}
/**
* Retrieve a property out of the $_SESSION variable; null if the
* property doesn't exist.
*
* @param string $key
* @return mixed
*/
protected function native_session_get(string $key)
{
$this->native_session_check();
return $_SESSION[static::LIB_PREFIX.$key] ?? null;
}
/**
* Set a value in the $_SESSION variable
*
* @param string $key
* @param mixed $value
* @return void
*/
protected function native_session_set(string $key, $value): void
{
$this->native_session_check();
$_SESSION[static::LIB_PREFIX.$key] = $value;
}
/**
* Check whether a PHP session exists, and if not try and start one
*
* @internal
* @return void
*/
protected function native_session_check(): void
{
if (session_status() == \PHP_SESSION_NONE)
session_start();
else if (session_status() == \PHP_SESSION_DISABLED)
throw new \Exception("Authkit2 requires PHP sessions are enabled");
}
/**
* Dummy cache implementation to avoid errors; always returns default
*
* @todo Check if apcu is available and use if so? Fall back to temp files?
* @param string $key cache key to retrieve
* @param mixed $default value to return if the specified key is not found
* @return mixed
*/
protected function native_cache_get(string $key, $default = null)
{
return $default;
}
/**
* Dummy cache implementation
*
* @param string $key cache key to set
* @param mixed $value value to cache
* @return void
*/
protected function native_cache_set(string $key, $value): void
{
}
}