Skip to content

Instantly share code, notes, and snippets.

@pentagonal
Last active December 1, 2017 06:05
Show Gist options
  • Save pentagonal/de7a7ef857d233cd0c61ed81707d8064 to your computer and use it in GitHub Desktop.
Save pentagonal/de7a7ef857d233cd0c61ed81707d8064 to your computer and use it in GitHub Desktop.
Phalcon Cache Class Setup for confusing configuration
<?php
/**
* MIT License
*
* Copyright (c) 2017, Pentagonal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace MyApplication;
use SplFileInfo;
use Phalcon;
use Phalcon\Cache\Backend;
use Phalcon\Cache\Frontend;
use Phalcon\Config;
/** @noinspection PhpHierarchyChecksInspection */
/**
* Cache class to easily setup cache container for phalcon
*
* Class Cache
* @package MyApplication
*
* @mixin Phalcon\Cache\BackendInterface
*/
class Cache
{
const ADAPTER_DEFAULT = self::ADAPTER_FILE;
const FRONT_ADAPTER_DEFAULT = self::FRONT_ADAPTER_NONE;
const
ADAPTER_FILE = 'file',
ADAPTER_MEMORY = 'memory',
ADAPTER_APCU = 'apcu',
ADAPTER_XCACHE = 'xcache',
ADAPTER_REDIS = 'redis',
ADAPTER_MEMCACHE = 'memcache',
ADAPTER_LIBMEMCACHED = 'libmemcached',
ADAPTER_MONGO = 'mongo';
const
FRONT_ADAPTER_NONE = 'none',
FRONT_ADAPTER_DATA = 'data',
FRONT_ADAPTER_BINARY = 'igbinary',
FRONT_ADAPTER_PACK = 'msgpack',
FRONT_ADAPTER_JSON = 'json',
FRONT_ADAPTER_OUTPUT = 'output',
FRONT_ADAPTER_BASE64 = 'base64';
/**
* @var array
*/
private static $frontEndAdapters = [
self::FRONT_ADAPTER_NONE => Frontend\None::class,
self::FRONT_ADAPTER_DATA => Frontend\Data::class,
self::FRONT_ADAPTER_BINARY => Frontend\Igbinary::class,
self::FRONT_ADAPTER_PACK => Frontend\Msgpack::class,
self::FRONT_ADAPTER_JSON => Frontend\Json::class,
self::FRONT_ADAPTER_OUTPUT => Frontend\Output::class,
self::FRONT_ADAPTER_BASE64 => Frontend\Base64::class,
];
/**
* @var string
*/
protected $defaultCachePath;
/**
* @var Phalcon\Cache\BackendInterface
*/
private $cache;
/**
* @return Phalcon\Cache\BackendInterface
*/
public function getCache() : Phalcon\Cache\BackendInterface
{
return $this->cache;
}
/**
* Cache constructor.
*
* @param $options
*/
public function __construct($options)
{
if ($options instanceof Config) {
$options = $options->toArray();
}
if ( ! is_array($options)) {
throw new \InvalidArgumentException(
"Options must be as array or config",
E_WARNING
);
}
/**
* Set Default Cache Path
*/
$this->defaultCachePath = __DIR__ . '/../../Storage/Cache';
$this->defaultCachePath = realpath($this->defaultCachePath)?: $this->defaultCachePath;
$this->defaultCachePath .= DIRECTORY_SEPARATOR;
$this->cache = $this->createFromOptions($options, $this->defaultCachePath);
}
/**
* Configure Options
*
* @param array $options
* @param string $defaultCachePath
*
* @return array
*/
public static function configureForOptions(array $options, string $defaultCachePath) : array
{
$currentUserParams = [];
$array = [
'adapter',
'driver',
'cacheAdapter',
'cacheDriver',
];
foreach ($array as $key) {
if (isset($options[$key]) && is_string($options[$key])) {
$currentUserParams['adapter'] = self::sanityadapter($options[$key]);
break;
}
$key = strtolower($key);
if (isset($options[$key]) && is_string($options[$key])) {
$currentUserParams['adapter'] = self::sanityadapter($options[$key]);
break;
}
}
if ( ! isset($currentUserParams['adapter'])) {
$currentUserParams['adapter'] = static::ADAPTER_DEFAULT;
}
if ($currentUserParams['adapter'] === self::ADAPTER_FILE) {
$array = [
'cacheDir',
'cachedir',
'cachePath',
'cachepath',
'Dir',
'dir',
'Path',
'path',
];
foreach ($array as $key) {
if (isset($options[$key]) && is_string($options[$key])) {
$cacheDir = new SplFileInfo($options[$key]);
if ($cacheDir->isDir() && $cacheDir->isWritable()) {
$currentUserParams['cacheDir'] = $cacheDir->getRealPath() . DIRECTORY_SEPARATOR;
break;
}
}
}
if ( ! isset($currentUserParams['cacheDir'])) {
$currentUserParams['cacheDir'] = $defaultCachePath;
}
}
$array = ['prefix', 'cachePrefix', 'Prefix', 'cacheprefix'];
$currentUserParams['prefix'] = "";
foreach ($array as $key) {
if (isset($options[$key]) && is_string($options[$key])) {
$currentUserParams['prefix'] = $options[$key];
break;
}
}
$defaultPort = [
self::ADAPTER_REDIS => 6379,
self::ADAPTER_MEMCACHE => 11211,
self::ADAPTER_LIBMEMCACHED => 11211,
];
if (isset($defaultPort[$currentUserParams['adapter']])) {
$array = [
'port',
'Port',
'cachePort',
'cacheport',
];
$currentUserParams['port'] = $defaultPort[$currentUserParams['adapter']];
foreach ($array as $key) {
if (isset($options[$key]) && is_numeric($options[$key])
&& is_int(abs($options[$key]))
) {
$currentUserParams['port'] = abs($options[$key]);
break;
}
}
$currentUserParams['host'] = 'localhost';
$array = [
'host',
'Host',
'cacheHost',
'cachehost',
];
foreach ($array as $key) {
if (isset($options[$key]) && is_string($options[$key])
&& trim($options[$key]) !== ''
) {
$currentUserParams['host'] = $options[$key];
break;
}
}
}
$array = [
'persistent',
'cachePersistent',
'Persistent',
];
foreach ($array as $key) {
if (isset($options[$key])) {
$currentUserParams['persistent'] = (bool) $options[$key];
break;
}
}
$array = [
'index',
'cacheIndex',
'Index',
];
foreach ($array as $key) {
if (isset($options[$key])) {
$currentUserParams['index'] = $options[$key];
break;
}
}
if ($currentUserParams['adapter'] === self::ADAPTER_MONGO) {
$currentUserParams['server'] = $currentUserParams['host'];
$array = [
'server',
'cacheServer',
'Server',
];
foreach ($array as $key) {
if (isset($options[$key])) {
$currentUserParams['server'] = $options[$key];
break;
}
}
$array = [
'collection',
'cacheCollection',
'Collection',
];
foreach ($array as $key) {
if (isset($options[$key])) {
$currentUserParams['collection'] = $options[$key];
break;
}
}
$array = [
'db',
'dbName',
'dbname',
'cacheDb',
'cacheDB',
'cachedb',
];
foreach ($array as $key) {
if (isset($options[$key])) {
$currentUserParams['db'] = $options[$key];
break;
}
}
}
$array = [
'frontend',
'frontEnd',
'cacheFrontEnd',
'front',
'Front',
'cacheFront',
'cachefront',
];
$adapter = self::$frontEndAdapters[static::FRONT_ADAPTER_DEFAULT];
$currentUserParams['frontend'] = new $adapter();
foreach ($array as $key) {
if (isset($options[$key])) {
if (is_object($options[$key]) && $options[$key] instanceof Phalcon\Cache\FrontendInterface) {
$currentUserParams['frontend'] = $options[$key];
break;
}
if (is_string($options[$key])) {
$adapter = self::$frontEndAdapters[self::sanityFrontEndAdapter($options[$key])];
$currentUserParams['frontend'] = new $adapter();
break;
}
}
}
$array = [
'lifetime',
'lifeTime',
'cacheLifeTime',
'cachelifeTime',
'cachelifetime',
];
foreach ($array as $key) {
if (array_key_exists($key, $options)
&& is_numeric($options[$key])
) {
$currentUserParams['lifetime'] = $options[$key];
break;
}
}
$array = ['auth', 'Auth', 'cacheAuth', 'Auth'];
foreach ($array as $key) {
if (isset($options[$key])) {
$currentUserParams['auth'] = $options[$key];
break;
}
}
if ($currentUserParams['adapter'] === self::ADAPTER_REDIS) {
$array = [
'statsKey',
'statskey',
'statKey',
'statkey',
'stat',
];
foreach ($array as $key) {
if (isset($options[$key]) && is_string($options[$key])) {
$currentUserParams['statsKey'] = $options[$key];
break;
}
}
}
if ($currentUserParams['adapter'] === self::ADAPTER_LIBMEMCACHED) {
// sanitize
$array = ['client', 'frontendClient', 'frontEndClient', 'frontendclient'];
foreach ($array as $key) {
if (isset($options[$key]) && is_array($options[$key])) {
$currentUserParams['client'] = $options[$key];
foreach ($currentUserParams['client'] as $k => $v) {
if (is_string($k) && defined($k)) {
$newK = constant($k);
if (is_int($newK)) {
$k = $newK;
}
}
if (is_int($k)) {
continue;
}
unset($currentUserParams['client'][$k]);
}
break;
}
}
$libMemcacheOptions = [
'host' => $currentUserParams['host'],
'port' => $currentUserParams['port']
];
$array = ['weight', 'cacheWeight', 'weight', 'frontendclient'];
foreach ($array as $key) {
if (isset($options[$key]) && is_numeric($options[$key])) {
$libMemcacheOptions['weight'] = abs($options[$key]);
break;
}
}
$array = [
'persistent_id',
'persistentId',
'id',
'Id',
];
foreach ($array as $key) {
if (isset($options[$key]) && is_string($options[$key])) {
$currentUserParams['persistent_id'] = $options[$key];
}
}
}
return $currentUserParams;
}
/**
* Create Cache from options
*
* @param array $options
* @param string $defaultCachePath
*
* @return Phalcon\Cache\BackendInterface
*/
public static function createFromOptions(array $options, string $defaultCachePath) : Phalcon\Cache\BackendInterface
{
return Backend\Factory::load(self::configureForOptions($options, $defaultCachePath));
}
/**
* @param string $adapterName
*
* @return string
*/
public static function sanityAdapter(string $adapterName): string
{
$default = [
'file' => self::ADAPTER_FILE,
'memory' => self::ADAPTER_MEMORY,
'apcu' => self::ADAPTER_APCU,
'xcache' => self::ADAPTER_XCACHE,
'redis' => self::ADAPTER_REDIS,
'memcache' => self::ADAPTER_MEMCACHE,
'libmemcached' => self::ADAPTER_LIBMEMCACHED,
'mongo' => self::ADAPTER_MONGO
];
$selectedAdapter = static::ADAPTER_DEFAULT;
preg_match('~
(?P<apcu>apc)
| (?P<libmemcached>libmem)
| (?P<redis>red)
| (?P<mongo>mong)
| (?P<memcache>memc)
| (?P<xcache>xca?)
| (?P<memory>mem)
| (?P<file>file?)
~ix', $adapterName, $match);
foreach ((array)$match as $key => $value) {
if (is_string($key) && ! empty($value)) {
$selectedAdapter = $default[$key];
break;
}
}
return $selectedAdapter;
}
/**
* Sanity for front End Adapter
*
* @param string $adapterName
*
* @return string
*/
public static function sanityFrontEndAdapter(string $adapterName): string
{
/**
* list available adapter
*/
$default = [
'none' => self::FRONT_ADAPTER_NONE,
'dData' => self::FRONT_ADAPTER_DATA,
'igbinary' => self::FRONT_ADAPTER_BINARY,
'msgpack' => self::FRONT_ADAPTER_PACK,
'json' => self::FRONT_ADAPTER_JSON,
'output' => self::FRONT_ADAPTER_OUTPUT,
'base64' => self::FRONT_ADAPTER_BASE64,
];
$selectedAdapter = static::FRONT_ADAPTER_DEFAULT;
preg_match('~
(?P<dData>dat)
| (?P<igbinary>bin)
| (?P<msgpack>(?:pack|msg))
| (?P<json>js)
| (?P<output>out)
| (?P<base64>base)
| (?P<none>no)
~ix', $adapterName, $match);
foreach ((array)$match as $key => $value) {
if (is_string($key) && ! empty($value)) {
$selectedAdapter = $default[$key];
break;
}
}
return $selectedAdapter;
}
/**
* Magic Method for Backend Interface
* @see Phalcon\Cache\BackendInterface
*
* @param string $name
* @param array $arguments
*
* @return mixed
*/
public function __call(string $name, array $arguments)
{
return call_user_func_array([$this->cache, $name], $arguments);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment