diff --git a/aeria.php b/aeria.php index 58e361a..3c9a61a 100755 --- a/aeria.php +++ b/aeria.php @@ -5,7 +5,7 @@ * Author: Caffeina Srl * Author URI: http://caffeina.co * Plugin URI: https://github.com/CaffeinaLab/aeria - * Version: 1.2.0 + * Version: 1.3.0 */ // Exit if accessed directly diff --git a/classes/Aeria.php b/classes/Aeria.php old mode 100755 new mode 100644 diff --git a/classes/AeriaAJAX.php b/classes/AeriaAJAX.php new file mode 100644 index 0000000..6b58cdb --- /dev/null +++ b/classes/AeriaAJAX.php @@ -0,0 +1,74 @@ + AERIA_HOME_URL.'index.php' ]); + +// Install Ajax Handler +add_filter('query_vars',function($vars) { + $vars[] = 'ajax'; + return $vars; +}); + +add_action('template_redirect', function() { + if ($action = get_query_var('ajax')) { + ini_set('zlib.output_compression','On'); + + if (!defined('DOING_AJAX')) { + define('DOING_AJAX', true); + } + + send_nosniff_header(); + + $action = $action; + $args = $_REQUEST; + $user_logged = is_user_logged_in(); + + $privateHook = 'AERIA_AJAX_HANDLER_private_'.$action; + $publicHook = 'AERIA_AJAX_HANDLER_public_'.$action; + + unset($args['ajax']); + ob_end_clean(); + + if ($user_logged && AeriaAJAX::existsPrivate($action)) { + do_action($privateHook,$args); + } elseif (AeriaAJAX::exists($action)) { + do_action($publicHook,$args); + } + + exit; + } +}); + +class AeriaAJAX { + + public static $registry = array(); + + public static function exists($function_name){ + return isset(static::$registry['AERIA_AJAX_HANDLER_public_'.$function_name]); + } + + public static function existsPrivate($function_name){ + return isset(static::$registry['AERIA_AJAX_HANDLER_private_'.$function_name]); + } + + public static function register($function_name,$callback){ + $key='AERIA_AJAX_HANDLER_public_'.$function_name; + static::$registry[$key] = $callback; + add_action($key,$callback); + } + + public static function registerPrivate($function_name,$callback){ + $key='AERIA_AJAX_HANDLER_private_'.$function_name; + static::$registry[$key] = $callback; + add_action($key,$callback); + } + + public static function sendJSON($payload){ + ob_end_clean(); + header('Content-Type: application/json'); + die(json_encode($payload,JSON_NUMERIC_CHECK)); + } + +} diff --git a/classes/AeriaCache.php b/classes/AeriaCache.php new file mode 100644 index 0000000..e12b749 --- /dev/null +++ b/classes/AeriaCache.php @@ -0,0 +1,164 @@ +getMessage()); + } +} + +class AeriaCacheRedis { + + public static function & redis() { + return redis(); + } + + public static function get($key,$group=false,$default=null) { + $r = redis(); + + try { + $v = $group ? $r->hget($group,$key) : $r->get($key); + } catch(Exception $e) { + $v = null; + } + + if (null === $v && $default){ + $v = is_callable($default) ? call_user_func($default) : $default; + static::set($v, $key, $group); + } + + return $v; + + } + + public static function set($data,$key,$group=false,$expire=0) { + $r = redis(); + try { + if ($group) { + return $r->hset($group, $key, $data); + } else { + if (!$expire) return $r->set($key, $data); + else return $r->setex($key, $expire, $data); + } + } catch(Exception $e) { + return null; + } + } + + public static function delete($key,$group=false) { + $r = redis(); + try { + return $group ? $r->hdel($group,$key) : $r->del($key); + } catch(Exception $e) { + return null; + } + } + + public static function deleteGroup($group) { + $r = redis(); + try { + return $r->delete($group); + } catch(Exception $e) { + return null; + } + } + + public static function clear() { + $r = redis(); + return $r->flushall(); + } + +} + + +class AeriaCacheBypass { + + public static function get($key,$group='') { return false; } + + public static function set($data,$key,$group='',$expire=0) {} + + public static function delete($key,$group='') {} + + public static function deleteGroup($group) {} + + public static function clear() {} + +} diff --git a/classes/AeriaMetabox.php b/classes/AeriaMetabox.php old mode 100755 new mode 100644 diff --git a/classes/AeriaNetwork.php b/classes/AeriaNetwork.php new file mode 100644 index 0000000..faa364d --- /dev/null +++ b/classes/AeriaNetwork.php @@ -0,0 +1,65 @@ + $v) curl_setopt($curl, $k, $v); + + try { + $content = curl_exec($ch); + if (curl_errno($ch)) throw new Exception(curl_error($ch)); + if (empty($content)) throw new Exception("Empty response"); + } catch (Exception $e) { + curl_close($ch); + AeriaDebug::exception($e); + static::$errors[] = $e->getMessage(); + return null; + } + + curl_close($ch); + return $content; + } + + public static function json() + { + $response = forward_static_call_array(['static','send'], func_get_args()); + $json = json_decode($response); + if (json_last_error() || empty($json)) return null; + return $json; + } + + public static function jsonp() + { + $response = forward_static_call_array(['static','send'], func_get_args()); + $response = str_replace('cb({', '{', $response); + $response = str_replace('})','}', $response); + $json = json_decode($response); + if (json_last_error() || empty($json)) return null; + return $json; + } + +} diff --git a/classes/AeriaPost.php b/classes/AeriaPost.php old mode 100755 new mode 100644 diff --git a/classes/AeriaSocial.php b/classes/AeriaSocial.php old mode 100755 new mode 100644 index 541b6b2..a7336d5 --- a/classes/AeriaSocial.php +++ b/classes/AeriaSocial.php @@ -2,6 +2,17 @@ // Exit if accessed directly. if( false === defined('AERIA') ) exit; +wp_enqueue_script('aeria.social', AERIA_URL.'resources/js/aeria.social.js', ['jquery']); +wp_enqueue_style('aeria.social', AERIA_URL.'resources/css/aeria.social.css'); + +AeriaAJAX::register('aeriasocial.get', function(){ + if (!isset($_REQUEST['uri'])) { + die(json_encode([ 'error' => 'Please provide a URI' ])); + } + + die(json_encode(AeriaSocial::getCount($_REQUEST['uri']))); +}); + class AeriaSocial { public static $services = []; @@ -21,18 +32,9 @@ public static function init($config = null) { if (is_array($config)) static::$config = array_merge(static::$config, $config); static::$services = array_keys(static::$config['services']); - add_action('wp_enqueue_scripts', function(){ - wp_enqueue_script('aeria.social', AERIA_URL.'/scripts/aeria.social.js'); - wp_enqueue_style('aeria.social', AERIA_URL.'/resources/css/aeria.social.css'); - if (isset(static::$config['apiurl'])) { - echo ''; - } - }); - - AeriaAJAX::register('aeriasocial.get', function(){ - if (!isset($_REQUEST['uri'])) die(json_encode([ 'error' => 'Please provide a URI' ])); - die(json_encode(static::getCount($_REQUEST['uri']))); - }); + if (isset(static::$config['apiurl'])) { + wp_localize_script('aeria.social', 'AERIA_SOCIAL', [ 'URL' => static::$config['apiurl'] ]); + } } public static function widget($uri, $info=[], $opt=[]) { @@ -57,7 +59,7 @@ public static function widget($uri, $info=[], $opt=[]) { if ( ! isset($opt['nocount'])) { $r .= ''; $r .= ''; - if (!is_null($stats)) $r .= $parsed_stats['services'][$service]; + if (!is_null($stats)) $r .= (string)$stats['services'][$service]; $r .= ''; $r .= ''; } @@ -177,4 +179,4 @@ public static function getMostSharedContents() { return $data; } -} +} \ No newline at end of file diff --git a/classes/AeriaTaxonomy.php b/classes/AeriaTaxonomy.php old mode 100755 new mode 100644 diff --git a/classes/AeriaType.php b/classes/AeriaType.php old mode 100755 new mode 100644 diff --git a/metadata.json b/metadata.json index cedad53..a9b3bdd 100644 --- a/metadata.json +++ b/metadata.json @@ -4,7 +4,7 @@ "homepage": "http://labs.caffeina.co/tech/aeria", "download_url": "https://github.com/CaffeinaLab/aeria/archive/master.zip", - "version": "1.2.0", + "version": "1.3.0", "requires": "3.5", "tested": "4.0", "last_updated": "2014-12-05 11:00:00", diff --git a/resources/css/aeria.social.css b/resources/css/aeria.social.css new file mode 100644 index 0000000..b44dcd5 --- /dev/null +++ b/resources/css/aeria.social.css @@ -0,0 +1,224 @@ +.aeriasocial-container .aeriasocial-btns { + padding: 10px; + height: 40px; + background: #eee; +} + +.aeriasocial-btn { + display: inline-block; + margin: 0 4px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.aeriasocial-btn .aeriasocial-claim { + border-radius: 3px; + border: 1px solid black; + font-weight: bold; + cursor: pointer; + text-decoration: none; + color: #222; + font-size: 11px; + float: left; + padding: 2px 6px; + line-height: 20px; + height: 20px; + background: #ddd; + -webkit-font-smoothing: antialiased; + position: relative; +} + +.aeriasocial-btn .aeriasocial-claim:hover { + opacity: .8 +} + +.aeriasocial-btn .aeriasocial-icon { + width: 14px; + height: 14px; + display: inline-block; + margin-right: 2px; +} + +.aeriasocial-btn .aeriasocial-text { + display: inline-block; + vertical-align: top; + height: 14px; + line-height: 14px; +} + +.aeriasocial-btn .aeriasocial-count { + background: #fff; + color: #444; + border-radius: 3px; + float: left; + margin-left: 4px; + position: relative; + border: 1px solid #bbb; + line-height: 20px; + height: 20px; + font-size: 11px; + padding: 0 6px; +} + +.aeriasocial-btn .aeriasocial-count i, +.aeriasocial-btn .aeriasocial-count u { + position: absolute; + top: 50%; + left: 0; + width: 0; + height: 0; + margin: -4px 0 0 -4px; + line-height: 0; + border: 4px transparent solid; + border-left: 0; + border-right-color: #AAA; + zoom: 1; +} + +.aeriasocial-btn .aeriasocial-count u { + margin-left: -3px; + border-right-color: white; +} + +.aeriasocial-btn .aeriasocial-count b { + font-weight: normal; + padding: 0 5px; + +} + +.aeriasocial-btns .aeriasocial-btn:first-child { margin-left: 0;} +.aeriasocial-btns .aeriasocial-btn:last-child { margin-right: 0; } + +/* CUSTOM */ + +.aeriasocial-btn-facebook .aeriasocial-claim { + background: #4c69ba; + background-image: -moz-linear-gradient(top,#4C69BA,#3B55A0); + background-image: -webkit-gradient(linear,left top,left bottom,from(#4C69BA),to(#3B55A0)); + background-image: -ms-linear-gradient(top,#4C69BA,#3B55A0); + background-image: linear-gradient(top,#4C69BA,#3B55A0); + border-color: #354c8c; + color: #fff; + text-shadow: 0 1px 0 #354c8c; +} + +.aeriasocial-btn-facebook .aeriasocial-icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjI2NEM5MEY5NjAwMjExRTM5NDI2RUM5ODg3ODlFODk1IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjI2NEM5MEZBNjAwMjExRTM5NDI2RUM5ODg3ODlFODk1Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MjY0QzkwRjc2MDAyMTFFMzk0MjZFQzk4ODc4OUU4OTUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MjY0QzkwRjg2MDAyMTFFMzk0MjZFQzk4ODc4OUU4OTUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz56i8csAAAA10lEQVR42pRSQQ6CQAzcLURAjfGgJxNj4kHfyIkjz9kf6Fu8epCYCMEsqy0phiAUbTKknXbSbalWSgHCZ5AvmUNYhprkeX56/WhUSxqNnynGD7GNc3cAWDSx1noG0vOyLDNpmu49z9vEcXxopWrNfOhZSZLsePYQsWp40ohCLgirqrp1+bEt1tvD+ZbdRK8Qh18jInSfBPKJK8vyIgp7DIwxxyAItg3h91XhGFfuHHH89bv8kU6DeeAz+tccCW1RFGdhq7ZNcK3VwpFb3mp9z5z/HPlbgAEAkcKuksEIhzsAAAAASUVORK5CYII=); + background-repeat: no-repeat; + background-position: center; +} + +.aeriasocial-btn-twitter .aeriasocial-claim { + background-color: white; + background-image: -moz-linear-gradient(top,#ffffff,#dedede); + background-image: -webkit-gradient(linear,left top,left bottom,from(#ffffff),to(#dedede)); + background-image: -ms-linear-gradient(top,#ffffff,#dedede); + background-image: linear-gradient(top,#ffffff,#dedede); + color: #333; + border-color: #ccc; + text-shadow: 0 1px 0 rgba(255,255,255,0.5); +} + +.aeriasocial-btn-twitter .aeriasocial-icon { + background: transparent url(https://about.twitter.com/sites/about/modules/internal/features/feature_corporate/img/b2_btn_icon.gif) 0 0 no-repeat; +} + +.aeriasocial-btn-linkedin .aeriasocial-claim { + background-color: #8FC6E0; + background-image: -moz-linear-gradient(top,#8FC6E0,#027FBA); + background-image: -webkit-gradient(linear,left top,left bottom,from(#8FC6E0),to(#027FBA)); + background-image: -ms-linear-gradient(top,#8FC6E0,#027FBA); + background-image: linear-gradient(top,#8FC6E0,#027FBA); + color: #fff; + border-color: #0480BB; + text-shadow: 0 1px 0 #0480BB; +} + +.aeriasocial-btn-linkedin .aeriasocial-icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAANCAYAAACZ3F9/AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNDOEYxOUYzNUYzQTExRTNBNzM5ODcyNTdDMDlEODI5IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNDOEYxOUY0NUYzQTExRTNBNzM5ODcyNTdDMDlEODI5Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6M0M4RjE5RjE1RjNBMTFFM0E3Mzk4NzI1N0MwOUQ4MjkiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6M0M4RjE5RjI1RjNBMTFFM0E3Mzk4NzI1N0MwOUQ4MjkiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7o9QTKAAAA1UlEQVR42pzSuw7BYBTA8baIzWK08AA2iVUkZg9gt/MMBi9hM5skEonRaDITDyDUbVCXz7+cxklD0/Qkv5x8zTnftbYxxkoSjuQBLuirb7Eaq8iijFycxrTkLuqYwJUJfXdVl8I13PjAAicZN1HBFBk0pHaOMTzLvxwszSeGMh7JeI2V+cYWLb/GUSvqfJFcwAhtbJBHR2/1Jjk4U/BGM/RwQA1FlKyIq7cl79Rk7q/n+BcZoWufemCHthgVtm48yBt56sy+c7ACcZRL27+7k/6rLwEGAGfKYG6sJaHuAAAAAElFTkSuQmCC); + background-repeat: no-repeat; + background-position: center; +} + +.aeriasocial-btn-gplus .aeriasocial-claim { + background-color: #D95132; + background-image: -moz-linear-gradient(top,#D95132,#D9432A); + background-image: -webkit-gradient(linear,left top,left bottom,from(#D95132),to(#D9432A)); + background-image: -ms-linear-gradient(top,#D95132,#D9432A); + background-image: linear-gradient(top,#D95132,#D9432A); + color: #fff; + border-color: #9A1900; + text-shadow: 0 1px 0 #9A1900; +} + +.aeriasocial-btn-gplus .aeriasocial-icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAMCAYAAABSgIzaAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNFMzk2NkMwNUYzQjExRTNBNzM5ODcyNTdDMDlEODI5IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNFMzk2NkMxNUYzQjExRTNBNzM5ODcyNTdDMDlEODI5Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6M0M4RjE5RjU1RjNBMTFFM0E3Mzk4NzI1N0MwOUQ4MjkiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6M0M4RjE5RjY1RjNBMTFFM0E3Mzk4NzI1N0MwOUQ4MjkiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4CWIeVAAAA4UlEQVR42pzSMQsBYRzHcXcxULoiMshIKQuTLFcWpchgs3oXNguzUsqKTFyUxXtgNHgFshjIQOd7+p8uusRTn557nv/9eur/PIppmp5/hiqzghLa6OOCCTSpB9FB8pW0TkQWJ9RkvcABPsRRxxJN+fd1YgZ37GS9QgReJFCWuQDdeWIaV8yhYQoDqkjJno7Asy8StOQwwB4zhBw1PyqI2XvOYBRJCVmj4ah9sD+K2KCFPHq4ofoteMTwrTjG2i1od/UsXQvL2upeDlO3B6DIy0ljJFeylesx0JU91+DP4yHAAClcJ8YOCscwAAAAAElFTkSuQmCC); + background-repeat: no-repeat; + background-position: center; +} + +.aeriasocial-container{ + clear: both; + height: 40px; + margin-bottom: 40px; + border-bottom: 1px solid #bbb; + position: relative; + overflow: hidden; + box-shadow: 0 4px 20px -12px rgba(0, 0, 0, 1); +} + +.aeriasocial-container .aeriasocial-sum { + position: absolute; + z-index: 1; + padding: 10px 0; + font-size: 16px; + line-height: 20px; + top: 0; left: 0; right: 0; bottom: 0; + -webkit-transition: .4s; + -moz-transition: .4s; + transition: .4s; + left: 0; + background: #fff; + font-weight: bold; + color: #444; + padding-left: 1em; +} + +.aeriasocial-container .aeriasocial-sum:after { + position: absolute; + content: ''; + right: 7px; + top: 12px; + width: 40px; + background-image: url("../img/share.png"); + background-repeat: no-repeat; + background-position: center; + background-size: cover; + -ms-behavior: url("/app/plugins/aeria/scripts/backgroundsize.htc"); + height: 40px; + opacity: .3; +} + +.aeriasocial-container.enabled:hover .aeriasocial-sum, +.aeriasocial-container.enabled.hover .aeriasocial-sum { + border-left: 1px solid #ddd; + -webkit-transform: translateX(500px); + -moz-transform: translateX(500px); + transform: translateX(500px); +} + +.no-csstransforms .aeriasocial-container.enabled:hover .aeriasocial-sum, +.no-csstransforms .aeriasocial-container.enabled.hover .aeriasocial-sum { + left: 500px; +} + +.aeriasocial-sum > span { + -webkit-transition: opacity .3s; + -moz-transition: opacity .3s; + transition: opacity .3s; + opacity: 0; +} + +.aeriasocial-sum > span.enabled { + opacity: 1; +} + diff --git a/resources/css/bootstrap-datetimepicker.css b/resources/css/bootstrap-datetimepicker.css old mode 100755 new mode 100644 diff --git a/resources/css/bootstrap-datetimepicker.less b/resources/css/bootstrap-datetimepicker.less old mode 100755 new mode 100644 diff --git a/resources/css/bootstrap.css b/resources/css/bootstrap.css old mode 100755 new mode 100644 diff --git a/resources/css/bootstrap.less b/resources/css/bootstrap.less old mode 100755 new mode 100644 diff --git a/resources/css/main.css b/resources/css/main.css old mode 100755 new mode 100644 diff --git a/resources/css/main.less b/resources/css/main.less old mode 100755 new mode 100644 diff --git a/resources/css/select2-bootstrap.css b/resources/css/select2-bootstrap.css old mode 100755 new mode 100644 diff --git a/resources/css/select2.css b/resources/css/select2.css old mode 100755 new mode 100644 diff --git a/resources/fonts/glyphicons-halflings-regular.eot b/resources/fonts/glyphicons-halflings-regular.eot old mode 100755 new mode 100644 diff --git a/resources/fonts/glyphicons-halflings-regular.svg b/resources/fonts/glyphicons-halflings-regular.svg old mode 100755 new mode 100644 diff --git a/resources/fonts/glyphicons-halflings-regular.ttf b/resources/fonts/glyphicons-halflings-regular.ttf old mode 100755 new mode 100644 diff --git a/resources/fonts/glyphicons-halflings-regular.woff b/resources/fonts/glyphicons-halflings-regular.woff old mode 100755 new mode 100644 diff --git a/resources/icons/accept.png b/resources/icons/accept.png old mode 100755 new mode 100644 diff --git a/resources/icons/add.png b/resources/icons/add.png old mode 100755 new mode 100644 diff --git a/resources/icons/alarm.png b/resources/icons/alarm.png old mode 100755 new mode 100644 diff --git a/resources/icons/anchor.png b/resources/icons/anchor.png old mode 100755 new mode 100644 diff --git a/resources/icons/application.png b/resources/icons/application.png old mode 100755 new mode 100644 diff --git a/resources/icons/application2.png b/resources/icons/application2.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_add.png b/resources/icons/application_add.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_cascade.png b/resources/icons/application_cascade.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_delete.png b/resources/icons/application_delete.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_double.png b/resources/icons/application_double.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_edit.png b/resources/icons/application_edit.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_error.png b/resources/icons/application_error.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_form.png b/resources/icons/application_form.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_get.png b/resources/icons/application_get.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_go.png b/resources/icons/application_go.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_home.png b/resources/icons/application_home.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_key.png b/resources/icons/application_key.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_lightning.png b/resources/icons/application_lightning.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_link.png b/resources/icons/application_link.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_osx.png b/resources/icons/application_osx.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_osx_terminal.png b/resources/icons/application_osx_terminal.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_put.png b/resources/icons/application_put.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_side_boxes.png b/resources/icons/application_side_boxes.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_side_contract.png b/resources/icons/application_side_contract.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_side_expand.png b/resources/icons/application_side_expand.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_side_list.png b/resources/icons/application_side_list.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_side_tree.png b/resources/icons/application_side_tree.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_split.png b/resources/icons/application_split.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_tile_horizontal.png b/resources/icons/application_tile_horizontal.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_tile_vertical.png b/resources/icons/application_tile_vertical.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_view_columns.png b/resources/icons/application_view_columns.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_view_detail.png b/resources/icons/application_view_detail.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_view_gallery.png b/resources/icons/application_view_gallery.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_view_icons.png b/resources/icons/application_view_icons.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_view_list.png b/resources/icons/application_view_list.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_view_tile.png b/resources/icons/application_view_tile.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_view_xp.png b/resources/icons/application_view_xp.png old mode 100755 new mode 100644 diff --git a/resources/icons/application_view_xp_terminal.png b/resources/icons/application_view_xp_terminal.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_branch.png b/resources/icons/arrow_branch.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_divide.png b/resources/icons/arrow_divide.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_in.png b/resources/icons/arrow_in.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_inout.png b/resources/icons/arrow_inout.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_join.png b/resources/icons/arrow_join.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_left.png b/resources/icons/arrow_left.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_merge.png b/resources/icons/arrow_merge.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_out.png b/resources/icons/arrow_out.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_redo.png b/resources/icons/arrow_redo.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_refresh.png b/resources/icons/arrow_refresh.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_right.png b/resources/icons/arrow_right.png old mode 100755 new mode 100644 diff --git a/resources/icons/arrow_undo.png b/resources/icons/arrow_undo.png old mode 100755 new mode 100644 diff --git a/resources/icons/asterisk_orange.png b/resources/icons/asterisk_orange.png old mode 100755 new mode 100644 diff --git a/resources/icons/attach.png b/resources/icons/attach.png old mode 100755 new mode 100644 diff --git a/resources/icons/attach_2.png b/resources/icons/attach_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/award_star_gold.png b/resources/icons/award_star_gold.png old mode 100755 new mode 100644 diff --git a/resources/icons/bandaid.png b/resources/icons/bandaid.png old mode 100755 new mode 100644 diff --git a/resources/icons/basket.png b/resources/icons/basket.png old mode 100755 new mode 100644 diff --git a/resources/icons/bell.png b/resources/icons/bell.png old mode 100755 new mode 100644 diff --git a/resources/icons/bin_closed.png b/resources/icons/bin_closed.png old mode 100755 new mode 100644 diff --git a/resources/icons/blog.png b/resources/icons/blog.png old mode 100755 new mode 100644 diff --git a/resources/icons/blueprint.png b/resources/icons/blueprint.png old mode 100755 new mode 100644 diff --git a/resources/icons/blueprint_horizontal.png b/resources/icons/blueprint_horizontal.png old mode 100755 new mode 100644 diff --git a/resources/icons/bluetooth.png b/resources/icons/bluetooth.png old mode 100755 new mode 100644 diff --git a/resources/icons/bomb.png b/resources/icons/bomb.png old mode 100755 new mode 100644 diff --git a/resources/icons/book.png b/resources/icons/book.png old mode 100755 new mode 100644 diff --git a/resources/icons/book_addresses.png b/resources/icons/book_addresses.png old mode 100755 new mode 100644 diff --git a/resources/icons/book_next.png b/resources/icons/book_next.png old mode 100755 new mode 100644 diff --git a/resources/icons/book_open.png b/resources/icons/book_open.png old mode 100755 new mode 100644 diff --git a/resources/icons/book_previous.png b/resources/icons/book_previous.png old mode 100755 new mode 100644 diff --git a/resources/icons/bookmark.png b/resources/icons/bookmark.png old mode 100755 new mode 100644 diff --git a/resources/icons/bookmark_book.png b/resources/icons/bookmark_book.png old mode 100755 new mode 100644 diff --git a/resources/icons/bookmark_book_open.png b/resources/icons/bookmark_book_open.png old mode 100755 new mode 100644 diff --git a/resources/icons/bookmark_document.png b/resources/icons/bookmark_document.png old mode 100755 new mode 100644 diff --git a/resources/icons/bookmark_folder.png b/resources/icons/bookmark_folder.png old mode 100755 new mode 100644 diff --git a/resources/icons/books.png b/resources/icons/books.png old mode 100755 new mode 100644 diff --git a/resources/icons/box.png b/resources/icons/box.png old mode 100755 new mode 100644 diff --git a/resources/icons/brick.png b/resources/icons/brick.png old mode 100755 new mode 100644 diff --git a/resources/icons/bricks.png b/resources/icons/bricks.png old mode 100755 new mode 100644 diff --git a/resources/icons/briefcase.png b/resources/icons/briefcase.png old mode 100755 new mode 100644 diff --git a/resources/icons/bug.png b/resources/icons/bug.png old mode 100755 new mode 100644 diff --git a/resources/icons/buildings.png b/resources/icons/buildings.png old mode 100755 new mode 100644 diff --git a/resources/icons/bullet_add_1.png b/resources/icons/bullet_add_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/bullet_add_2.png b/resources/icons/bullet_add_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/bullet_key.png b/resources/icons/bullet_key.png old mode 100755 new mode 100644 diff --git a/resources/icons/cake.png b/resources/icons/cake.png old mode 100755 new mode 100644 diff --git a/resources/icons/calculator.png b/resources/icons/calculator.png old mode 100755 new mode 100644 diff --git a/resources/icons/calendar_1.png b/resources/icons/calendar_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/calendar_2.png b/resources/icons/calendar_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/camera.png b/resources/icons/camera.png old mode 100755 new mode 100644 diff --git a/resources/icons/cancel.png b/resources/icons/cancel.png old mode 100755 new mode 100644 diff --git a/resources/icons/car.png b/resources/icons/car.png old mode 100755 new mode 100644 diff --git a/resources/icons/cart.png b/resources/icons/cart.png old mode 100755 new mode 100644 diff --git a/resources/icons/cd.png b/resources/icons/cd.png old mode 100755 new mode 100644 diff --git a/resources/icons/chart_bar.png b/resources/icons/chart_bar.png old mode 100755 new mode 100644 diff --git a/resources/icons/chart_curve.png b/resources/icons/chart_curve.png old mode 100755 new mode 100644 diff --git a/resources/icons/chart_line.png b/resources/icons/chart_line.png old mode 100755 new mode 100644 diff --git a/resources/icons/chart_organisation.png b/resources/icons/chart_organisation.png old mode 100755 new mode 100644 diff --git a/resources/icons/chart_pie.png b/resources/icons/chart_pie.png old mode 100755 new mode 100644 diff --git a/resources/icons/clipboard_paste_image.png b/resources/icons/clipboard_paste_image.png old mode 100755 new mode 100644 diff --git a/resources/icons/clipboard_sign.png b/resources/icons/clipboard_sign.png old mode 100755 new mode 100644 diff --git a/resources/icons/clipboard_text.png b/resources/icons/clipboard_text.png old mode 100755 new mode 100644 diff --git a/resources/icons/clock.png b/resources/icons/clock.png old mode 100755 new mode 100644 diff --git a/resources/icons/cog.png b/resources/icons/cog.png old mode 100755 new mode 100644 diff --git a/resources/icons/coins.png b/resources/icons/coins.png old mode 100755 new mode 100644 diff --git a/resources/icons/color_swatch_1.png b/resources/icons/color_swatch_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/color_swatch_2.png b/resources/icons/color_swatch_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/comment.png b/resources/icons/comment.png old mode 100755 new mode 100644 diff --git a/resources/icons/compass.png b/resources/icons/compass.png old mode 100755 new mode 100644 diff --git a/resources/icons/compress.png b/resources/icons/compress.png old mode 100755 new mode 100644 diff --git a/resources/icons/computer.png b/resources/icons/computer.png old mode 100755 new mode 100644 diff --git a/resources/icons/connect.png b/resources/icons/connect.png old mode 100755 new mode 100644 diff --git a/resources/icons/contrast.png b/resources/icons/contrast.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_eject.png b/resources/icons/control_eject.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_end.png b/resources/icons/control_end.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_equalizer.png b/resources/icons/control_equalizer.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_fastforward.png b/resources/icons/control_fastforward.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_pause.png b/resources/icons/control_pause.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_play.png b/resources/icons/control_play.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_repeat.png b/resources/icons/control_repeat.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_rewind.png b/resources/icons/control_rewind.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_start.png b/resources/icons/control_start.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_stop.png b/resources/icons/control_stop.png old mode 100755 new mode 100644 diff --git a/resources/icons/control_wheel.png b/resources/icons/control_wheel.png old mode 100755 new mode 100644 diff --git a/resources/icons/counter.png b/resources/icons/counter.png old mode 100755 new mode 100644 diff --git a/resources/icons/counter_count.png b/resources/icons/counter_count.png old mode 100755 new mode 100644 diff --git a/resources/icons/counter_count_up.png b/resources/icons/counter_count_up.png old mode 100755 new mode 100644 diff --git a/resources/icons/counter_reset.png b/resources/icons/counter_reset.png old mode 100755 new mode 100644 diff --git a/resources/icons/counter_stop.png b/resources/icons/counter_stop.png old mode 100755 new mode 100644 diff --git a/resources/icons/cross.png b/resources/icons/cross.png old mode 100755 new mode 100644 diff --git a/resources/icons/cross_octagon.png b/resources/icons/cross_octagon.png old mode 100755 new mode 100644 diff --git a/resources/icons/cross_octagon_fram.png b/resources/icons/cross_octagon_fram.png old mode 100755 new mode 100644 diff --git a/resources/icons/cross_shield.png b/resources/icons/cross_shield.png old mode 100755 new mode 100644 diff --git a/resources/icons/cross_shield_2.png b/resources/icons/cross_shield_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/crown.png b/resources/icons/crown.png old mode 100755 new mode 100644 diff --git a/resources/icons/crown_bronze.png b/resources/icons/crown_bronze.png old mode 100755 new mode 100644 diff --git a/resources/icons/crown_silver.png b/resources/icons/crown_silver.png old mode 100755 new mode 100644 diff --git a/resources/icons/css.png b/resources/icons/css.png old mode 100755 new mode 100644 diff --git a/resources/icons/cursor.png b/resources/icons/cursor.png old mode 100755 new mode 100644 diff --git a/resources/icons/cut.png b/resources/icons/cut.png old mode 100755 new mode 100644 diff --git a/resources/icons/dashboard.png b/resources/icons/dashboard.png old mode 100755 new mode 100644 diff --git a/resources/icons/data.png b/resources/icons/data.png old mode 100755 new mode 100644 diff --git a/resources/icons/database.png b/resources/icons/database.png old mode 100755 new mode 100644 diff --git a/resources/icons/databases.png b/resources/icons/databases.png old mode 100755 new mode 100644 diff --git a/resources/icons/delete.png b/resources/icons/delete.png old mode 100755 new mode 100644 diff --git a/resources/icons/delivery.png b/resources/icons/delivery.png old mode 100755 new mode 100644 diff --git a/resources/icons/desktop.png b/resources/icons/desktop.png old mode 100755 new mode 100644 diff --git a/resources/icons/desktop_empty.png b/resources/icons/desktop_empty.png old mode 100755 new mode 100644 diff --git a/resources/icons/direction.png b/resources/icons/direction.png old mode 100755 new mode 100644 diff --git a/resources/icons/disconnect.png b/resources/icons/disconnect.png old mode 100755 new mode 100644 diff --git a/resources/icons/disk.png b/resources/icons/disk.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_access.png b/resources/icons/doc_access.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_break.png b/resources/icons/doc_break.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_convert.png b/resources/icons/doc_convert.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_excel_csv.png b/resources/icons/doc_excel_csv.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_excel_table.png b/resources/icons/doc_excel_table.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_film.png b/resources/icons/doc_film.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_illustrator.png b/resources/icons/doc_illustrator.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_music.png b/resources/icons/doc_music.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_music_playlist.png b/resources/icons/doc_music_playlist.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_offlice.png b/resources/icons/doc_offlice.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_page.png b/resources/icons/doc_page.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_page_previous.png b/resources/icons/doc_page_previous.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_pdf.png b/resources/icons/doc_pdf.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_photoshop.png b/resources/icons/doc_photoshop.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_resize.png b/resources/icons/doc_resize.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_resize_actual.png b/resources/icons/doc_resize_actual.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_shred.png b/resources/icons/doc_shred.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_stand.png b/resources/icons/doc_stand.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_table.png b/resources/icons/doc_table.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_tag.png b/resources/icons/doc_tag.png old mode 100755 new mode 100644 diff --git a/resources/icons/doc_text_image.png b/resources/icons/doc_text_image.png old mode 100755 new mode 100644 diff --git a/resources/icons/door.png b/resources/icons/door.png old mode 100755 new mode 100644 diff --git a/resources/icons/door_in.png b/resources/icons/door_in.png old mode 100755 new mode 100644 diff --git a/resources/icons/drawer.png b/resources/icons/drawer.png old mode 100755 new mode 100644 diff --git a/resources/icons/drink.png b/resources/icons/drink.png old mode 100755 new mode 100644 diff --git a/resources/icons/drink_empty.png b/resources/icons/drink_empty.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive.png b/resources/icons/drive.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_burn.png b/resources/icons/drive_burn.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_cd.png b/resources/icons/drive_cd.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_cd_empty.png b/resources/icons/drive_cd_empty.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_delete.png b/resources/icons/drive_delete.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_disk.png b/resources/icons/drive_disk.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_error.png b/resources/icons/drive_error.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_go.png b/resources/icons/drive_go.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_link.png b/resources/icons/drive_link.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_network.png b/resources/icons/drive_network.png old mode 100755 new mode 100644 diff --git a/resources/icons/drive_rename.png b/resources/icons/drive_rename.png old mode 100755 new mode 100644 diff --git a/resources/icons/dvd.png b/resources/icons/dvd.png old mode 100755 new mode 100644 diff --git a/resources/icons/email.png b/resources/icons/email.png old mode 100755 new mode 100644 diff --git a/resources/icons/email_open.png b/resources/icons/email_open.png old mode 100755 new mode 100644 diff --git a/resources/icons/email_open_image.png b/resources/icons/email_open_image.png old mode 100755 new mode 100644 diff --git a/resources/icons/emoticon_evilgrin.png b/resources/icons/emoticon_evilgrin.png old mode 100755 new mode 100644 diff --git a/resources/icons/emoticon_grin.png b/resources/icons/emoticon_grin.png old mode 100755 new mode 100644 diff --git a/resources/icons/emoticon_happy.png b/resources/icons/emoticon_happy.png old mode 100755 new mode 100644 diff --git a/resources/icons/emoticon_smile.png b/resources/icons/emoticon_smile.png old mode 100755 new mode 100644 diff --git a/resources/icons/emoticon_surprised.png b/resources/icons/emoticon_surprised.png old mode 100755 new mode 100644 diff --git a/resources/icons/emoticon_tongue.png b/resources/icons/emoticon_tongue.png old mode 100755 new mode 100644 diff --git a/resources/icons/emoticon_unhappy.png b/resources/icons/emoticon_unhappy.png old mode 100755 new mode 100644 diff --git a/resources/icons/emoticon_waii.png b/resources/icons/emoticon_waii.png old mode 100755 new mode 100644 diff --git a/resources/icons/emoticon_wink.png b/resources/icons/emoticon_wink.png old mode 100755 new mode 100644 diff --git a/resources/icons/envelope.png b/resources/icons/envelope.png old mode 100755 new mode 100644 diff --git a/resources/icons/envelope_2.png b/resources/icons/envelope_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/error.png b/resources/icons/error.png old mode 100755 new mode 100644 diff --git a/resources/icons/exclamation.png b/resources/icons/exclamation.png old mode 100755 new mode 100644 diff --git a/resources/icons/exclamation_octagon_fram.png b/resources/icons/exclamation_octagon_fram.png old mode 100755 new mode 100644 diff --git a/resources/icons/eye.png b/resources/icons/eye.png old mode 100755 new mode 100644 diff --git a/resources/icons/feed.png b/resources/icons/feed.png old mode 100755 new mode 100644 diff --git a/resources/icons/feed_ballon.png b/resources/icons/feed_ballon.png old mode 100755 new mode 100644 diff --git a/resources/icons/feed_document.png b/resources/icons/feed_document.png old mode 100755 new mode 100644 diff --git a/resources/icons/female.png b/resources/icons/female.png old mode 100755 new mode 100644 diff --git a/resources/icons/film.png b/resources/icons/film.png old mode 100755 new mode 100644 diff --git a/resources/icons/films.png b/resources/icons/films.png old mode 100755 new mode 100644 diff --git a/resources/icons/find.png b/resources/icons/find.png old mode 100755 new mode 100644 diff --git a/resources/icons/flag_blue.png b/resources/icons/flag_blue.png old mode 100755 new mode 100644 diff --git a/resources/icons/folder.png b/resources/icons/folder.png old mode 100755 new mode 100644 diff --git a/resources/icons/font.png b/resources/icons/font.png old mode 100755 new mode 100644 diff --git a/resources/icons/funnel.png b/resources/icons/funnel.png old mode 100755 new mode 100644 diff --git a/resources/icons/grid.png b/resources/icons/grid.png old mode 100755 new mode 100644 diff --git a/resources/icons/grid_dot.png b/resources/icons/grid_dot.png old mode 100755 new mode 100644 diff --git a/resources/icons/group.png b/resources/icons/group.png old mode 100755 new mode 100644 diff --git a/resources/icons/hammer.png b/resources/icons/hammer.png old mode 100755 new mode 100644 diff --git a/resources/icons/hammer_screwdriver.png b/resources/icons/hammer_screwdriver.png old mode 100755 new mode 100644 diff --git a/resources/icons/hand.png b/resources/icons/hand.png old mode 100755 new mode 100644 diff --git a/resources/icons/hand_point.png b/resources/icons/hand_point.png old mode 100755 new mode 100644 diff --git a/resources/icons/heart.png b/resources/icons/heart.png old mode 100755 new mode 100644 diff --git a/resources/icons/heart_break.png b/resources/icons/heart_break.png old mode 100755 new mode 100644 diff --git a/resources/icons/heart_empty.png b/resources/icons/heart_empty.png old mode 100755 new mode 100644 diff --git a/resources/icons/heart_half.png b/resources/icons/heart_half.png old mode 100755 new mode 100644 diff --git a/resources/icons/heart_small.png b/resources/icons/heart_small.png old mode 100755 new mode 100644 diff --git a/resources/icons/help.png b/resources/icons/help.png old mode 100755 new mode 100644 diff --git a/resources/icons/highlighter.png b/resources/icons/highlighter.png old mode 100755 new mode 100644 diff --git a/resources/icons/house.png b/resources/icons/house.png old mode 100755 new mode 100644 diff --git a/resources/icons/html.png b/resources/icons/html.png old mode 100755 new mode 100644 diff --git a/resources/icons/image_1.png b/resources/icons/image_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/image_2.png b/resources/icons/image_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/images.png b/resources/icons/images.png old mode 100755 new mode 100644 diff --git a/resources/icons/inbox.png b/resources/icons/inbox.png old mode 100755 new mode 100644 diff --git a/resources/icons/ipod.png b/resources/icons/ipod.png old mode 100755 new mode 100644 diff --git a/resources/icons/ipod_cast.png b/resources/icons/ipod_cast.png old mode 100755 new mode 100644 diff --git a/resources/icons/joystick.png b/resources/icons/joystick.png old mode 100755 new mode 100644 diff --git a/resources/icons/key.png b/resources/icons/key.png old mode 100755 new mode 100644 diff --git a/resources/icons/keyboard.png b/resources/icons/keyboard.png old mode 100755 new mode 100644 diff --git a/resources/icons/layer_treansparent.png b/resources/icons/layer_treansparent.png old mode 100755 new mode 100644 diff --git a/resources/icons/layers.png b/resources/icons/layers.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout.png b/resources/icons/layout.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_header_footer_3.png b/resources/icons/layout_header_footer_3.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_header_footer_3_mix.png b/resources/icons/layout_header_footer_3_mix.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_join.png b/resources/icons/layout_join.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_join_vertical.png b/resources/icons/layout_join_vertical.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_select.png b/resources/icons/layout_select.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_select_content.png b/resources/icons/layout_select_content.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_select_footer.png b/resources/icons/layout_select_footer.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_select_sidebar.png b/resources/icons/layout_select_sidebar.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_split.png b/resources/icons/layout_split.png old mode 100755 new mode 100644 diff --git a/resources/icons/layout_split_vertical.png b/resources/icons/layout_split_vertical.png old mode 100755 new mode 100644 diff --git a/resources/icons/lifebuoy.png b/resources/icons/lifebuoy.png old mode 100755 new mode 100644 diff --git a/resources/icons/lightbulb.png b/resources/icons/lightbulb.png old mode 100755 new mode 100644 diff --git a/resources/icons/lightbulb_off.png b/resources/icons/lightbulb_off.png old mode 100755 new mode 100644 diff --git a/resources/icons/lightning.png b/resources/icons/lightning.png old mode 100755 new mode 100644 diff --git a/resources/icons/link.png b/resources/icons/link.png old mode 100755 new mode 100644 diff --git a/resources/icons/link_break.png b/resources/icons/link_break.png old mode 100755 new mode 100644 diff --git a/resources/icons/lock.png b/resources/icons/lock.png old mode 100755 new mode 100644 diff --git a/resources/icons/lock_unlock.png b/resources/icons/lock_unlock.png old mode 100755 new mode 100644 diff --git a/resources/icons/magnet.png b/resources/icons/magnet.png old mode 100755 new mode 100644 diff --git a/resources/icons/magnifier.png b/resources/icons/magnifier.png old mode 100755 new mode 100644 diff --git a/resources/icons/magnifier_zoom_in.png b/resources/icons/magnifier_zoom_in.png old mode 100755 new mode 100644 diff --git a/resources/icons/male.png b/resources/icons/male.png old mode 100755 new mode 100644 diff --git a/resources/icons/map.png b/resources/icons/map.png old mode 100755 new mode 100644 diff --git a/resources/icons/marker.png b/resources/icons/marker.png old mode 100755 new mode 100644 diff --git a/resources/icons/medal_bronze_1.png b/resources/icons/medal_bronze_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/medal_gold_1.png b/resources/icons/medal_gold_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/media_player_small_blue.png b/resources/icons/media_player_small_blue.png old mode 100755 new mode 100644 diff --git a/resources/icons/microphone.png b/resources/icons/microphone.png old mode 100755 new mode 100644 diff --git a/resources/icons/mobile_phone.png b/resources/icons/mobile_phone.png old mode 100755 new mode 100644 diff --git a/resources/icons/money.png b/resources/icons/money.png old mode 100755 new mode 100644 diff --git a/resources/icons/money_dollar.png b/resources/icons/money_dollar.png old mode 100755 new mode 100644 diff --git a/resources/icons/money_euro.png b/resources/icons/money_euro.png old mode 100755 new mode 100644 diff --git a/resources/icons/money_pound.png b/resources/icons/money_pound.png old mode 100755 new mode 100644 diff --git a/resources/icons/money_yen.png b/resources/icons/money_yen.png old mode 100755 new mode 100644 diff --git a/resources/icons/monitor.png b/resources/icons/monitor.png old mode 100755 new mode 100644 diff --git a/resources/icons/mouse.png b/resources/icons/mouse.png old mode 100755 new mode 100644 diff --git a/resources/icons/music.png b/resources/icons/music.png old mode 100755 new mode 100644 diff --git a/resources/icons/music_beam.png b/resources/icons/music_beam.png old mode 100755 new mode 100644 diff --git a/resources/icons/neutral.png b/resources/icons/neutral.png old mode 100755 new mode 100644 diff --git a/resources/icons/new.png b/resources/icons/new.png old mode 100755 new mode 100644 diff --git a/resources/icons/newspaper.png b/resources/icons/newspaper.png old mode 100755 new mode 100644 diff --git a/resources/icons/note.png b/resources/icons/note.png old mode 100755 new mode 100644 diff --git a/resources/icons/nuclear.png b/resources/icons/nuclear.png old mode 100755 new mode 100644 diff --git a/resources/icons/package.png b/resources/icons/package.png old mode 100755 new mode 100644 diff --git a/resources/icons/page.png b/resources/icons/page.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_2.png b/resources/icons/page_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_2_copy.png b/resources/icons/page_2_copy.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_code.png b/resources/icons/page_code.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_copy.png b/resources/icons/page_copy.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_excel.png b/resources/icons/page_excel.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_lightning.png b/resources/icons/page_lightning.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_paste.png b/resources/icons/page_paste.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_red.png b/resources/icons/page_red.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_refresh.png b/resources/icons/page_refresh.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_save.png b/resources/icons/page_save.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_cplusplus.png b/resources/icons/page_white_cplusplus.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_csharp.png b/resources/icons/page_white_csharp.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_cup.png b/resources/icons/page_white_cup.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_database.png b/resources/icons/page_white_database.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_delete.png b/resources/icons/page_white_delete.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_dvd.png b/resources/icons/page_white_dvd.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_edit.png b/resources/icons/page_white_edit.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_error.png b/resources/icons/page_white_error.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_excel.png b/resources/icons/page_white_excel.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_find.png b/resources/icons/page_white_find.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_flash.png b/resources/icons/page_white_flash.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_freehand.png b/resources/icons/page_white_freehand.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_gear.png b/resources/icons/page_white_gear.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_get.png b/resources/icons/page_white_get.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_paintbrush.png b/resources/icons/page_white_paintbrush.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_paste.png b/resources/icons/page_white_paste.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_php.png b/resources/icons/page_white_php.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_picture.png b/resources/icons/page_white_picture.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_powerpoint.png b/resources/icons/page_white_powerpoint.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_put.png b/resources/icons/page_white_put.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_ruby.png b/resources/icons/page_white_ruby.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_stack.png b/resources/icons/page_white_stack.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_star.png b/resources/icons/page_white_star.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_swoosh.png b/resources/icons/page_white_swoosh.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_text.png b/resources/icons/page_white_text.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_text_width.png b/resources/icons/page_white_text_width.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_tux.png b/resources/icons/page_white_tux.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_vector.png b/resources/icons/page_white_vector.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_visualstudio.png b/resources/icons/page_white_visualstudio.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_width.png b/resources/icons/page_white_width.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_word.png b/resources/icons/page_white_word.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_world.png b/resources/icons/page_white_world.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_wrench.png b/resources/icons/page_white_wrench.png old mode 100755 new mode 100644 diff --git a/resources/icons/page_white_zip.png b/resources/icons/page_white_zip.png old mode 100755 new mode 100644 diff --git a/resources/icons/paintbrush.png b/resources/icons/paintbrush.png old mode 100755 new mode 100644 diff --git a/resources/icons/paintcan.png b/resources/icons/paintcan.png old mode 100755 new mode 100644 diff --git a/resources/icons/palette.png b/resources/icons/palette.png old mode 100755 new mode 100644 diff --git a/resources/icons/paper_bag.png b/resources/icons/paper_bag.png old mode 100755 new mode 100644 diff --git a/resources/icons/paste_plain.png b/resources/icons/paste_plain.png old mode 100755 new mode 100644 diff --git a/resources/icons/paste_word.png b/resources/icons/paste_word.png old mode 100755 new mode 100644 diff --git a/resources/icons/pencil.png b/resources/icons/pencil.png old mode 100755 new mode 100644 diff --git a/resources/icons/photo.png b/resources/icons/photo.png old mode 100755 new mode 100644 diff --git a/resources/icons/photo_album.png b/resources/icons/photo_album.png old mode 100755 new mode 100644 diff --git a/resources/icons/photos.png b/resources/icons/photos.png old mode 100755 new mode 100644 diff --git a/resources/icons/piano.png b/resources/icons/piano.png old mode 100755 new mode 100644 diff --git a/resources/icons/picture.png b/resources/icons/picture.png old mode 100755 new mode 100644 diff --git a/resources/icons/pilcrow.png b/resources/icons/pilcrow.png old mode 100755 new mode 100644 diff --git a/resources/icons/pill.png b/resources/icons/pill.png old mode 100755 new mode 100644 diff --git a/resources/icons/pin.png b/resources/icons/pin.png old mode 100755 new mode 100644 diff --git a/resources/icons/pipette.png b/resources/icons/pipette.png old mode 100755 new mode 100644 diff --git a/resources/icons/plaing_card.png b/resources/icons/plaing_card.png old mode 100755 new mode 100644 diff --git a/resources/icons/plug.png b/resources/icons/plug.png old mode 100755 new mode 100644 diff --git a/resources/icons/plugin.png b/resources/icons/plugin.png old mode 100755 new mode 100644 diff --git a/resources/icons/printer.png b/resources/icons/printer.png old mode 100755 new mode 100644 diff --git a/resources/icons/projection_screen.png b/resources/icons/projection_screen.png old mode 100755 new mode 100644 diff --git a/resources/icons/projection_screen_present.png b/resources/icons/projection_screen_present.png old mode 100755 new mode 100644 diff --git a/resources/icons/rainbow.png b/resources/icons/rainbow.png old mode 100755 new mode 100644 diff --git a/resources/icons/report.png b/resources/icons/report.png old mode 100755 new mode 100644 diff --git a/resources/icons/rocket.png b/resources/icons/rocket.png old mode 100755 new mode 100644 diff --git a/resources/icons/rosette.png b/resources/icons/rosette.png old mode 100755 new mode 100644 diff --git a/resources/icons/rss.png b/resources/icons/rss.png old mode 100755 new mode 100644 diff --git a/resources/icons/ruby.png b/resources/icons/ruby.png old mode 100755 new mode 100644 diff --git a/resources/icons/ruler_1.png b/resources/icons/ruler_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/ruler_2.png b/resources/icons/ruler_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/ruler_crop.png b/resources/icons/ruler_crop.png old mode 100755 new mode 100644 diff --git a/resources/icons/ruler_triangle.png b/resources/icons/ruler_triangle.png old mode 100755 new mode 100644 diff --git a/resources/icons/safe.png b/resources/icons/safe.png old mode 100755 new mode 100644 diff --git a/resources/icons/script.png b/resources/icons/script.png old mode 100755 new mode 100644 diff --git a/resources/icons/selection.png b/resources/icons/selection.png old mode 100755 new mode 100644 diff --git a/resources/icons/selection_select.png b/resources/icons/selection_select.png old mode 100755 new mode 100644 diff --git a/resources/icons/server.png b/resources/icons/server.png old mode 100755 new mode 100644 diff --git a/resources/icons/shading.png b/resources/icons/shading.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_aling_bottom.png b/resources/icons/shape_aling_bottom.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_aling_center.png b/resources/icons/shape_aling_center.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_aling_left.png b/resources/icons/shape_aling_left.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_aling_middle.png b/resources/icons/shape_aling_middle.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_aling_right.png b/resources/icons/shape_aling_right.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_aling_top.png b/resources/icons/shape_aling_top.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_flip_horizontal.png b/resources/icons/shape_flip_horizontal.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_flip_vertical.png b/resources/icons/shape_flip_vertical.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_group.png b/resources/icons/shape_group.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_handles.png b/resources/icons/shape_handles.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_move_back.png b/resources/icons/shape_move_back.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_move_backwards.png b/resources/icons/shape_move_backwards.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_move_forwards.png b/resources/icons/shape_move_forwards.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_move_front.png b/resources/icons/shape_move_front.png old mode 100755 new mode 100644 diff --git a/resources/icons/shape_square.png b/resources/icons/shape_square.png old mode 100755 new mode 100644 diff --git a/resources/icons/shield.png b/resources/icons/shield.png old mode 100755 new mode 100644 diff --git a/resources/icons/sitemap.png b/resources/icons/sitemap.png old mode 100755 new mode 100644 diff --git a/resources/icons/slide.png b/resources/icons/slide.png old mode 100755 new mode 100644 diff --git a/resources/icons/slides.png b/resources/icons/slides.png old mode 100755 new mode 100644 diff --git a/resources/icons/slides_stack.png b/resources/icons/slides_stack.png old mode 100755 new mode 100644 diff --git a/resources/icons/smiley_confuse.png b/resources/icons/smiley_confuse.png old mode 100755 new mode 100644 diff --git a/resources/icons/smiley_cool.png b/resources/icons/smiley_cool.png old mode 100755 new mode 100644 diff --git a/resources/icons/smiley_cry.png b/resources/icons/smiley_cry.png old mode 100755 new mode 100644 diff --git a/resources/icons/smiley_fat.png b/resources/icons/smiley_fat.png old mode 100755 new mode 100644 diff --git a/resources/icons/smiley_mad.png b/resources/icons/smiley_mad.png old mode 100755 new mode 100644 diff --git a/resources/icons/smiley_red.png b/resources/icons/smiley_red.png old mode 100755 new mode 100644 diff --git a/resources/icons/smiley_roll.png b/resources/icons/smiley_roll.png old mode 100755 new mode 100644 diff --git a/resources/icons/smiley_slim.png b/resources/icons/smiley_slim.png old mode 100755 new mode 100644 diff --git a/resources/icons/smiley_yell.png b/resources/icons/smiley_yell.png old mode 100755 new mode 100644 diff --git a/resources/icons/socket.png b/resources/icons/socket.png old mode 100755 new mode 100644 diff --git a/resources/icons/sockets.png b/resources/icons/sockets.png old mode 100755 new mode 100644 diff --git a/resources/icons/sort.png b/resources/icons/sort.png old mode 100755 new mode 100644 diff --git a/resources/icons/sort_alphabet.png b/resources/icons/sort_alphabet.png old mode 100755 new mode 100644 diff --git a/resources/icons/sort_date.png b/resources/icons/sort_date.png old mode 100755 new mode 100644 diff --git a/resources/icons/sort_disable.png b/resources/icons/sort_disable.png old mode 100755 new mode 100644 diff --git a/resources/icons/sort_number.png b/resources/icons/sort_number.png old mode 100755 new mode 100644 diff --git a/resources/icons/sort_price.png b/resources/icons/sort_price.png old mode 100755 new mode 100644 diff --git a/resources/icons/sort_quantity.png b/resources/icons/sort_quantity.png old mode 100755 new mode 100644 diff --git a/resources/icons/sort_rating.png b/resources/icons/sort_rating.png old mode 100755 new mode 100644 diff --git a/resources/icons/sound.png b/resources/icons/sound.png old mode 100755 new mode 100644 diff --git a/resources/icons/sound_note.png b/resources/icons/sound_note.png old mode 100755 new mode 100644 diff --git a/resources/icons/spellcheck.png b/resources/icons/spellcheck.png old mode 100755 new mode 100644 diff --git a/resources/icons/sport_8ball.png b/resources/icons/sport_8ball.png old mode 100755 new mode 100644 diff --git a/resources/icons/sport_basketball.png b/resources/icons/sport_basketball.png old mode 100755 new mode 100644 diff --git a/resources/icons/sport_football.png b/resources/icons/sport_football.png old mode 100755 new mode 100644 diff --git a/resources/icons/sport_golf.png b/resources/icons/sport_golf.png old mode 100755 new mode 100644 diff --git a/resources/icons/sport_raquet.png b/resources/icons/sport_raquet.png old mode 100755 new mode 100644 diff --git a/resources/icons/sport_shuttlecock.png b/resources/icons/sport_shuttlecock.png old mode 100755 new mode 100644 diff --git a/resources/icons/sport_soccer.png b/resources/icons/sport_soccer.png old mode 100755 new mode 100644 diff --git a/resources/icons/sport_tennis.png b/resources/icons/sport_tennis.png old mode 100755 new mode 100644 diff --git a/resources/icons/stamp.png b/resources/icons/stamp.png old mode 100755 new mode 100644 diff --git a/resources/icons/star_1.png b/resources/icons/star_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/star_2.png b/resources/icons/star_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/status_online.png b/resources/icons/status_online.png old mode 100755 new mode 100644 diff --git a/resources/icons/stop.png b/resources/icons/stop.png old mode 100755 new mode 100644 diff --git a/resources/icons/style.png b/resources/icons/style.png old mode 100755 new mode 100644 diff --git a/resources/icons/sum.png b/resources/icons/sum.png old mode 100755 new mode 100644 diff --git a/resources/icons/sum_2.png b/resources/icons/sum_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/switch.png b/resources/icons/switch.png old mode 100755 new mode 100644 diff --git a/resources/icons/tab.png b/resources/icons/tab.png old mode 100755 new mode 100644 diff --git a/resources/icons/table.png b/resources/icons/table.png old mode 100755 new mode 100644 diff --git a/resources/icons/tag.png b/resources/icons/tag.png old mode 100755 new mode 100644 diff --git a/resources/icons/tag_blue.png b/resources/icons/tag_blue.png old mode 100755 new mode 100644 diff --git a/resources/icons/target.png b/resources/icons/target.png old mode 100755 new mode 100644 diff --git a/resources/icons/telephone.png b/resources/icons/telephone.png old mode 100755 new mode 100644 diff --git a/resources/icons/television.png b/resources/icons/television.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_align_center.png b/resources/icons/text_align_center.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_align_justify.png b/resources/icons/text_align_justify.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_align_left.png b/resources/icons/text_align_left.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_align_right.png b/resources/icons/text_align_right.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_allcaps.png b/resources/icons/text_allcaps.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_bold.png b/resources/icons/text_bold.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_columns.png b/resources/icons/text_columns.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_dropcaps.png b/resources/icons/text_dropcaps.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_heading_1.png b/resources/icons/text_heading_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_horizontalrule.png b/resources/icons/text_horizontalrule.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_indent.png b/resources/icons/text_indent.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_indent_remove.png b/resources/icons/text_indent_remove.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_italic.png b/resources/icons/text_italic.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_kerning.png b/resources/icons/text_kerning.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_letter_omega.png b/resources/icons/text_letter_omega.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_letterspacing.png b/resources/icons/text_letterspacing.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_linespacing.png b/resources/icons/text_linespacing.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_list_bullets.png b/resources/icons/text_list_bullets.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_list_numbers.png b/resources/icons/text_list_numbers.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_lowercase.png b/resources/icons/text_lowercase.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_padding_bottom.png b/resources/icons/text_padding_bottom.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_padding_left.png b/resources/icons/text_padding_left.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_padding_right.png b/resources/icons/text_padding_right.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_padding_top.png b/resources/icons/text_padding_top.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_signature.png b/resources/icons/text_signature.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_smallcaps.png b/resources/icons/text_smallcaps.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_strikethrough.png b/resources/icons/text_strikethrough.png old mode 100755 new mode 100644 diff --git a/resources/icons/text_subscript.png b/resources/icons/text_subscript.png old mode 100755 new mode 100644 diff --git a/resources/icons/textfield.png b/resources/icons/textfield.png old mode 100755 new mode 100644 diff --git a/resources/icons/textfield_rename.png b/resources/icons/textfield_rename.png old mode 100755 new mode 100644 diff --git a/resources/icons/ticket.png b/resources/icons/ticket.png old mode 100755 new mode 100644 diff --git a/resources/icons/timeline_marker.png b/resources/icons/timeline_marker.png old mode 100755 new mode 100644 diff --git a/resources/icons/traffic.png b/resources/icons/traffic.png old mode 100755 new mode 100644 diff --git a/resources/icons/transmit.png b/resources/icons/transmit.png old mode 100755 new mode 100644 diff --git a/resources/icons/trophy.png b/resources/icons/trophy.png old mode 100755 new mode 100644 diff --git a/resources/icons/trophy_bronze.png b/resources/icons/trophy_bronze.png old mode 100755 new mode 100644 diff --git a/resources/icons/trophy_silver.png b/resources/icons/trophy_silver.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_combo_box.png b/resources/icons/ui_combo_box.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_saccordion.png b/resources/icons/ui_saccordion.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_slider_1.png b/resources/icons/ui_slider_1.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_slider_2.png b/resources/icons/ui_slider_2.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_tab_bottom.png b/resources/icons/ui_tab_bottom.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_tab_content.png b/resources/icons/ui_tab_content.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_tab_disable.png b/resources/icons/ui_tab_disable.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_tab_side.png b/resources/icons/ui_tab_side.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_text_field_hidden.png b/resources/icons/ui_text_field_hidden.png old mode 100755 new mode 100644 diff --git a/resources/icons/ui_text_field_password.png b/resources/icons/ui_text_field_password.png old mode 100755 new mode 100644 diff --git a/resources/icons/umbrella.png b/resources/icons/umbrella.png old mode 100755 new mode 100644 diff --git a/resources/icons/user.png b/resources/icons/user.png old mode 100755 new mode 100644 diff --git a/resources/icons/user_black_female.png b/resources/icons/user_black_female.png old mode 100755 new mode 100644 diff --git a/resources/icons/user_business.png b/resources/icons/user_business.png old mode 100755 new mode 100644 diff --git a/resources/icons/user_business_boss.png b/resources/icons/user_business_boss.png old mode 100755 new mode 100644 diff --git a/resources/icons/user_female.png b/resources/icons/user_female.png old mode 100755 new mode 100644 diff --git a/resources/icons/user_silhouette.png b/resources/icons/user_silhouette.png old mode 100755 new mode 100644 diff --git a/resources/icons/user_thief.png b/resources/icons/user_thief.png old mode 100755 new mode 100644 diff --git a/resources/icons/user_thief_baldie.png b/resources/icons/user_thief_baldie.png old mode 100755 new mode 100644 diff --git a/resources/icons/vcard.png b/resources/icons/vcard.png old mode 100755 new mode 100644 diff --git a/resources/icons/vector.png b/resources/icons/vector.png old mode 100755 new mode 100644 diff --git a/resources/icons/wait.png b/resources/icons/wait.png old mode 100755 new mode 100644 diff --git a/resources/icons/wall.png b/resources/icons/wall.png old mode 100755 new mode 100644 diff --git a/resources/icons/wall_break.png b/resources/icons/wall_break.png old mode 100755 new mode 100644 diff --git a/resources/icons/wall_brick.png b/resources/icons/wall_brick.png old mode 100755 new mode 100644 diff --git a/resources/icons/wall_disable.png b/resources/icons/wall_disable.png old mode 100755 new mode 100644 diff --git a/resources/icons/wand.png b/resources/icons/wand.png old mode 100755 new mode 100644 diff --git a/resources/icons/weather_clouds.png b/resources/icons/weather_clouds.png old mode 100755 new mode 100644 diff --git a/resources/icons/weather_cloudy.png b/resources/icons/weather_cloudy.png old mode 100755 new mode 100644 diff --git a/resources/icons/weather_lightning.png b/resources/icons/weather_lightning.png old mode 100755 new mode 100644 diff --git a/resources/icons/weather_rain.png b/resources/icons/weather_rain.png old mode 100755 new mode 100644 diff --git a/resources/icons/weather_snow.png b/resources/icons/weather_snow.png old mode 100755 new mode 100644 diff --git a/resources/icons/weather_sun.png b/resources/icons/weather_sun.png old mode 100755 new mode 100644 diff --git a/resources/icons/webcam.png b/resources/icons/webcam.png old mode 100755 new mode 100644 diff --git a/resources/icons/world.png b/resources/icons/world.png old mode 100755 new mode 100644 diff --git a/resources/icons/zone.png b/resources/icons/zone.png old mode 100755 new mode 100644 diff --git a/resources/icons/zone_money.png b/resources/icons/zone_money.png old mode 100755 new mode 100644 diff --git a/resources/icons/zones.png b/resources/icons/zones.png old mode 100755 new mode 100644 diff --git a/resources/img/Aeria-Logo.png b/resources/img/Aeria-Logo.png old mode 100755 new mode 100644 diff --git a/resources/img/file.jpg b/resources/img/file.jpg old mode 100755 new mode 100644 diff --git a/resources/img/select2-spinner.gif b/resources/img/select2-spinner.gif old mode 100755 new mode 100644 diff --git a/resources/img/select2.png b/resources/img/select2.png old mode 100755 new mode 100644 diff --git a/resources/img/select2x2.png b/resources/img/select2x2.png old mode 100755 new mode 100644 diff --git a/resources/js/aeria.ajax.js b/resources/js/aeria.ajax.js new file mode 100644 index 0000000..18171b1 --- /dev/null +++ b/resources/js/aeria.ajax.js @@ -0,0 +1,18 @@ +window.Aeria = window.Aeria || {}; +Aeria.ajax = { + action: function(method,params,callback){ + params = params || {}; + params.ajax = method; + return jQuery.post(AERIA_AJAX.URL, params, callback); + }, + load: function(method,target,fragm,params,callback){ + params = params || {}; params.ajax = method; + return jQuery(target).load(AERIA_AJAX.URL+(fragm!==undefined?' '+fragm:''),params,callback); + }, + append: function(method,target,fragm,params,callback,wrapper){ + params = params || {}; params.ajax = method; + return jQuery(target).append( + jQuery(wrapper||"").load(AERIA_AJAX.URL+(fragm!==undefined?' '+fragm:''),params,callback) + ); + } +}; diff --git a/resources/js/aeria.social.js b/resources/js/aeria.social.js new file mode 100644 index 0000000..85d7e9b --- /dev/null +++ b/resources/js/aeria.social.js @@ -0,0 +1,135 @@ +window.Aeria = window.Aeria || {}; +Aeria.social = (function(){ + var self = {}; + var $ = jQuery; + + function getAjaxCount(data, callback) { + if (window.AERIA_SOCIAL && window.AERIA_SOCIAL.URL != null) { + $.getJSON(window.AERIA_SOCIAL.URL, data, function(response) { + if (response == null) return; + callback(response); + }); + } else { + Aeria.ajax.action('aeriasocial.get', data, function(response) { + if (response == null) return; + callback($.parseJSON(response) || {}); + }); + } + } + + function setSum($t) { + if ($t.data('aeriasocial-summed') == true) return; + $t.attr('data-aeriasocial-summed', true); + + var sum = 0; + $t.find('[data-aeriasocial-count]').each(function() { + sum += ($(this).text() << 0); + }); + + var $cont = $t.parents('.aeriasocial-container'); + if ($cont.length > 0) { + $cont.addClass('enabled'); + + var $sum = $cont.find('[data-aeriasocial-count-sum]'); + if ($sum.length > 0) { + $sum.addClass('enabled').text(sum + ' shares'); + } + } + } + + function popup($t) { + var $p = $t.parents('[data-aeriasocial-uri]'); + + var uri = $p.length > 0 ? encodeURIComponent($p.data('aeriasocial-uri')) : location.href; + var service = $t.data('aeriasocial-service'); + var text = encodeURIComponent($t.data('aeriasocial-text')) || ''; + + if (service === 'facebook') { + popupJS('http://facebook.com/sharer/sharer.php?u=' + uri); + } else if (service === 'twitter') { + var via = encodeURIComponent($t.data('aeriasocial-via')) || ''; + popupJS('https://twitter.com/intent/tweet?url=' + uri + '&text=' + text + '&via=' + via); + } else if (service === 'linkedin') { + popupJS('http://www.linkedin.com/shareArticle?mini=true&url=' + uri); + } else if (service === 'gplus') { + popupJS('https://plus.google.com/share?url=' + uri); + } + } + + function popupJS(uri){ + var w = 550, h = 420, l = Math.floor((screen.width-w)/2), t = Math.floor((screen.height-h)/2); + window.open(uri, '_blank', "width=" + w + ",height=" + h + ",top=" + t + ",left=" + l); + } + + self.load = function() { + var uri = []; + + $('[data-aeriasocial-needajax=true]').each(function(){ + var $t = $(this); + if ($t.data('aeriasocial-needajax') == false) return; + + $t.attr('data-aeriasocial-needajax', false); + $t.attr('data-aeriasocial-summed', false); + uri.push( $t.data('aeriasocial-uri') ); + }); + + if (uri.length === 0) return; + + getAjaxCount({ + uri: uri, + }, function(response) { + if ( ! $.isArray(response)) response = [ response ]; + + $.each(response, function(key, response_per_uri) { + var $t = $('[data-aeriasocial-uri="' + response_per_uri.uri + '"]'); + + $.each(response_per_uri.services, function(srv, count) { + $t.find('[data-aeriasocial-service="' + srv + '"] [data-aeriasocial-count]').html(count); + }); + + setSum($t); + }); + + }); + }, + + self.bind = function() { + $('[data-aeriasocial-uri]').each(function() { + setSum( $(this) ); + }); + + if (self.binded === true) return; + self.binded = true; + + var st = (document.body || document.documentElement).style; + if (st.transition==null && st.WebkitTransition==null && st.MozTransition==null && st.MsTransition==null && st.OTransition==null) { + $(document.body).addClass('no-csstransforms'); + } + + if ('ontouchstart' in window) { + $(document.body).on('touchstart', '.aeriasocial-container', function(){ + var $t = $(this); + if ($t.hasClass('hover')) { + return $t.removeClass('hover'); + } + + $('.aeriasocial-container.hover').removeClass('hover'); + $t.addClass('hover'); + }); + } + + $(document).on('click', '.aeriasocial-btn', function(e){ + e.preventDefault(); + e.stopPropagation(); + popup( $(this) ); + }); + }; + + $(document).ready(function(){ + self.bind(); + self.load(); + }); + + return self; + +})(); diff --git a/resources/js/bootstrap-datetimepicker.js b/resources/js/bootstrap-datetimepicker.js old mode 100755 new mode 100644 diff --git a/resources/js/bootstrap.min.js b/resources/js/bootstrap.min.js old mode 100755 new mode 100644 diff --git a/resources/js/jquery-ui-sortable.js b/resources/js/jquery-ui-sortable.js old mode 100755 new mode 100644 diff --git a/resources/js/moment.min.js b/resources/js/moment.min.js old mode 100755 new mode 100644 diff --git a/resources/js/select2-aeria.js b/resources/js/select2-aeria.js old mode 100755 new mode 100644 diff --git a/resources/js/select2.min.js b/resources/js/select2.min.js old mode 100755 new mode 100644 diff --git a/resources/js/uploads.js b/resources/js/uploads.js old mode 100755 new mode 100644 diff --git a/scripts/admin.js b/scripts/admin.js new file mode 100644 index 0000000..e204b2c --- /dev/null +++ b/scripts/admin.js @@ -0,0 +1,41 @@ +jQuery(function(){ + (function(formatRes,formatSel){ + var $sel = jQuery('.select2'); + $sel.select2({ + formatResult: formatRes, + minimumInputLength: $sel.data('minimum'), + allowClear: false, + formatSelection: formatSel || undefined, + escapeMarkup: function(m) { return m; } + }).on('select2-loaded',function(){ + this.addClass('loaded').css({opacity:1}); + }); + + })( + function(state){ + if (!state.id) return state.text; // optgroup + var originalOption = state.element, + $me = jQuery(originalOption); + var tmp = ''; + if($me.data('image')) { + // tmp += '
'+$me.data('html')+'
'; + + tmp += '
'+$me.data('html')+'
'; + } else { + tmp += $me.data('html') || state.text; + } + return tmp ; + }, + function(state){ + var originalOption = state.element, + $me = jQuery(originalOption); + var tmp = ''; + if($me.data('image')) { + //tmp += '
'+$me.data('html'); + tmp += '
'+$me.data('html'); + } else { + tmp += $me.data('html') || state.text; + } + return tmp ; + }); +}); diff --git a/scripts/aeria.js b/scripts/aeria.js new file mode 100644 index 0000000..89f561a --- /dev/null +++ b/scripts/aeria.js @@ -0,0 +1,125 @@ +window.aeria_setup_media_upload_fields = function(){ + var file_frame; + + jQuery('.aeria_upload_media_button').off('click').on('click', function( event ){ + var $me = jQuery(this), + target = $me.data('target').replace('##','#'), // FIX! + $target = jQuery(target), + $target_image = jQuery(target+'_image'); + + event.preventDefault(); + + /* + // If the media frame already exists, reopen it. + if ( file_frame ) { + file_frame.open(); + return; + } + */ + + // Create the media frame. + file_frame = wp.media.frames.file_frame = wp.media({ + title: jQuery( this ).data( 'uploader_title' ), + button: { + text: jQuery( this ).data( 'uploader_button_text' ) + }, + multiple: false // Set to true to allow multiple files to be selected + }); + + // When an image is selected, run a callback. + file_frame.on( 'select', function() { + // We set multiple to false so only get one image from the uploader + attachment = file_frame.state().get('selection').first().toJSON(); + + // Do something with attachment.id and/or attachment.url here + $target.val(attachment.url); + if($target_image.length) $target_image.attr('src',attachment.url); + }); + + // Finally, open the modal + file_frame.open(); + }); +}; + +window.aeria_setup_media_gallery_fields = function(){ + var file_frame; + + jQuery('.aeria_upload_media_gallery_button').off('click').on('click', function( event ){ + var $me = jQuery(this), + target = $me.data('target').replace('##','#'), // FIX! + $target = jQuery(target), + $target_image = jQuery(target+'_image'); + + event.preventDefault(); + + // Create the media frame. + file_frame = wp.media.frames.file_frame = wp.media({ + title: jQuery( this ).data( 'uploader_title' ), + button: { + text: jQuery( this ).data( 'uploader_button_text' ) + }, + multiple: false // Set to true to allow multiple files to be selected + }); + + // When an image is selected, run a callback. + file_frame.on( 'select', function() { + // We set multiple to false so only get one image from the uploader + attachment = file_frame.state().get('selection').first().toJSON(); + + // Do something with attachment.id and/or attachment.url here + $target.val(attachment.url); + if($target_image.length) $target_image.attr('src',attachment.url); + }); + + // Finally, open the modal + file_frame.open(); + }); +}; + +// Init Select2 +window.aeria_init_select2 = function(){ + jQuery(function(){ + (function(formatRes,formatSel){ + jQuery('.select2:not(.multisettings)').each(function(idx,e){ + var $this = jQuery(e); + if( ! $this.data("select2")){ + $this.select2({ + formatResult: formatRes, + minimumInputLength: $this.data('minimum'), + formatSelection: formatSel || undefined, + escapeMarkup: function(m) { return m; } + }).on('select2-loaded',function(){ + this.addClass('loaded').css({opacity:1}); + }); + } + }); + })( + function(state){ + if (!state.id) return state.text; // optgroup + var originalOption = state.element, + $me = jQuery(originalOption); + var tmp = ''; + if($me.data('image')) { + tmp += '
'+$me.data('html')+'
'; + } else { + tmp += $me.data('html') || state.text; + } + return tmp ; + }, + function(state){ + var originalOption = state.element, + $me = jQuery(originalOption); + var tmp = ''; + if($me.data('image')) { + tmp += '
'+$me.data('html'); + } else { + tmp += $me.data('html') || state.text; + } + return tmp ; + }); + }); +}; + +window.aeria_setup_media_upload_fields(); +window.aeria_setup_media_gallery_fields(); +window.aeria_init_select2(); diff --git a/vendor/Predis/Autoloader.php b/vendor/Predis/Autoloader.php new file mode 100644 index 0000000..d48635a --- /dev/null +++ b/vendor/Predis/Autoloader.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Implements a lightweight PSR-0 compliant autoloader. + * + * @author Eric Naeseth + * @author Daniele Alessandri + */ +class Autoloader +{ + private $directory; + private $prefix; + private $prefixLength; + + /** + * @param string $baseDirectory Base directory where the source files are located. + */ + public function __construct($baseDirectory = __DIR__) + { + $this->directory = $baseDirectory; + $this->prefix = __NAMESPACE__ . '\\'; + $this->prefixLength = strlen($this->prefix); + } + + /** + * Registers the autoloader class with the PHP SPL autoloader. + * + * @param boolean $prepend Prepend the autoloader on the stack instead of appending it. + */ + public static function register($prepend = false) + { + spl_autoload_register(array(new self, 'autoload'), true, $prepend); + } + + /** + * Loads a class from a file using its fully qualified name. + * + * @param string $className Fully qualified name of a class. + */ + public function autoload($className) + { + if (0 === strpos($className, $this->prefix)) { + $parts = explode('\\', substr($className, $this->prefixLength)); + $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php'; + + if (is_file($filepath)) { + require($filepath); + } + } + } +} diff --git a/vendor/Predis/BasicClientInterface.php b/vendor/Predis/BasicClientInterface.php new file mode 100644 index 0000000..a25f410 --- /dev/null +++ b/vendor/Predis/BasicClientInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +use Predis\Command\CommandInterface; + +/** + * Defines the interface of a basic client object or abstraction that + * can send commands to Redis. + * + * @author Daniele Alessandri + */ +interface BasicClientInterface +{ + /** + * Executes the specified Redis command. + * + * @param CommandInterface $command A Redis command. + * @return mixed + */ + public function executeCommand(CommandInterface $command); +} diff --git a/vendor/Predis/Client.php b/vendor/Predis/Client.php new file mode 100644 index 0000000..8abef13 --- /dev/null +++ b/vendor/Predis/Client.php @@ -0,0 +1,436 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +use Predis\Command\CommandInterface; +use Predis\Command\ScriptedCommand; +use Predis\Connection\AggregatedConnectionInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Connection\ConnectionFactoryInterface; +use Predis\Monitor\MonitorContext; +use Predis\Option\ClientOptions; +use Predis\Option\ClientOptionsInterface; +use Predis\Pipeline\PipelineContext; +use Predis\Profile\ServerProfile; +use Predis\PubSub\PubSubContext; +use Predis\Transaction\MultiExecContext; + +/** + * Main class that exposes the most high-level interface to interact with Redis. + * + * @author Daniele Alessandri + */ +class Client implements ClientInterface +{ + const VERSION = '0.8.4'; + + private $options; + private $profile; + private $connection; + + /** + * Initializes a new client with optional connection parameters and client options. + * + * @param mixed $parameters Connection parameters for one or multiple servers. + * @param mixed $options Options that specify certain behaviours for the client. + */ + public function __construct($parameters = null, $options = null) + { + $this->options = $this->filterOptions($options); + $this->profile = $this->options->profile; + $this->connection = $this->initializeConnection($parameters); + } + + /** + * Creates an instance of Predis\Option\ClientOptions from various types of + * arguments (string, array, Predis\Profile\ServerProfile) or returns the + * passed object if it is an instance of Predis\Option\ClientOptions. + * + * @param mixed $options Client options. + * @return ClientOptions + */ + protected function filterOptions($options) + { + if (!isset($options)) { + return new ClientOptions(); + } + + if (is_array($options)) { + return new ClientOptions($options); + } + + if ($options instanceof ClientOptionsInterface) { + return $options; + } + + throw new \InvalidArgumentException("Invalid type for client options"); + } + + /** + * Initializes one or multiple connection (cluster) objects from various + * types of arguments (string, array) or returns the passed object if it + * implements Predis\Connection\ConnectionInterface. + * + * @param mixed $parameters Connection parameters or instance. + * @return ConnectionInterface + */ + protected function initializeConnection($parameters) + { + if ($parameters instanceof ConnectionInterface) { + return $parameters; + } + + if (is_array($parameters) && isset($parameters[0])) { + $options = $this->options; + $replication = isset($options->replication) && $options->replication; + $connection = $options->{$replication ? 'replication' : 'cluster'}; + + return $options->connections->createAggregated($connection, $parameters); + } + + if (is_callable($parameters)) { + $connection = call_user_func($parameters, $this->options); + + if (!$connection instanceof ConnectionInterface) { + throw new \InvalidArgumentException( + 'Callable parameters must return instances of Predis\Connection\ConnectionInterface' + ); + } + + return $connection; + } + + return $this->options->connections->create($parameters); + } + + /** + * {@inheritdoc} + */ + public function getProfile() + { + return $this->profile; + } + + /** + * {@inheritdoc} + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns the connection factory object used by the client. + * + * @return ConnectionFactoryInterface + */ + public function getConnectionFactory() + { + return $this->options->connections; + } + + /** + * Returns a new instance of a client for the specified connection when the + * client is connected to a cluster. The new instance will use the same + * options of the original client. + * + * @return Client + */ + public function getClientFor($connectionID) + { + if (!$connection = $this->getConnectionById($connectionID)) { + throw new \InvalidArgumentException("Invalid connection ID: '$connectionID'"); + } + + return new static($connection, $this->options); + } + + /** + * Opens the connection to the server. + */ + public function connect() + { + $this->connection->connect(); + } + + /** + * Disconnects from the server. + */ + public function disconnect() + { + $this->connection->disconnect(); + } + + /** + * Disconnects from the server. + * + * This method is an alias of disconnect(). + */ + public function quit() + { + $this->disconnect(); + } + + /** + * Checks if the underlying connection is connected to Redis. + * + * @return Boolean True means that the connection is open. + * False means that the connection is closed. + */ + public function isConnected() + { + return $this->connection->isConnected(); + } + + /** + * {@inheritdoc} + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Retrieves a single connection out of an aggregated connections instance. + * + * @param string $connectionId Index or alias of the connection. + * @return Connection\SingleConnectionInterface + */ + public function getConnectionById($connectionId) + { + if (!$this->connection instanceof AggregatedConnectionInterface) { + throw new NotSupportedException('Retrieving connections by ID is supported only when using aggregated connections'); + } + + return $this->connection->getConnectionById($connectionId); + } + + /** + * Dynamically invokes a Redis command with the specified arguments. + * + * @param string $method The name of a Redis command. + * @param array $arguments The arguments for the command. + * @return mixed + */ + public function __call($method, $arguments) + { + $command = $this->profile->createCommand($method, $arguments); + $response = $this->connection->executeCommand($command); + + if ($response instanceof ResponseObjectInterface) { + if ($response instanceof ResponseErrorInterface) { + $response = $this->onResponseError($command, $response); + } + + return $response; + } + + return $command->parseResponse($response); + } + + /** + * {@inheritdoc} + */ + public function createCommand($method, $arguments = array()) + { + return $this->profile->createCommand($method, $arguments); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $response = $this->connection->executeCommand($command); + + if ($response instanceof ResponseObjectInterface) { + if ($response instanceof ResponseErrorInterface) { + $response = $this->onResponseError($command, $response); + } + + return $response; + } + + return $command->parseResponse($response); + } + + /** + * Handles -ERR responses returned by Redis. + * + * @param CommandInterface $command The command that generated the error. + * @param ResponseErrorInterface $response The error response instance. + * @return mixed + */ + protected function onResponseError(CommandInterface $command, ResponseErrorInterface $response) + { + if ($command instanceof ScriptedCommand && $response->getErrorType() === 'NOSCRIPT') { + $eval = $this->createCommand('eval'); + $eval->setRawArguments($command->getEvalArguments()); + + $response = $this->executeCommand($eval); + + if (!$response instanceof ResponseObjectInterface) { + $response = $command->parseResponse($response); + } + + return $response; + } + + if ($this->options->exceptions) { + throw new ServerException($response->getMessage()); + } + + return $response; + } + + /** + * Calls the specified initializer method on $this with 0, 1 or 2 arguments. + * + * TODO: Invert $argv and $initializer. + * + * @param array $argv Arguments for the initializer. + * @param string $initializer The initializer method. + * @return mixed + */ + private function sharedInitializer($argv, $initializer) + { + switch (count($argv)) { + case 0: + return $this->$initializer(); + + case 1: + list($arg0) = $argv; + return is_array($arg0) ? $this->$initializer($arg0) : $this->$initializer(null, $arg0); + + case 2: + list($arg0, $arg1) = $argv; + return $this->$initializer($arg0, $arg1); + + default: + return $this->$initializer($this, $argv); + } + } + + /** + * Creates a new pipeline context and returns it, or returns the results of + * a pipeline executed inside the optionally provided callable object. + * + * @param mixed $arg,... Options for the context, a callable object, or both. + * @return PipelineContext|array + */ + public function pipeline(/* arguments */) + { + return $this->sharedInitializer(func_get_args(), 'initPipeline'); + } + + /** + * Pipeline context initializer. + * + * @param array $options Options for the context. + * @param mixed $callable Optional callable object used to execute the context. + * @return PipelineContext|array + */ + protected function initPipeline(Array $options = null, $callable = null) + { + $executor = isset($options['executor']) ? $options['executor'] : null; + + if (is_callable($executor)) { + $executor = call_user_func($executor, $this, $options); + } + + $pipeline = new PipelineContext($this, $executor); + $replies = $this->pipelineExecute($pipeline, $callable); + + return $replies; + } + + /** + * Executes a pipeline context when a callable object is passed. + * + * @param array $options Options of the context initialization. + * @param mixed $callable Optional callable object used to execute the context. + * @return PipelineContext|array + */ + private function pipelineExecute(PipelineContext $pipeline, $callable) + { + return isset($callable) ? $pipeline->execute($callable) : $pipeline; + } + + /** + * Creates a new transaction context and returns it, or returns the results of + * a transaction executed inside the optionally provided callable object. + * + * @param mixed $arg,... Options for the context, a callable object, or both. + * @return MultiExecContext|array + */ + public function multiExec(/* arguments */) + { + return $this->sharedInitializer(func_get_args(), 'initMultiExec'); + } + + /** + * Transaction context initializer. + * + * @param array $options Options for the context. + * @param mixed $callable Optional callable object used to execute the context. + * @return MultiExecContext|array + */ + protected function initMultiExec(Array $options = null, $callable = null) + { + $transaction = new MultiExecContext($this, $options ?: array()); + return isset($callable) ? $transaction->execute($callable) : $transaction; + } + + /** + * Creates a new Publish / Subscribe context and returns it, or executes it + * inside the optionally provided callable object. + * + * @param mixed $arg,... Options for the context, a callable object, or both. + * @return MultiExecContext|array + */ + public function pubSub(/* arguments */) + { + return $this->sharedInitializer(func_get_args(), 'initPubSub'); + } + + /** + * Publish / Subscribe context initializer. + * + * @param array $options Options for the context. + * @param mixed $callable Optional callable object used to execute the context. + * @return PubSubContext + */ + protected function initPubSub(Array $options = null, $callable = null) + { + $pubsub = new PubSubContext($this, $options); + + if (!isset($callable)) { + return $pubsub; + } + + foreach ($pubsub as $message) { + if (call_user_func($callable, $pubsub, $message) === false) { + $pubsub->closeContext(); + } + } + } + + /** + * Returns a new monitor context. + * + * @return MonitorContext + */ + public function monitor() + { + return new MonitorContext($this); + } +} diff --git a/vendor/Predis/ClientException.php b/vendor/Predis/ClientException.php new file mode 100644 index 0000000..6c07aaf --- /dev/null +++ b/vendor/Predis/ClientException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Exception class that identifies client-side errors. + * + * @author Daniele Alessandri + */ +class ClientException extends PredisException +{ +} diff --git a/vendor/Predis/ClientInterface.php b/vendor/Predis/ClientInterface.php new file mode 100644 index 0000000..767c3da --- /dev/null +++ b/vendor/Predis/ClientInterface.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +use Predis\Connection\ConnectionInterface; +use Predis\Option\ClientOptionsInterface; +use Predis\Profile\ServerProfileInterface; + +/** + * Interface defining the most important parts needed to create an + * high-level Redis client object that can interact with other + * building blocks of Predis. + * + * @author Daniele Alessandri + */ +interface ClientInterface extends BasicClientInterface +{ + /** + * Returns the server profile used by the client. + * + * @return ServerProfileInterface + */ + public function getProfile(); + + /** + * Returns the client options specified upon initialization. + * + * @return ClientOptionsInterface + */ + public function getOptions(); + + /** + * Opens the connection to the server. + */ + public function connect(); + + /** + * Disconnects from the server. + */ + public function disconnect(); + + /** + * Returns the underlying connection instance. + * + * @return ConnectionInterface + */ + public function getConnection(); + + /** + * Creates a new instance of the specified Redis command. + * + * @param string $method The name of a Redis command. + * @param array $arguments The arguments for the command. + * @return Command\CommandInterface + */ + public function createCommand($method, $arguments = array()); +} diff --git a/vendor/Predis/Cluster/CommandHashStrategyInterface.php b/vendor/Predis/Cluster/CommandHashStrategyInterface.php new file mode 100644 index 0000000..bfea050 --- /dev/null +++ b/vendor/Predis/Cluster/CommandHashStrategyInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster; + +use Predis\Command\CommandInterface; + +/** + * Interface for classes defining the strategy used to calculate an hash + * out of keys extracted from supported commands. + * + * This is mostly useful to support clustering via client-side sharding. + * + * @author Daniele Alessandri + */ +interface CommandHashStrategyInterface +{ + /** + * Returns the hash for the given command using the specified algorithm, or null + * if the command cannot be hashed. + * + * @param CommandInterface $command Command to be hashed. + * @return int + */ + public function getHash(CommandInterface $command); + + /** + * Returns the hash for the given key using the specified algorithm. + * + * @param string $key Key to be hashed. + * @return string + */ + public function getKeyHash($key); +} diff --git a/vendor/Predis/Cluster/Distribution/DistributionStrategyInterface.php b/vendor/Predis/Cluster/Distribution/DistributionStrategyInterface.php new file mode 100644 index 0000000..2b07936 --- /dev/null +++ b/vendor/Predis/Cluster/Distribution/DistributionStrategyInterface.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Distribution; + +use Predis\Cluster\Hash\HashGeneratorInterface; + +/** + * A distributor implements the logic to automatically distribute + * keys among several nodes for client-side sharding. + * + * @author Daniele Alessandri + */ +interface DistributionStrategyInterface +{ + /** + * Adds a node to the distributor with an optional weight. + * + * @param mixed $node Node object. + * @param int $weight Weight for the node. + */ + public function add($node, $weight = null); + + /** + * Removes a node from the distributor. + * + * @param mixed $node Node object. + */ + public function remove($node); + + /** + * Gets a node from the distributor using the computed hash of a key. + * + * @return mixed + */ + public function get($key); + + /** + * Returns the underlying hash generator instance. + * + * @return HashGeneratorInterface + */ + public function getHashGenerator(); +} diff --git a/vendor/Predis/Cluster/Distribution/EmptyRingException.php b/vendor/Predis/Cluster/Distribution/EmptyRingException.php new file mode 100644 index 0000000..ed08ca3 --- /dev/null +++ b/vendor/Predis/Cluster/Distribution/EmptyRingException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Distribution; + +/** + * Exception class that identifies empty rings. + * + * @author Daniele Alessandri + */ +class EmptyRingException extends \Exception +{ +} diff --git a/vendor/Predis/Cluster/Distribution/HashRing.php b/vendor/Predis/Cluster/Distribution/HashRing.php new file mode 100644 index 0000000..8958929 --- /dev/null +++ b/vendor/Predis/Cluster/Distribution/HashRing.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Distribution; + +use Predis\Cluster\Hash\HashGeneratorInterface; + +/** + * This class implements an hashring-based distributor that uses the same + * algorithm of memcache to distribute keys in a cluster using client-side + * sharding. + * + * @author Daniele Alessandri + * @author Lorenzo Castelli + */ +class HashRing implements DistributionStrategyInterface, HashGeneratorInterface +{ + const DEFAULT_REPLICAS = 128; + const DEFAULT_WEIGHT = 100; + + private $ring; + private $ringKeys; + private $ringKeysCount; + private $replicas; + private $nodeHashCallback; + private $nodes = array(); + + /** + * @param int $replicas Number of replicas in the ring. + * @param mixed $nodeHashCallback Callback returning the string used to calculate the hash of a node. + */ + public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null) + { + $this->replicas = $replicas; + $this->nodeHashCallback = $nodeHashCallback; + } + + /** + * Adds a node to the ring with an optional weight. + * + * @param mixed $node Node object. + * @param int $weight Weight for the node. + */ + public function add($node, $weight = null) + { + // In case of collisions in the hashes of the nodes, the node added + // last wins, thus the order in which nodes are added is significant. + $this->nodes[] = array('object' => $node, 'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT); + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function remove($node) + { + // A node is removed by resetting the ring so that it's recreated from + // scratch, in order to reassign possible hashes with collisions to the + // right node according to the order in which they were added in the + // first place. + for ($i = 0; $i < count($this->nodes); ++$i) { + if ($this->nodes[$i]['object'] === $node) { + array_splice($this->nodes, $i, 1); + $this->reset(); + break; + } + } + } + + /** + * Resets the distributor. + */ + private function reset() + { + unset( + $this->ring, + $this->ringKeys, + $this->ringKeysCount + ); + } + + /** + * Returns the initialization status of the distributor. + * + * @return Boolean + */ + private function isInitialized() + { + return isset($this->ringKeys); + } + + /** + * Calculates the total weight of all the nodes in the distributor. + * + * @return int + */ + private function computeTotalWeight() + { + $totalWeight = 0; + + foreach ($this->nodes as $node) { + $totalWeight += $node['weight']; + } + + return $totalWeight; + } + + /** + * Initializes the distributor. + */ + private function initialize() + { + if ($this->isInitialized()) { + return; + } + + if (!$this->nodes) { + throw new EmptyRingException('Cannot initialize empty hashring'); + } + + $this->ring = array(); + $totalWeight = $this->computeTotalWeight(); + $nodesCount = count($this->nodes); + + foreach ($this->nodes as $node) { + $weightRatio = $node['weight'] / $totalWeight; + $this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio); + } + + ksort($this->ring, SORT_NUMERIC); + $this->ringKeys = array_keys($this->ring); + $this->ringKeysCount = count($this->ringKeys); + } + + /** + * Implements the logic needed to add a node to the hashring. + * + * @param array $ring Source hashring. + * @param mixed $node Node object to be added. + * @param int $totalNodes Total number of nodes. + * @param int $replicas Number of replicas in the ring. + * @param float $weightRatio Weight ratio for the node. + */ + protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio) + { + $nodeObject = $node['object']; + $nodeHash = $this->getNodeHash($nodeObject); + $replicas = (int) round($weightRatio * $totalNodes * $replicas); + + for ($i = 0; $i < $replicas; $i++) { + $key = crc32("$nodeHash:$i"); + $ring[$key] = $nodeObject; + } + } + + /** + * {@inheritdoc} + */ + protected function getNodeHash($nodeObject) + { + if ($this->nodeHashCallback === null) { + return (string) $nodeObject; + } + + return call_user_func($this->nodeHashCallback, $nodeObject); + } + + /** + * Calculates the hash for the specified value. + * + * @param string $value Input value. + * @return int + */ + public function hash($value) + { + return crc32($value); + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + return $this->ring[$this->getNodeKey($key)]; + } + + /** + * Calculates the corrisponding key of a node distributed in the hashring. + * + * @param int $key Computed hash of a key. + * @return int + */ + private function getNodeKey($key) + { + $this->initialize(); + $ringKeys = $this->ringKeys; + $upper = $this->ringKeysCount - 1; + $lower = 0; + + while ($lower <= $upper) { + $index = ($lower + $upper) >> 1; + $item = $ringKeys[$index]; + + if ($item > $key) { + $upper = $index - 1; + } else if ($item < $key) { + $lower = $index + 1; + } else { + return $item; + } + } + + return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)]; + } + + /** + * Implements a strategy to deal with wrap-around errors during binary searches. + * + * @param int $upper + * @param int $lower + * @param int $ringKeysCount + * @return int + */ + protected function wrapAroundStrategy($upper, $lower, $ringKeysCount) + { + // Binary search for the last item in ringkeys with a value less or + // equal to the key. If no such item exists, return the last item. + return $upper >= 0 ? $upper : $ringKeysCount - 1; + } + + /** + * {@inheritdoc} + */ + public function getHashGenerator() + { + return $this; + } +} diff --git a/vendor/Predis/Cluster/Distribution/KetamaPureRing.php b/vendor/Predis/Cluster/Distribution/KetamaPureRing.php new file mode 100644 index 0000000..d68fa95 --- /dev/null +++ b/vendor/Predis/Cluster/Distribution/KetamaPureRing.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Distribution; + +/** + * This class implements an hashring-based distributor that uses the same + * algorithm of libketama to distribute keys in a cluster using client-side + * sharding. + * + * @author Daniele Alessandri + * @author Lorenzo Castelli + */ +class KetamaPureRing extends HashRing +{ + const DEFAULT_REPLICAS = 160; + + /** + * @param mixed $nodeHashCallback Callback returning the string used to calculate the hash of a node. + */ + public function __construct($nodeHashCallback = null) + { + parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback); + } + + /** + * {@inheritdoc} + */ + protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio) + { + $nodeObject = $node['object']; + $nodeHash = $this->getNodeHash($nodeObject); + $replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4)); + + for ($i = 0; $i < $replicas; $i++) { + $unpackedDigest = unpack('V4', md5("$nodeHash-$i", true)); + + foreach ($unpackedDigest as $key) { + $ring[$key] = $nodeObject; + } + } + } + + /** + * {@inheritdoc} + */ + public function hash($value) + { + $hash = unpack('V', md5($value, true)); + return $hash[1]; + } + + /** + * {@inheritdoc} + */ + protected function wrapAroundStrategy($upper, $lower, $ringKeysCount) + { + // Binary search for the first item in _ringkeys with a value greater + // or equal to the key. If no such item exists, return the first item. + return $lower < $ringKeysCount ? $lower : 0; + } +} diff --git a/vendor/Predis/Cluster/Hash/CRC16HashGenerator.php b/vendor/Predis/Cluster/Hash/CRC16HashGenerator.php new file mode 100644 index 0000000..f4c17f2 --- /dev/null +++ b/vendor/Predis/Cluster/Hash/CRC16HashGenerator.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Hash; + +/** + * This class implements the CRC-CCITT-16 algorithm used by redis-cluster. + * + * @author Daniele Alessandri + */ +class CRC16HashGenerator implements HashGeneratorInterface +{ + private static $CCITT_16 = array( + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, + ); + + /** + * {@inheritdoc} + */ + public function hash($value) + { + // CRC-CCITT-16 algorithm + $crc = 0; + $CCITT_16 = self::$CCITT_16; + $strlen = strlen($value); + + for ($i = 0; $i < $strlen; $i++) { + $crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF; + } + + return $crc; + } +} diff --git a/vendor/Predis/Cluster/Hash/HashGeneratorInterface.php b/vendor/Predis/Cluster/Hash/HashGeneratorInterface.php new file mode 100644 index 0000000..891320d --- /dev/null +++ b/vendor/Predis/Cluster/Hash/HashGeneratorInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster\Hash; + +/** + * A generator of node keys implements the logic used to calculate the hash of + * a key to distribute the respective operations among nodes. + * + * @author Daniele Alessandri + */ +interface HashGeneratorInterface +{ + /** + * Generates an hash that is used by the distributor algorithm + * + * @param string $value Value used to generate the hash. + * @return int + */ + public function hash($value); +} diff --git a/vendor/Predis/Cluster/PredisClusterHashStrategy.php b/vendor/Predis/Cluster/PredisClusterHashStrategy.php new file mode 100644 index 0000000..9b9b303 --- /dev/null +++ b/vendor/Predis/Cluster/PredisClusterHashStrategy.php @@ -0,0 +1,385 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster; + +use Predis\Cluster\Hash\HashGeneratorInterface; +use Predis\Command\CommandInterface; +use Predis\Command\ScriptedCommand; + +/** + * Default class used by Predis for client-side sharding to calculate + * hashes out of keys of supported commands. + * + * @author Daniele Alessandri + */ +class PredisClusterHashStrategy implements CommandHashStrategyInterface +{ + private $commands; + private $hashGenerator; + + /** + * @param HashGeneratorInterface $hashGenerator Hash generator instance. + */ + public function __construct(HashGeneratorInterface $hashGenerator) + { + $this->commands = $this->getDefaultCommands(); + $this->hashGenerator = $hashGenerator; + } + + /** + * Returns the default map of supported commands with their handlers. + * + * @return array + */ + protected function getDefaultCommands() + { + $keyIsFirstArgument = array($this, 'getKeyFromFirstArgument'); + $keysAreAllArguments = array($this, 'getKeyFromAllArguments'); + + return array( + /* commands operating on the key space */ + 'EXISTS' => $keyIsFirstArgument, + 'DEL' => $keysAreAllArguments, + 'TYPE' => $keyIsFirstArgument, + 'EXPIRE' => $keyIsFirstArgument, + 'EXPIREAT' => $keyIsFirstArgument, + 'PERSIST' => $keyIsFirstArgument, + 'PEXPIRE' => $keyIsFirstArgument, + 'PEXPIREAT' => $keyIsFirstArgument, + 'TTL' => $keyIsFirstArgument, + 'PTTL' => $keyIsFirstArgument, + 'SORT' => $keyIsFirstArgument, // TODO + 'DUMP' => $keyIsFirstArgument, + 'RESTORE' => $keyIsFirstArgument, + + /* commands operating on string values */ + 'APPEND' => $keyIsFirstArgument, + 'DECR' => $keyIsFirstArgument, + 'DECRBY' => $keyIsFirstArgument, + 'GET' => $keyIsFirstArgument, + 'GETBIT' => $keyIsFirstArgument, + 'MGET' => $keysAreAllArguments, + 'SET' => $keyIsFirstArgument, + 'GETRANGE' => $keyIsFirstArgument, + 'GETSET' => $keyIsFirstArgument, + 'INCR' => $keyIsFirstArgument, + 'INCRBY' => $keyIsFirstArgument, + 'SETBIT' => $keyIsFirstArgument, + 'SETEX' => $keyIsFirstArgument, + 'MSET' => array($this, 'getKeyFromInterleavedArguments'), + 'MSETNX' => array($this, 'getKeyFromInterleavedArguments'), + 'SETNX' => $keyIsFirstArgument, + 'SETRANGE' => $keyIsFirstArgument, + 'STRLEN' => $keyIsFirstArgument, + 'SUBSTR' => $keyIsFirstArgument, + 'BITOP' => array($this, 'getKeyFromBitOp'), + 'BITCOUNT' => $keyIsFirstArgument, + + /* commands operating on lists */ + 'LINSERT' => $keyIsFirstArgument, + 'LINDEX' => $keyIsFirstArgument, + 'LLEN' => $keyIsFirstArgument, + 'LPOP' => $keyIsFirstArgument, + 'RPOP' => $keyIsFirstArgument, + 'RPOPLPUSH' => $keysAreAllArguments, + 'BLPOP' => array($this, 'getKeyFromBlockingListCommands'), + 'BRPOP' => array($this, 'getKeyFromBlockingListCommands'), + 'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'), + 'LPUSH' => $keyIsFirstArgument, + 'LPUSHX' => $keyIsFirstArgument, + 'RPUSH' => $keyIsFirstArgument, + 'RPUSHX' => $keyIsFirstArgument, + 'LRANGE' => $keyIsFirstArgument, + 'LREM' => $keyIsFirstArgument, + 'LSET' => $keyIsFirstArgument, + 'LTRIM' => $keyIsFirstArgument, + + /* commands operating on sets */ + 'SADD' => $keyIsFirstArgument, + 'SCARD' => $keyIsFirstArgument, + 'SDIFF' => $keysAreAllArguments, + 'SDIFFSTORE' => $keysAreAllArguments, + 'SINTER' => $keysAreAllArguments, + 'SINTERSTORE' => $keysAreAllArguments, + 'SUNION' => $keysAreAllArguments, + 'SUNIONSTORE' => $keysAreAllArguments, + 'SISMEMBER' => $keyIsFirstArgument, + 'SMEMBERS' => $keyIsFirstArgument, + 'SPOP' => $keyIsFirstArgument, + 'SRANDMEMBER' => $keyIsFirstArgument, + 'SREM' => $keyIsFirstArgument, + + /* commands operating on sorted sets */ + 'ZADD' => $keyIsFirstArgument, + 'ZCARD' => $keyIsFirstArgument, + 'ZCOUNT' => $keyIsFirstArgument, + 'ZINCRBY' => $keyIsFirstArgument, + 'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), + 'ZRANGE' => $keyIsFirstArgument, + 'ZRANGEBYSCORE' => $keyIsFirstArgument, + 'ZRANK' => $keyIsFirstArgument, + 'ZREM' => $keyIsFirstArgument, + 'ZREMRANGEBYRANK' => $keyIsFirstArgument, + 'ZREMRANGEBYSCORE' => $keyIsFirstArgument, + 'ZREVRANGE' => $keyIsFirstArgument, + 'ZREVRANGEBYSCORE' => $keyIsFirstArgument, + 'ZREVRANK' => $keyIsFirstArgument, + 'ZSCORE' => $keyIsFirstArgument, + 'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), + + /* commands operating on hashes */ + 'HDEL' => $keyIsFirstArgument, + 'HEXISTS' => $keyIsFirstArgument, + 'HGET' => $keyIsFirstArgument, + 'HGETALL' => $keyIsFirstArgument, + 'HMGET' => $keyIsFirstArgument, + 'HMSET' => $keyIsFirstArgument, + 'HINCRBY' => $keyIsFirstArgument, + 'HINCRBYFLOAT' => $keyIsFirstArgument, + 'HKEYS' => $keyIsFirstArgument, + 'HLEN' => $keyIsFirstArgument, + 'HSET' => $keyIsFirstArgument, + 'HSETNX' => $keyIsFirstArgument, + 'HVALS' => $keyIsFirstArgument, + + /* scripting */ + 'EVAL' => array($this, 'getKeyFromScriptingCommands'), + 'EVALSHA' => array($this, 'getKeyFromScriptingCommands'), + ); + } + + /** + * Returns the list of IDs for the supported commands. + * + * @return array + */ + public function getSupportedCommands() + { + return array_keys($this->commands); + } + + /** + * Sets an handler for the specified command ID. + * + * The signature of the callback must have a single parameter + * of type Predis\Command\CommandInterface. + * + * When the callback argument is omitted or NULL, the previously + * associated handler for the specified command ID is removed. + * + * @param string $commandId The ID of the command to be handled. + * @param mixed $callback A valid callable object or NULL. + */ + public function setCommandHandler($commandId, $callback = null) + { + $commandId = strtoupper($commandId); + + if (!isset($callback)) { + unset($this->commands[$commandId]); + return; + } + + if (!is_callable($callback)) { + throw new \InvalidArgumentException("Callback must be a valid callable object or NULL"); + } + + $this->commands[$commandId] = $callback; + } + + /** + * Extracts the key from the first argument of a command instance. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromFirstArgument(CommandInterface $command) + { + return $command->getArgument(0); + } + + /** + * Extracts the key from a command with multiple keys only when all keys + * in the arguments array produce the same hash. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromAllArguments(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if ($this->checkSameHashForKeys($arguments)) { + return $arguments[0]; + } + } + + /** + * Extracts the key from a command with multiple keys only when all keys + * in the arguments array produce the same hash. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromInterleavedArguments(CommandInterface $command) + { + $arguments = $command->getArguments(); + $keys = array(); + + for ($i = 0; $i < count($arguments); $i += 2) { + $keys[] = $arguments[$i]; + } + + if ($this->checkSameHashForKeys($keys)) { + return $arguments[0]; + } + } + + /** + * Extracts the key from BLPOP and BRPOP commands. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromBlockingListCommands(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if ($this->checkSameHashForKeys(array_slice($arguments, 0, count($arguments) - 1))) { + return $arguments[0]; + } + } + + /** + * Extracts the key from BITOP command. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromBitOp(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if ($this->checkSameHashForKeys(array_slice($arguments, 1, count($arguments)))) { + return $arguments[1]; + } + } + + /** + * Extracts the key from ZINTERSTORE and ZUNIONSTORE commands. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromZsetAggregationCommands(CommandInterface $command) + { + $arguments = $command->getArguments(); + $keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1])); + + if ($this->checkSameHashForKeys($keys)) { + return $arguments[0]; + } + } + + /** + * Extracts the key from EVAL and EVALSHA commands. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromScriptingCommands(CommandInterface $command) + { + if ($command instanceof ScriptedCommand) { + $keys = $command->getKeys(); + } else { + $keys = array_slice($args = $command->getArguments(), 2, $args[1]); + } + + if ($keys && $this->checkSameHashForKeys($keys)) { + return $keys[0]; + } + } + + /** + * {@inheritdoc} + */ + public function getHash(CommandInterface $command) + { + $hash = $command->getHash(); + + if (!isset($hash) && isset($this->commands[$cmdID = $command->getId()])) { + $key = call_user_func($this->commands[$cmdID], $command); + + if (isset($key)) { + $hash = $this->getKeyHash($key); + $command->setHash($hash); + } + } + + return $hash; + } + + /** + * {@inheritdoc} + */ + public function getKeyHash($key) + { + $key = $this->extractKeyTag($key); + $hash = $this->hashGenerator->hash($key); + + return $hash; + } + + /** + * Checks if the specified array of keys will generate the same hash. + * + * @param array $keys Array of keys. + * @return Boolean + */ + protected function checkSameHashForKeys(Array $keys) + { + if (!$count = count($keys)) { + return false; + } + + $currentKey = $this->extractKeyTag($keys[0]); + + for ($i = 1; $i < $count; $i++) { + $nextKey = $this->extractKeyTag($keys[$i]); + + if ($currentKey !== $nextKey) { + return false; + } + + $currentKey = $nextKey; + } + + return true; + } + + /** + * Returns only the hashable part of a key (delimited by "{...}"), or the + * whole key if a key tag is not found in the string. + * + * @param string $key A key. + * @return string + */ + protected function extractKeyTag($key) + { + if (false !== $start = strpos($key, '{')) { + if (false !== $end = strpos($key, '}', $start)) { + $key = substr($key, ++$start, $end - $start); + } + } + + return $key; + } +} diff --git a/vendor/Predis/Cluster/RedisClusterHashStrategy.php b/vendor/Predis/Cluster/RedisClusterHashStrategy.php new file mode 100644 index 0000000..bec2bf5 --- /dev/null +++ b/vendor/Predis/Cluster/RedisClusterHashStrategy.php @@ -0,0 +1,288 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Cluster; + +use Predis\Cluster\Hash\CRC16HashGenerator; +use Predis\Command\CommandInterface; +use Predis\Command\ScriptedCommand; + +/** + * Default class used by Predis to calculate hashes out of keys of + * commands supported by redis-cluster. + * + * @author Daniele Alessandri + */ +class RedisClusterHashStrategy implements CommandHashStrategyInterface +{ + private $commands; + private $hashGenerator; + + /** + * + */ + public function __construct() + { + $this->commands = $this->getDefaultCommands(); + $this->hashGenerator = new CRC16HashGenerator(); + } + + /** + * Returns the default map of supported commands with their handlers. + * + * @return array + */ + protected function getDefaultCommands() + { + $keyIsFirstArgument = array($this, 'getKeyFromFirstArgument'); + + return array( + /* commands operating on the key space */ + 'EXISTS' => $keyIsFirstArgument, + 'DEL' => array($this, 'getKeyFromAllArguments'), + 'TYPE' => $keyIsFirstArgument, + 'EXPIRE' => $keyIsFirstArgument, + 'EXPIREAT' => $keyIsFirstArgument, + 'PERSIST' => $keyIsFirstArgument, + 'PEXPIRE' => $keyIsFirstArgument, + 'PEXPIREAT' => $keyIsFirstArgument, + 'TTL' => $keyIsFirstArgument, + 'PTTL' => $keyIsFirstArgument, + 'SORT' => $keyIsFirstArgument, // TODO + + /* commands operating on string values */ + 'APPEND' => $keyIsFirstArgument, + 'DECR' => $keyIsFirstArgument, + 'DECRBY' => $keyIsFirstArgument, + 'GET' => $keyIsFirstArgument, + 'GETBIT' => $keyIsFirstArgument, + 'MGET' => array($this, 'getKeyFromAllArguments'), + 'SET' => $keyIsFirstArgument, + 'GETRANGE' => $keyIsFirstArgument, + 'GETSET' => $keyIsFirstArgument, + 'INCR' => $keyIsFirstArgument, + 'INCRBY' => $keyIsFirstArgument, + 'SETBIT' => $keyIsFirstArgument, + 'SETEX' => $keyIsFirstArgument, + 'MSET' => array($this, 'getKeyFromInterleavedArguments'), + 'MSETNX' => array($this, 'getKeyFromInterleavedArguments'), + 'SETNX' => $keyIsFirstArgument, + 'SETRANGE' => $keyIsFirstArgument, + 'STRLEN' => $keyIsFirstArgument, + 'SUBSTR' => $keyIsFirstArgument, + 'BITCOUNT' => $keyIsFirstArgument, + + /* commands operating on lists */ + 'LINSERT' => $keyIsFirstArgument, + 'LINDEX' => $keyIsFirstArgument, + 'LLEN' => $keyIsFirstArgument, + 'LPOP' => $keyIsFirstArgument, + 'RPOP' => $keyIsFirstArgument, + 'BLPOP' => array($this, 'getKeyFromBlockingListCommands'), + 'BRPOP' => array($this, 'getKeyFromBlockingListCommands'), + 'LPUSH' => $keyIsFirstArgument, + 'LPUSHX' => $keyIsFirstArgument, + 'RPUSH' => $keyIsFirstArgument, + 'RPUSHX' => $keyIsFirstArgument, + 'LRANGE' => $keyIsFirstArgument, + 'LREM' => $keyIsFirstArgument, + 'LSET' => $keyIsFirstArgument, + 'LTRIM' => $keyIsFirstArgument, + + /* commands operating on sets */ + 'SADD' => $keyIsFirstArgument, + 'SCARD' => $keyIsFirstArgument, + 'SISMEMBER' => $keyIsFirstArgument, + 'SMEMBERS' => $keyIsFirstArgument, + 'SPOP' => $keyIsFirstArgument, + 'SRANDMEMBER' => $keyIsFirstArgument, + 'SREM' => $keyIsFirstArgument, + + /* commands operating on sorted sets */ + 'ZADD' => $keyIsFirstArgument, + 'ZCARD' => $keyIsFirstArgument, + 'ZCOUNT' => $keyIsFirstArgument, + 'ZINCRBY' => $keyIsFirstArgument, + 'ZRANGE' => $keyIsFirstArgument, + 'ZRANGEBYSCORE' => $keyIsFirstArgument, + 'ZRANK' => $keyIsFirstArgument, + 'ZREM' => $keyIsFirstArgument, + 'ZREMRANGEBYRANK' => $keyIsFirstArgument, + 'ZREMRANGEBYSCORE' => $keyIsFirstArgument, + 'ZREVRANGE' => $keyIsFirstArgument, + 'ZREVRANGEBYSCORE' => $keyIsFirstArgument, + 'ZREVRANK' => $keyIsFirstArgument, + 'ZSCORE' => $keyIsFirstArgument, + + /* commands operating on hashes */ + 'HDEL' => $keyIsFirstArgument, + 'HEXISTS' => $keyIsFirstArgument, + 'HGET' => $keyIsFirstArgument, + 'HGETALL' => $keyIsFirstArgument, + 'HMGET' => $keyIsFirstArgument, + 'HMSET' => $keyIsFirstArgument, + 'HINCRBY' => $keyIsFirstArgument, + 'HINCRBYFLOAT' => $keyIsFirstArgument, + 'HKEYS' => $keyIsFirstArgument, + 'HLEN' => $keyIsFirstArgument, + 'HSET' => $keyIsFirstArgument, + 'HSETNX' => $keyIsFirstArgument, + 'HVALS' => $keyIsFirstArgument, + + /* scripting */ + 'EVAL' => array($this, 'getKeyFromScriptingCommands'), + 'EVALSHA' => array($this, 'getKeyFromScriptingCommands'), + ); + } + + /** + * Returns the list of IDs for the supported commands. + * + * @return array + */ + public function getSupportedCommands() + { + return array_keys($this->commands); + } + + /** + * Sets an handler for the specified command ID. + * + * The signature of the callback must have a single parameter + * of type Predis\Command\CommandInterface. + * + * When the callback argument is omitted or NULL, the previously + * associated handler for the specified command ID is removed. + * + * @param string $commandId The ID of the command to be handled. + * @param mixed $callback A valid callable object or NULL. + */ + public function setCommandHandler($commandId, $callback = null) + { + $commandId = strtoupper($commandId); + + if (!isset($callback)) { + unset($this->commands[$commandId]); + return; + } + + if (!is_callable($callback)) { + throw new \InvalidArgumentException("Callback must be a valid callable object or NULL"); + } + + $this->commands[$commandId] = $callback; + } + + /** + * Extracts the key from the first argument of a command instance. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromFirstArgument(CommandInterface $command) + { + return $command->getArgument(0); + } + + /** + * Extracts the key from a command that can accept multiple keys ensuring + * that only one key is actually specified to comply with redis-cluster. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromAllArguments(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if (count($arguments) === 1) { + return $arguments[0]; + } + } + + /** + * Extracts the key from a command that can accept multiple keys ensuring + * that only one key is actually specified to comply with redis-cluster. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromInterleavedArguments(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if (count($arguments) === 2) { + return $arguments[0]; + } + } + + /** + * Extracts the key from BLPOP and BRPOP commands ensuring that only one key + * is actually specified to comply with redis-cluster. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromBlockingListCommands(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if (count($arguments) === 2) { + return $arguments[0]; + } + } + + /** + * Extracts the key from EVAL and EVALSHA commands. + * + * @param CommandInterface $command Command instance. + * @return string + */ + protected function getKeyFromScriptingCommands(CommandInterface $command) + { + if ($command instanceof ScriptedCommand) { + $keys = $command->getKeys(); + } else { + $keys = array_slice($args = $command->getArguments(), 2, $args[1]); + } + + if (count($keys) === 1) { + return $keys[0]; + } + } + + /** + * {@inheritdoc} + */ + public function getHash(CommandInterface $command) + { + $hash = $command->getHash(); + + if (!isset($hash) && isset($this->commands[$cmdID = $command->getId()])) { + $key = call_user_func($this->commands[$cmdID], $command); + + if (isset($key)) { + $hash = $this->hashGenerator->hash($key); + $command->setHash($hash); + } + } + + return $hash; + } + + /** + * {@inheritdoc} + */ + public function getKeyHash($key) + { + return $this->hashGenerator->hash($key); + } +} diff --git a/vendor/Predis/Command/AbstractCommand.php b/vendor/Predis/Command/AbstractCommand.php new file mode 100644 index 0000000..0584a9b --- /dev/null +++ b/vendor/Predis/Command/AbstractCommand.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Base class for Redis commands. + * + * @author Daniele Alessandri + */ +abstract class AbstractCommand implements CommandInterface +{ + private $hash; + private $arguments = array(); + + /** + * Returns a filtered array of the arguments. + * + * @param array $arguments List of arguments. + * @return array + */ + protected function filterArguments(Array $arguments) + { + return $arguments; + } + + /** + * {@inheritdoc} + */ + public function setArguments(Array $arguments) + { + $this->arguments = $this->filterArguments($arguments); + unset($this->hash); + } + + /** + * Sets the arguments array without filtering. + * + * @param array $arguments List of arguments. + */ + public function setRawArguments(Array $arguments) + { + $this->arguments = $arguments; + unset($this->hash); + } + + /** + * {@inheritdoc} + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Gets the argument from the arguments list at the specified index. + * + * @param array $arguments Position of the argument. + */ + public function getArgument($index) + { + if (isset($this->arguments[$index])) { + return $this->arguments[$index]; + } + } + + /** + * {@inheritdoc} + */ + public function setHash($hash) + { + $this->hash = $hash; + } + + /** + * {@inheritdoc} + */ + public function getHash() + { + if (isset($this->hash)) { + return $this->hash; + } + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data; + } + + /** + * Helper function used to reduce a list of arguments to a string. + * + * @param string $accumulator Temporary string. + * @param string $argument Current argument. + * @return string + */ + protected function toStringArgumentReducer($accumulator, $argument) + { + if (strlen($argument) > 32) { + $argument = substr($argument, 0, 32) . '[...]'; + } + + $accumulator .= " $argument"; + + return $accumulator; + } + + /** + * Returns a partial string representation of the command with its arguments. + * + * @return string + */ + public function __toString() + { + return array_reduce( + $this->getArguments(), + array($this, 'toStringArgumentReducer'), + $this->getId() + ); + } + + /** + * Normalizes the arguments array passed to a Redis command. + * + * @param array $arguments Arguments for a command. + * @return array + */ + public static function normalizeArguments(Array $arguments) + { + if (count($arguments) === 1 && is_array($arguments[0])) { + return $arguments[0]; + } + + return $arguments; + } + + /** + * Normalizes the arguments array passed to a variadic Redis command. + * + * @param array $arguments Arguments for a command. + * @return array + */ + public static function normalizeVariadic(Array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + return array_merge(array($arguments[0]), $arguments[1]); + } + + return $arguments; + } +} diff --git a/vendor/Predis/Command/CommandInterface.php b/vendor/Predis/Command/CommandInterface.php new file mode 100644 index 0000000..0be8c81 --- /dev/null +++ b/vendor/Predis/Command/CommandInterface.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Defines an abstraction representing a Redis command. + * @author Daniele Alessandri + */ +interface CommandInterface +{ + /** + * Gets the ID of a Redis command. + * + * @return string + */ + public function getId(); + + /** + * Set the hash for the command. + * + * @param int $hash Calculated hash. + */ + public function setHash($hash); + + /** + * Returns the hash of the command. + * + * @return int + */ + public function getHash(); + + /** + * Sets the arguments for the command. + * + * @param array $arguments List of arguments. + */ + public function setArguments(Array $arguments); + + /** + * Sets the raw arguments for the command without processing them. + * + * @param array $arguments List of arguments. + */ + public function setRawArguments(Array $arguments); + + /** + * Gets the arguments of the command. + * + * @return array + */ + public function getArguments(); + + /** + * Gets the argument of the command at the specified index. + * + * @return array + */ + public function getArgument($index); + + /** + * Parses a reply buffer and returns a PHP object. + * + * @param string $data Binary string containing the whole reply. + * @return mixed + */ + public function parseResponse($data); +} diff --git a/vendor/Predis/Command/ConnectionAuth.php b/vendor/Predis/Command/ConnectionAuth.php new file mode 100644 index 0000000..de0d3ee --- /dev/null +++ b/vendor/Predis/Command/ConnectionAuth.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/auth + * @author Daniele Alessandri + */ +class ConnectionAuth extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'AUTH'; + } +} diff --git a/vendor/Predis/Command/ConnectionEcho.php b/vendor/Predis/Command/ConnectionEcho.php new file mode 100644 index 0000000..dd9d072 --- /dev/null +++ b/vendor/Predis/Command/ConnectionEcho.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/echo + * @author Daniele Alessandri + */ +class ConnectionEcho extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ECHO'; + } +} diff --git a/vendor/Predis/Command/ConnectionPing.php b/vendor/Predis/Command/ConnectionPing.php new file mode 100644 index 0000000..cf29416 --- /dev/null +++ b/vendor/Predis/Command/ConnectionPing.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/ping + * @author Daniele Alessandri + */ +class ConnectionPing extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PING'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data === 'PONG' ? true : false; + } +} diff --git a/vendor/Predis/Command/ConnectionQuit.php b/vendor/Predis/Command/ConnectionQuit.php new file mode 100644 index 0000000..641cc35 --- /dev/null +++ b/vendor/Predis/Command/ConnectionQuit.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/quit + * @author Daniele Alessandri + */ +class ConnectionQuit extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'QUIT'; + } +} diff --git a/vendor/Predis/Command/ConnectionSelect.php b/vendor/Predis/Command/ConnectionSelect.php new file mode 100644 index 0000000..51b96b7 --- /dev/null +++ b/vendor/Predis/Command/ConnectionSelect.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/select + * @author Daniele Alessandri + */ +class ConnectionSelect extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SELECT'; + } +} diff --git a/vendor/Predis/Command/HashDelete.php b/vendor/Predis/Command/HashDelete.php new file mode 100644 index 0000000..9584678 --- /dev/null +++ b/vendor/Predis/Command/HashDelete.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hdel + * @author Daniele Alessandri + */ +class HashDelete extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HDEL'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/Predis/Command/HashExists.php b/vendor/Predis/Command/HashExists.php new file mode 100644 index 0000000..a9d620a --- /dev/null +++ b/vendor/Predis/Command/HashExists.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hexists + * @author Daniele Alessandri + */ +class HashExists extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HEXISTS'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/HashGet.php b/vendor/Predis/Command/HashGet.php new file mode 100644 index 0000000..b18583a --- /dev/null +++ b/vendor/Predis/Command/HashGet.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hget + * @author Daniele Alessandri + */ +class HashGet extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HGET'; + } +} diff --git a/vendor/Predis/Command/HashGetAll.php b/vendor/Predis/Command/HashGetAll.php new file mode 100644 index 0000000..51a745f --- /dev/null +++ b/vendor/Predis/Command/HashGetAll.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hgetall + * @author Daniele Alessandri + */ +class HashGetAll extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HGETALL'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $result = array(); + + for ($i = 0; $i < count($data); $i++) { + $result[$data[$i]] = $data[++$i]; + } + + return $result; + } +} diff --git a/vendor/Predis/Command/HashGetMultiple.php b/vendor/Predis/Command/HashGetMultiple.php new file mode 100644 index 0000000..d533489 --- /dev/null +++ b/vendor/Predis/Command/HashGetMultiple.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hmget + * @author Daniele Alessandri + */ +class HashGetMultiple extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HMGET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/Predis/Command/HashIncrementBy.php b/vendor/Predis/Command/HashIncrementBy.php new file mode 100644 index 0000000..1c43e63 --- /dev/null +++ b/vendor/Predis/Command/HashIncrementBy.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hincrby + * @author Daniele Alessandri + */ +class HashIncrementBy extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HINCRBY'; + } +} diff --git a/vendor/Predis/Command/HashIncrementByFloat.php b/vendor/Predis/Command/HashIncrementByFloat.php new file mode 100644 index 0000000..7597058 --- /dev/null +++ b/vendor/Predis/Command/HashIncrementByFloat.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hincrbyfloat + * @author Daniele Alessandri + */ +class HashIncrementByFloat extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HINCRBYFLOAT'; + } +} diff --git a/vendor/Predis/Command/HashKeys.php b/vendor/Predis/Command/HashKeys.php new file mode 100644 index 0000000..279f09a --- /dev/null +++ b/vendor/Predis/Command/HashKeys.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hkeys + * @author Daniele Alessandri + */ +class HashKeys extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HKEYS'; + } +} diff --git a/vendor/Predis/Command/HashLength.php b/vendor/Predis/Command/HashLength.php new file mode 100644 index 0000000..b2a59c0 --- /dev/null +++ b/vendor/Predis/Command/HashLength.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hlen + * @author Daniele Alessandri + */ +class HashLength extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HLEN'; + } +} diff --git a/vendor/Predis/Command/HashSet.php b/vendor/Predis/Command/HashSet.php new file mode 100644 index 0000000..588afc4 --- /dev/null +++ b/vendor/Predis/Command/HashSet.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hset + * @author Daniele Alessandri + */ +class HashSet extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSET'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/HashSetMultiple.php b/vendor/Predis/Command/HashSetMultiple.php new file mode 100644 index 0000000..f84485f --- /dev/null +++ b/vendor/Predis/Command/HashSetMultiple.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hmset + * @author Daniele Alessandri + */ +class HashSetMultiple extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HMSET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $flattenedKVs = array($arguments[0]); + $args = $arguments[1]; + + foreach ($args as $k => $v) { + $flattenedKVs[] = $k; + $flattenedKVs[] = $v; + } + + return $flattenedKVs; + } + + return $arguments; + } +} diff --git a/vendor/Predis/Command/HashSetPreserve.php b/vendor/Predis/Command/HashSetPreserve.php new file mode 100644 index 0000000..3e9890a --- /dev/null +++ b/vendor/Predis/Command/HashSetPreserve.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hsetnx + * @author Daniele Alessandri + */ +class HashSetPreserve extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSETNX'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/HashValues.php b/vendor/Predis/Command/HashValues.php new file mode 100644 index 0000000..fec3116 --- /dev/null +++ b/vendor/Predis/Command/HashValues.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/hvals + * @author Daniele Alessandri + */ +class HashValues extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HVALS'; + } +} diff --git a/vendor/Predis/Command/KeyDelete.php b/vendor/Predis/Command/KeyDelete.php new file mode 100644 index 0000000..4e43141 --- /dev/null +++ b/vendor/Predis/Command/KeyDelete.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/del + * @author Daniele Alessandri + */ +class KeyDelete extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DEL'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeArguments($arguments); + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::all($this, $prefix); + } +} diff --git a/vendor/Predis/Command/KeyDump.php b/vendor/Predis/Command/KeyDump.php new file mode 100644 index 0000000..55cb90b --- /dev/null +++ b/vendor/Predis/Command/KeyDump.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/dump + * @author Daniele Alessandri + */ +class KeyDump extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DUMP'; + } +} diff --git a/vendor/Predis/Command/KeyExists.php b/vendor/Predis/Command/KeyExists.php new file mode 100644 index 0000000..7f877cd --- /dev/null +++ b/vendor/Predis/Command/KeyExists.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/exists + * @author Daniele Alessandri + */ +class KeyExists extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXISTS'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/KeyExpire.php b/vendor/Predis/Command/KeyExpire.php new file mode 100644 index 0000000..9db8e02 --- /dev/null +++ b/vendor/Predis/Command/KeyExpire.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/expire + * @author Daniele Alessandri + */ +class KeyExpire extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXPIRE'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/KeyExpireAt.php b/vendor/Predis/Command/KeyExpireAt.php new file mode 100644 index 0000000..1f6db38 --- /dev/null +++ b/vendor/Predis/Command/KeyExpireAt.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/expireat + * @author Daniele Alessandri + */ +class KeyExpireAt extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXPIREAT'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/KeyKeys.php b/vendor/Predis/Command/KeyKeys.php new file mode 100644 index 0000000..3b973ed --- /dev/null +++ b/vendor/Predis/Command/KeyKeys.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/keys + * @author Daniele Alessandri + */ +class KeyKeys extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'KEYS'; + } +} diff --git a/vendor/Predis/Command/KeyKeysV12x.php b/vendor/Predis/Command/KeyKeysV12x.php new file mode 100644 index 0000000..2e0d3c9 --- /dev/null +++ b/vendor/Predis/Command/KeyKeysV12x.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/keys + * @author Daniele Alessandri + * @deprecated + */ +class KeyKeysV12x extends KeyKeys +{ + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return explode(' ', $data); + } +} diff --git a/vendor/Predis/Command/KeyMove.php b/vendor/Predis/Command/KeyMove.php new file mode 100644 index 0000000..9e2a64d --- /dev/null +++ b/vendor/Predis/Command/KeyMove.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/move + * @author Daniele Alessandri + */ +class KeyMove extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MOVE'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/KeyPersist.php b/vendor/Predis/Command/KeyPersist.php new file mode 100644 index 0000000..5c6b23c --- /dev/null +++ b/vendor/Predis/Command/KeyPersist.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/persist + * @author Daniele Alessandri + */ +class KeyPersist extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PERSIST'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/KeyPreciseExpire.php b/vendor/Predis/Command/KeyPreciseExpire.php new file mode 100644 index 0000000..3fb8678 --- /dev/null +++ b/vendor/Predis/Command/KeyPreciseExpire.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pexpire + * @author Daniele Alessandri + */ +class KeyPreciseExpire extends KeyExpire +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PEXPIRE'; + } +} diff --git a/vendor/Predis/Command/KeyPreciseExpireAt.php b/vendor/Predis/Command/KeyPreciseExpireAt.php new file mode 100644 index 0000000..14697f2 --- /dev/null +++ b/vendor/Predis/Command/KeyPreciseExpireAt.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pexpireat + * @author Daniele Alessandri + */ +class KeyPreciseExpireAt extends KeyExpireAt +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PEXPIREAT'; + } +} diff --git a/vendor/Predis/Command/KeyPreciseTimeToLive.php b/vendor/Predis/Command/KeyPreciseTimeToLive.php new file mode 100644 index 0000000..92e3a02 --- /dev/null +++ b/vendor/Predis/Command/KeyPreciseTimeToLive.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pttl + * @author Daniele Alessandri + */ +class KeyPreciseTimeToLive extends KeyTimeToLive +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PTTL'; + } +} diff --git a/vendor/Predis/Command/KeyRandom.php b/vendor/Predis/Command/KeyRandom.php new file mode 100644 index 0000000..520a368 --- /dev/null +++ b/vendor/Predis/Command/KeyRandom.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/randomkey + * @author Daniele Alessandri + */ +class KeyRandom extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RANDOMKEY'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data !== '' ? $data : null; + } +} diff --git a/vendor/Predis/Command/KeyRename.php b/vendor/Predis/Command/KeyRename.php new file mode 100644 index 0000000..49af1ff --- /dev/null +++ b/vendor/Predis/Command/KeyRename.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rename + * @author Daniele Alessandri + */ +class KeyRename extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RENAME'; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::all($this, $prefix); + } +} diff --git a/vendor/Predis/Command/KeyRenamePreserve.php b/vendor/Predis/Command/KeyRenamePreserve.php new file mode 100644 index 0000000..1db8b7d --- /dev/null +++ b/vendor/Predis/Command/KeyRenamePreserve.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/renamenx + * @author Daniele Alessandri + */ +class KeyRenamePreserve extends KeyRename +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RENAMENX'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/KeyRestore.php b/vendor/Predis/Command/KeyRestore.php new file mode 100644 index 0000000..623cef6 --- /dev/null +++ b/vendor/Predis/Command/KeyRestore.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/restore + * @author Daniele Alessandri + */ +class KeyRestore extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RESTORE'; + } +} diff --git a/vendor/Predis/Command/KeySort.php b/vendor/Predis/Command/KeySort.php new file mode 100644 index 0000000..549469a --- /dev/null +++ b/vendor/Predis/Command/KeySort.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sort + * @author Daniele Alessandri + */ +class KeySort extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SORT'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (count($arguments) === 1) { + return $arguments; + } + + $query = array($arguments[0]); + $sortParams = array_change_key_case($arguments[1], CASE_UPPER); + + if (isset($sortParams['BY'])) { + $query[] = 'BY'; + $query[] = $sortParams['BY']; + } + + if (isset($sortParams['GET'])) { + $getargs = $sortParams['GET']; + + if (is_array($getargs)) { + foreach ($getargs as $getarg) { + $query[] = 'GET'; + $query[] = $getarg; + } + } else { + $query[] = 'GET'; + $query[] = $getargs; + } + } + + if (isset($sortParams['LIMIT']) && + is_array($sortParams['LIMIT']) && + count($sortParams['LIMIT']) == 2) { + + $query[] = 'LIMIT'; + $query[] = $sortParams['LIMIT'][0]; + $query[] = $sortParams['LIMIT'][1]; + } + + if (isset($sortParams['SORT'])) { + $query[] = strtoupper($sortParams['SORT']); + } + + if (isset($sortParams['ALPHA']) && $sortParams['ALPHA'] == true) { + $query[] = 'ALPHA'; + } + + if (isset($sortParams['STORE'])) { + $query[] = 'STORE'; + $query[] = $sortParams['STORE']; + } + + return $query; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + if ($arguments = $this->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + + if (($count = count($arguments)) > 1) { + for ($i = 1; $i < $count; $i++) { + switch ($arguments[$i]) { + case 'BY': + case 'STORE': + $arguments[$i] = "$prefix{$arguments[++$i]}"; + break; + + case 'GET': + $value = $arguments[++$i]; + if ($value !== '#') { + $arguments[$i] = "$prefix$value"; + } + break; + + case 'LIMIT'; + $i += 2; + break; + } + } + } + + $this->setRawArguments($arguments); + } + } +} diff --git a/vendor/Predis/Command/KeyTimeToLive.php b/vendor/Predis/Command/KeyTimeToLive.php new file mode 100644 index 0000000..103885a --- /dev/null +++ b/vendor/Predis/Command/KeyTimeToLive.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/ttl + * @author Daniele Alessandri + */ +class KeyTimeToLive extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'TTL'; + } +} diff --git a/vendor/Predis/Command/KeyType.php b/vendor/Predis/Command/KeyType.php new file mode 100644 index 0000000..b2baf9b --- /dev/null +++ b/vendor/Predis/Command/KeyType.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/type + * @author Daniele Alessandri + */ +class KeyType extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'TYPE'; + } +} diff --git a/vendor/Predis/Command/ListIndex.php b/vendor/Predis/Command/ListIndex.php new file mode 100644 index 0000000..9762ebf --- /dev/null +++ b/vendor/Predis/Command/ListIndex.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lindex + * @author Daniele Alessandri + */ +class ListIndex extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LINDEX'; + } +} diff --git a/vendor/Predis/Command/ListInsert.php b/vendor/Predis/Command/ListInsert.php new file mode 100644 index 0000000..289f9b4 --- /dev/null +++ b/vendor/Predis/Command/ListInsert.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/linsert + * @author Daniele Alessandri + */ +class ListInsert extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LINSERT'; + } +} diff --git a/vendor/Predis/Command/ListLength.php b/vendor/Predis/Command/ListLength.php new file mode 100644 index 0000000..c46dad4 --- /dev/null +++ b/vendor/Predis/Command/ListLength.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/llen + * @author Daniele Alessandri + */ +class ListLength extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LLEN'; + } +} diff --git a/vendor/Predis/Command/ListPopFirst.php b/vendor/Predis/Command/ListPopFirst.php new file mode 100644 index 0000000..f1afc4e --- /dev/null +++ b/vendor/Predis/Command/ListPopFirst.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lpop + * @author Daniele Alessandri + */ +class ListPopFirst extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LPOP'; + } +} diff --git a/vendor/Predis/Command/ListPopFirstBlocking.php b/vendor/Predis/Command/ListPopFirstBlocking.php new file mode 100644 index 0000000..0ada467 --- /dev/null +++ b/vendor/Predis/Command/ListPopFirstBlocking.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/blpop + * @author Daniele Alessandri + */ +class ListPopFirstBlocking extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BLPOP'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[0])) { + list($arguments, $timeout) = $arguments; + array_push($arguments, $timeout); + } + + return $arguments; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::skipLast($this, $prefix); + } +} diff --git a/vendor/Predis/Command/ListPopLast.php b/vendor/Predis/Command/ListPopLast.php new file mode 100644 index 0000000..a161f90 --- /dev/null +++ b/vendor/Predis/Command/ListPopLast.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rpop + * @author Daniele Alessandri + */ +class ListPopLast extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPOP'; + } +} diff --git a/vendor/Predis/Command/ListPopLastBlocking.php b/vendor/Predis/Command/ListPopLastBlocking.php new file mode 100644 index 0000000..fc3d421 --- /dev/null +++ b/vendor/Predis/Command/ListPopLastBlocking.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/brpop + * @author Daniele Alessandri + */ +class ListPopLastBlocking extends ListPopFirstBlocking +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BRPOP'; + } +} diff --git a/vendor/Predis/Command/ListPopLastPushHead.php b/vendor/Predis/Command/ListPopLastPushHead.php new file mode 100644 index 0000000..761b612 --- /dev/null +++ b/vendor/Predis/Command/ListPopLastPushHead.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rpoplpush + * @author Daniele Alessandri + */ +class ListPopLastPushHead extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPOPLPUSH'; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::all($this, $prefix); + } +} diff --git a/vendor/Predis/Command/ListPopLastPushHeadBlocking.php b/vendor/Predis/Command/ListPopLastPushHeadBlocking.php new file mode 100644 index 0000000..3ef1407 --- /dev/null +++ b/vendor/Predis/Command/ListPopLastPushHeadBlocking.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/brpoplpush + * @author Daniele Alessandri + */ +class ListPopLastPushHeadBlocking extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BRPOPLPUSH'; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::skipLast($this, $prefix); + } +} diff --git a/vendor/Predis/Command/ListPushHead.php b/vendor/Predis/Command/ListPushHead.php new file mode 100644 index 0000000..6dcb61b --- /dev/null +++ b/vendor/Predis/Command/ListPushHead.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lpush + * @author Daniele Alessandri + */ +class ListPushHead extends ListPushTail +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LPUSH'; + } +} diff --git a/vendor/Predis/Command/ListPushHeadX.php b/vendor/Predis/Command/ListPushHeadX.php new file mode 100644 index 0000000..2cb671e --- /dev/null +++ b/vendor/Predis/Command/ListPushHeadX.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lpushx + * @author Daniele Alessandri + */ +class ListPushHeadX extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LPUSHX'; + } +} diff --git a/vendor/Predis/Command/ListPushTail.php b/vendor/Predis/Command/ListPushTail.php new file mode 100644 index 0000000..6a0f2e0 --- /dev/null +++ b/vendor/Predis/Command/ListPushTail.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rpush + * @author Daniele Alessandri + */ +class ListPushTail extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPUSH'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/Predis/Command/ListPushTailX.php b/vendor/Predis/Command/ListPushTailX.php new file mode 100644 index 0000000..af42a52 --- /dev/null +++ b/vendor/Predis/Command/ListPushTailX.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/rpushx + * @author Daniele Alessandri + */ +class ListPushTailX extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPUSHX'; + } +} diff --git a/vendor/Predis/Command/ListRange.php b/vendor/Predis/Command/ListRange.php new file mode 100644 index 0000000..2b2e945 --- /dev/null +++ b/vendor/Predis/Command/ListRange.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lrange + * @author Daniele Alessandri + */ +class ListRange extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LRANGE'; + } +} diff --git a/vendor/Predis/Command/ListRemove.php b/vendor/Predis/Command/ListRemove.php new file mode 100644 index 0000000..98f8854 --- /dev/null +++ b/vendor/Predis/Command/ListRemove.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lrem + * @author Daniele Alessandri + */ +class ListRemove extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LREM'; + } +} diff --git a/vendor/Predis/Command/ListSet.php b/vendor/Predis/Command/ListSet.php new file mode 100644 index 0000000..0ea1ee7 --- /dev/null +++ b/vendor/Predis/Command/ListSet.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lset + * @author Daniele Alessandri + */ +class ListSet extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LSET'; + } +} diff --git a/vendor/Predis/Command/ListTrim.php b/vendor/Predis/Command/ListTrim.php new file mode 100644 index 0000000..80d73e5 --- /dev/null +++ b/vendor/Predis/Command/ListTrim.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/ltrim + * @author Daniele Alessandri + */ +class ListTrim extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LTRIM'; + } +} diff --git a/vendor/Predis/Command/PrefixHelpers.php b/vendor/Predis/Command/PrefixHelpers.php new file mode 100644 index 0000000..0c37d6f --- /dev/null +++ b/vendor/Predis/Command/PrefixHelpers.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Class that defines a few helpers method for prefixing keys. + * + * @author Daniele Alessandri + */ +class PrefixHelpers +{ + /** + * Applies the specified prefix only the first argument. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function first(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to all the arguments. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function all(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + foreach ($arguments as &$key) { + $key = "$prefix$key"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix only to even arguments in the list. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function interleaved(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $length = count($arguments); + + for ($i = 0; $i < $length; $i += 2) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to all the arguments but the first one. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function skipFirst(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $length = count($arguments); + + for ($i = 1; $i < $length; $i++) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to all the arguments but the last one. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function skipLast(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $length = count($arguments); + + for ($i = 0; $i < $length - 1; $i++) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } +} diff --git a/vendor/Predis/Command/PrefixableCommand.php b/vendor/Predis/Command/PrefixableCommand.php new file mode 100644 index 0000000..7112e87 --- /dev/null +++ b/vendor/Predis/Command/PrefixableCommand.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Base class for Redis commands with prefixable keys. + * + * @author Daniele Alessandri + */ +abstract class PrefixableCommand extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + if ($arguments = $this->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + $this->setRawArguments($arguments); + } + } +} diff --git a/vendor/Predis/Command/PrefixableCommandInterface.php b/vendor/Predis/Command/PrefixableCommandInterface.php new file mode 100644 index 0000000..acb99a2 --- /dev/null +++ b/vendor/Predis/Command/PrefixableCommandInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Defines a command whose keys can be prefixed. + * + * @author Daniele Alessandri + */ +interface PrefixableCommandInterface +{ + /** + * Prefixes all the keys found in the arguments of the command. + * + * @param string $prefix String used to prefix the keys. + */ + public function prefixKeys($prefix); +} diff --git a/vendor/Predis/Command/Processor/CommandProcessingInterface.php b/vendor/Predis/Command/Processor/CommandProcessingInterface.php new file mode 100644 index 0000000..376d465 --- /dev/null +++ b/vendor/Predis/Command/Processor/CommandProcessingInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command\Processor; + +/** + * Defines an object that can process commands using command processors. + * + * @author Daniele Alessandri + */ +interface CommandProcessingInterface +{ + /** + * Associates a command processor. + * + * @param CommandProcessorInterface $processor The command processor. + */ + public function setProcessor(CommandProcessorInterface $processor); + + /** + * Returns the associated command processor. + * + * @return CommandProcessorInterface + */ + public function getProcessor(); +} diff --git a/vendor/Predis/Command/Processor/CommandProcessorChainInterface.php b/vendor/Predis/Command/Processor/CommandProcessorChainInterface.php new file mode 100644 index 0000000..2985ac9 --- /dev/null +++ b/vendor/Predis/Command/Processor/CommandProcessorChainInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command\Processor; + +/** + * A command processor chain processes a command using multiple chained command + * processor before it is sent to Redis. + * + * @author Daniele Alessandri + */ +interface CommandProcessorChainInterface extends CommandProcessorInterface, \IteratorAggregate, \Countable +{ + /** + * Adds a command processor. + * + * @param CommandProcessorInterface $processor A command processor. + */ + public function add(CommandProcessorInterface $processor); + + /** + * Removes a command processor from the chain. + * + * @param CommandProcessorInterface $processor A command processor. + */ + public function remove(CommandProcessorInterface $processor); + + /** + * Returns an ordered list of the command processors in the chain. + * + * @return array + */ + public function getProcessors(); +} diff --git a/vendor/Predis/Command/Processor/CommandProcessorInterface.php b/vendor/Predis/Command/Processor/CommandProcessorInterface.php new file mode 100644 index 0000000..e23eade --- /dev/null +++ b/vendor/Predis/Command/Processor/CommandProcessorInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command\Processor; + +use Predis\Command\CommandInterface; + +/** + * A command processor processes commands before they are sent to Redis. + * + * @author Daniele Alessandri + */ +interface CommandProcessorInterface +{ + /** + * Processes a Redis command. + * + * @param CommandInterface $command Redis command. + */ + public function process(CommandInterface $command); +} diff --git a/vendor/Predis/Command/Processor/KeyPrefixProcessor.php b/vendor/Predis/Command/Processor/KeyPrefixProcessor.php new file mode 100644 index 0000000..ed0d17f --- /dev/null +++ b/vendor/Predis/Command/Processor/KeyPrefixProcessor.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command\Processor; + +use Predis\Command\CommandInterface; +use Predis\Command\PrefixableCommandInterface; + +/** + * Command processor that is used to prefix the keys contained in the arguments + * of a Redis command. + * + * @author Daniele Alessandri + */ +class KeyPrefixProcessor implements CommandProcessorInterface +{ + private $prefix; + + /** + * @param string $prefix Prefix for the keys. + */ + public function __construct($prefix) + { + $this->setPrefix($prefix); + } + + /** + * Sets a prefix that is applied to all the keys. + * + * @param string $prefix Prefix for the keys. + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Gets the current prefix. + * + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + /** + * {@inheritdoc} + */ + public function process(CommandInterface $command) + { + if ($command instanceof PrefixableCommandInterface && $command->getArguments()) { + $command->prefixKeys($this->prefix); + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->getPrefix(); + } +} diff --git a/vendor/Predis/Command/Processor/ProcessorChain.php b/vendor/Predis/Command/Processor/ProcessorChain.php new file mode 100644 index 0000000..fb4b109 --- /dev/null +++ b/vendor/Predis/Command/Processor/ProcessorChain.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command\Processor; + +use Predis\Command\CommandInterface; + +/** + * Default implementation of a command processors chain. + * + * @author Daniele Alessandri + */ +class ProcessorChain implements CommandProcessorChainInterface, \ArrayAccess +{ + private $processors = array(); + + /** + * @param array $processors List of instances of CommandProcessorInterface. + */ + public function __construct($processors = array()) + { + foreach ($processors as $processor) { + $this->add($processor); + } + } + + /** + * {@inheritdoc} + */ + public function add(CommandProcessorInterface $processor) + { + $this->processors[] = $processor; + } + + /** + * {@inheritdoc} + */ + public function remove(CommandProcessorInterface $processor) + { + if (false !== $index = array_search($processor, $this->processors, true)) { + unset($this[$index]); + } + } + + /** + * {@inheritdoc} + */ + public function process(CommandInterface $command) + { + for ($i = 0; $i < $count = count($this->processors); $i++) { + $this->processors[$i]->process($command); + } + } + + /** + * {@inheritdoc} + */ + public function getProcessors() + { + return $this->processors; + } + + /** + * Returns an iterator over the list of command processor in the chain. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->processors); + } + + /** + * Returns the number of command processors in the chain. + * + * @return int + */ + public function count() + { + return count($this->processors); + } + + /** + * {@inheritdoc} + */ + public function offsetExists($index) + { + return isset($this->processors[$index]); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($index) + { + return $this->processors[$index]; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($index, $processor) + { + if (!$processor instanceof CommandProcessorInterface) { + throw new \InvalidArgumentException( + 'A processor chain can hold only instances of classes implementing '. + 'the Predis\Command\Processor\CommandProcessorInterface interface' + ); + } + + $this->processors[$index] = $processor; + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($index) + { + unset($this->processors[$index]); + $this->processors = array_values($this->processors); + } +} diff --git a/vendor/Predis/Command/PubSubPublish.php b/vendor/Predis/Command/PubSubPublish.php new file mode 100644 index 0000000..b2ecce5 --- /dev/null +++ b/vendor/Predis/Command/PubSubPublish.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/publish + * @author Daniele Alessandri + */ +class PubSubPublish extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PUBLISH'; + } +} diff --git a/vendor/Predis/Command/PubSubSubscribe.php b/vendor/Predis/Command/PubSubSubscribe.php new file mode 100644 index 0000000..eb8be65 --- /dev/null +++ b/vendor/Predis/Command/PubSubSubscribe.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/subscribe + * @author Daniele Alessandri + */ +class PubSubSubscribe extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUBSCRIBE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeArguments($arguments); + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::all($this, $prefix); + } +} diff --git a/vendor/Predis/Command/PubSubSubscribeByPattern.php b/vendor/Predis/Command/PubSubSubscribeByPattern.php new file mode 100644 index 0000000..7629e6d --- /dev/null +++ b/vendor/Predis/Command/PubSubSubscribeByPattern.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/psubscribe + * @author Daniele Alessandri + */ +class PubSubSubscribeByPattern extends PubSubSubscribe +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PSUBSCRIBE'; + } +} diff --git a/vendor/Predis/Command/PubSubUnsubscribe.php b/vendor/Predis/Command/PubSubUnsubscribe.php new file mode 100644 index 0000000..ba70fe4 --- /dev/null +++ b/vendor/Predis/Command/PubSubUnsubscribe.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/unsubscribe + * @author Daniele Alessandri + */ +class PubSubUnsubscribe extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'UNSUBSCRIBE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeArguments($arguments); + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::all($this, $prefix); + } +} diff --git a/vendor/Predis/Command/PubSubUnsubscribeByPattern.php b/vendor/Predis/Command/PubSubUnsubscribeByPattern.php new file mode 100644 index 0000000..f846e65 --- /dev/null +++ b/vendor/Predis/Command/PubSubUnsubscribeByPattern.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/punsubscribe + * @author Daniele Alessandri + */ +class PubSubUnsubscribeByPattern extends PubSubUnsubscribe +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PUNSUBSCRIBE'; + } +} diff --git a/vendor/Predis/Command/ScriptedCommand.php b/vendor/Predis/Command/ScriptedCommand.php new file mode 100644 index 0000000..9c0065c --- /dev/null +++ b/vendor/Predis/Command/ScriptedCommand.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * Base class used to implement an higher level abstraction for "virtual" + * commands based on EVAL. + * + * @link http://redis.io/commands/eval + * @author Daniele Alessandri + */ +abstract class ScriptedCommand extends ServerEvalSHA +{ + /** + * Gets the body of a Lua script. + * + * @return string + */ + public abstract function getScript(); + + /** + * Specifies the number of arguments that should be considered as keys. + * + * The default behaviour for the base class is to return 0 to indicate that + * all the elements of the arguments array should be considered as keys, but + * subclasses can enforce a static number of keys. + * + * @return int + */ + protected function getKeysCount() + { + return 0; + } + + /** + * Returns the elements from the arguments that are identified as keys. + * + * @return array + */ + public function getKeys() + { + return array_slice($this->getArguments(), 2, $this->getKeysCount()); + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (($numkeys = $this->getKeysCount()) && $numkeys < 0) { + $numkeys = count($arguments) + $numkeys; + } + + return array_merge(array(sha1($this->getScript()), (int) $numkeys), $arguments); + } + + /** + * @return array + */ + public function getEvalArguments() + { + $arguments = $this->getArguments(); + $arguments[0] = $this->getScript(); + + return $arguments; + } +} diff --git a/vendor/Predis/Command/ServerBackgroundRewriteAOF.php b/vendor/Predis/Command/ServerBackgroundRewriteAOF.php new file mode 100644 index 0000000..c4b4007 --- /dev/null +++ b/vendor/Predis/Command/ServerBackgroundRewriteAOF.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bgrewriteaof + * @author Daniele Alessandri + */ +class ServerBackgroundRewriteAOF extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BGREWRITEAOF'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data == 'Background append only file rewriting started'; + } +} diff --git a/vendor/Predis/Command/ServerBackgroundSave.php b/vendor/Predis/Command/ServerBackgroundSave.php new file mode 100644 index 0000000..5993201 --- /dev/null +++ b/vendor/Predis/Command/ServerBackgroundSave.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bgsave + * @author Daniele Alessandri + */ +class ServerBackgroundSave extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BGSAVE'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data === 'Background saving started' ? true : $data; + } +} diff --git a/vendor/Predis/Command/ServerClient.php b/vendor/Predis/Command/ServerClient.php new file mode 100644 index 0000000..d020f67 --- /dev/null +++ b/vendor/Predis/Command/ServerClient.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/client + * @author Daniele Alessandri + */ +class ServerClient extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'CLIENT'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $args = array_change_key_case($this->getArguments(), CASE_UPPER); + + switch (strtoupper($args[0])) { + case 'LIST': + return $this->parseClientList($data); + case 'KILL': + case 'GETNAME': + case 'SETNAME': + default: + return $data; + } + } + + /** + * Parses the reply buffer and returns the list of clients returned by + * the CLIENT LIST command. + * + * @param string $data Reply buffer + * @return array + */ + protected function parseClientList($data) + { + $clients = array(); + + foreach (explode("\n", $data, -1) as $clientData) { + $client = array(); + + foreach (explode(' ', $clientData) as $kv) { + @list($k, $v) = explode('=', $kv); + $client[$k] = $v; + } + + $clients[] = $client; + } + + return $clients; + } +} diff --git a/vendor/Predis/Command/ServerConfig.php b/vendor/Predis/Command/ServerConfig.php new file mode 100644 index 0000000..251efdd --- /dev/null +++ b/vendor/Predis/Command/ServerConfig.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/config-set + * @link http://redis.io/commands/config-get + * @link http://redis.io/commands/config-resetstat + * @author Daniele Alessandri + */ +class ServerConfig extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'CONFIG'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $result = array(); + + for ($i = 0; $i < count($data); $i++) { + $result[$data[$i]] = $data[++$i]; + } + + return $result; + } + + return $data; + } +} diff --git a/vendor/Predis/Command/ServerDatabaseSize.php b/vendor/Predis/Command/ServerDatabaseSize.php new file mode 100644 index 0000000..51dcac6 --- /dev/null +++ b/vendor/Predis/Command/ServerDatabaseSize.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/dbsize + * @author Daniele Alessandri + */ +class ServerDatabaseSize extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DBSIZE'; + } +} diff --git a/vendor/Predis/Command/ServerEval.php b/vendor/Predis/Command/ServerEval.php new file mode 100644 index 0000000..0c1f182 --- /dev/null +++ b/vendor/Predis/Command/ServerEval.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/eval + * @author Daniele Alessandri + */ +class ServerEval extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EVAL'; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + if ($arguments = $this->getArguments()) { + for ($i = 2; $i < $arguments[1] + 2; $i++) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $this->setRawArguments($arguments); + } + } + + /** + * Calculates the SHA1 hash of the body of the script. + * + * @return string SHA1 hash. + */ + public function getScriptHash() + { + return sha1($this->getArgument(0)); + } +} diff --git a/vendor/Predis/Command/ServerEvalSHA.php b/vendor/Predis/Command/ServerEvalSHA.php new file mode 100644 index 0000000..a670390 --- /dev/null +++ b/vendor/Predis/Command/ServerEvalSHA.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/evalsha + * @author Daniele Alessandri + */ +class ServerEvalSHA extends ServerEval +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EVALSHA'; + } + + /** + * Returns the SHA1 hash of the body of the script. + * + * @return string SHA1 hash. + */ + public function getScriptHash() + { + return $this->getArgument(0); + } +} diff --git a/vendor/Predis/Command/ServerFlushAll.php b/vendor/Predis/Command/ServerFlushAll.php new file mode 100644 index 0000000..a50218c --- /dev/null +++ b/vendor/Predis/Command/ServerFlushAll.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/flushall + * @author Daniele Alessandri + */ +class ServerFlushAll extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'FLUSHALL'; + } +} diff --git a/vendor/Predis/Command/ServerFlushDatabase.php b/vendor/Predis/Command/ServerFlushDatabase.php new file mode 100644 index 0000000..8b6b6a2 --- /dev/null +++ b/vendor/Predis/Command/ServerFlushDatabase.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/flushdb + * @author Daniele Alessandri + */ +class ServerFlushDatabase extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'FLUSHDB'; + } +} diff --git a/vendor/Predis/Command/ServerInfo.php b/vendor/Predis/Command/ServerInfo.php new file mode 100644 index 0000000..6dd351f --- /dev/null +++ b/vendor/Predis/Command/ServerInfo.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/info + * @author Daniele Alessandri + */ +class ServerInfo extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INFO'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $info = array(); + $infoLines = preg_split('/\r?\n/', $data); + + foreach ($infoLines as $row) { + @list($k, $v) = explode(':', $row); + + if ($row === '' || !isset($v)) { + continue; + } + + if (!preg_match('/^db\d+$/', $k)) { + if ($k === 'allocation_stats') { + $info[$k] = $this->parseAllocationStats($v); + continue; + } + + $info[$k] = $v; + } else { + $info[$k] = $this->parseDatabaseStats($v); + } + } + + return $info; + } + + /** + * Parses the reply buffer and extracts the statistics of each logical DB. + * + * @param string $str Reply buffer. + * @return array + */ + protected function parseDatabaseStats($str) + { + $db = array(); + + foreach (explode(',', $str) as $dbvar) { + list($dbvk, $dbvv) = explode('=', $dbvar); + $db[trim($dbvk)] = $dbvv; + } + + return $db; + } + + /** + * Parses the reply buffer and extracts the allocation statistics. + * + * @param string $str Reply buffer. + * @return array + */ + protected function parseAllocationStats($str) + { + $stats = array(); + + foreach (explode(',', $str) as $kv) { + @list($size, $objects, $extra) = explode('=', $kv); + + // hack to prevent incorrect values when parsing the >=256 key + if (isset($extra)) { + $size = ">=$objects"; + $objects = $extra; + } + + $stats[$size] = $objects; + } + + return $stats; + } +} diff --git a/vendor/Predis/Command/ServerInfoV26x.php b/vendor/Predis/Command/ServerInfoV26x.php new file mode 100644 index 0000000..457fe30 --- /dev/null +++ b/vendor/Predis/Command/ServerInfoV26x.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/info + * @author Daniele Alessandri + */ +class ServerInfoV26x extends ServerInfo +{ + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $info = array(); + $current = null; + $infoLines = preg_split('/\r?\n/', $data); + + if (isset($infoLines[0]) && $infoLines[0][0] !== '#') { + return parent::parseResponse($data); + } + + foreach ($infoLines as $row) { + if ($row === '') { + continue; + } + + if (preg_match('/^# (\w+)$/', $row, $matches)) { + $info[$matches[1]] = array(); + $current = &$info[$matches[1]]; + continue; + } + + list($k, $v) = explode(':', $row); + + if (!preg_match('/^db\d+$/', $k)) { + if ($k === 'allocation_stats') { + $current[$k] = $this->parseAllocationStats($v); + continue; + } + + $current[$k] = $v; + } else { + $current[$k] = $this->parseDatabaseStats($v); + } + } + + return $info; + } +} diff --git a/vendor/Predis/Command/ServerLastSave.php b/vendor/Predis/Command/ServerLastSave.php new file mode 100644 index 0000000..33a1e93 --- /dev/null +++ b/vendor/Predis/Command/ServerLastSave.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/lastsave + * @author Daniele Alessandri + */ +class ServerLastSave extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LASTSAVE'; + } +} diff --git a/vendor/Predis/Command/ServerMonitor.php b/vendor/Predis/Command/ServerMonitor.php new file mode 100644 index 0000000..7f4a83f --- /dev/null +++ b/vendor/Predis/Command/ServerMonitor.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/monitor + * @author Daniele Alessandri + */ +class ServerMonitor extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MONITOR'; + } +} diff --git a/vendor/Predis/Command/ServerObject.php b/vendor/Predis/Command/ServerObject.php new file mode 100644 index 0000000..989fbd7 --- /dev/null +++ b/vendor/Predis/Command/ServerObject.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/object + * @author Daniele Alessandri + */ +class ServerObject extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'OBJECT'; + } +} diff --git a/vendor/Predis/Command/ServerSave.php b/vendor/Predis/Command/ServerSave.php new file mode 100644 index 0000000..6dd3c79 --- /dev/null +++ b/vendor/Predis/Command/ServerSave.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/save + * @author Daniele Alessandri + */ +class ServerSave extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SAVE'; + } +} diff --git a/vendor/Predis/Command/ServerScript.php b/vendor/Predis/Command/ServerScript.php new file mode 100644 index 0000000..5b66866 --- /dev/null +++ b/vendor/Predis/Command/ServerScript.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/script + * @author Daniele Alessandri + */ +class ServerScript extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SCRIPT'; + } +} diff --git a/vendor/Predis/Command/ServerShutdown.php b/vendor/Predis/Command/ServerShutdown.php new file mode 100644 index 0000000..b0278b0 --- /dev/null +++ b/vendor/Predis/Command/ServerShutdown.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/shutdown + * @author Daniele Alessandri + */ +class ServerShutdown extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SHUTDOWN'; + } +} diff --git a/vendor/Predis/Command/ServerSlaveOf.php b/vendor/Predis/Command/ServerSlaveOf.php new file mode 100644 index 0000000..94f9f69 --- /dev/null +++ b/vendor/Predis/Command/ServerSlaveOf.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/slaveof + * @author Daniele Alessandri + */ +class ServerSlaveOf extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SLAVEOF'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (count($arguments) === 0 || $arguments[0] === 'NO ONE') { + return array('NO', 'ONE'); + } + + return $arguments; + } +} diff --git a/vendor/Predis/Command/ServerSlowlog.php b/vendor/Predis/Command/ServerSlowlog.php new file mode 100644 index 0000000..ad4155f --- /dev/null +++ b/vendor/Predis/Command/ServerSlowlog.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/slowlog + * @author Daniele Alessandri + */ +class ServerSlowlog extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SLOWLOG'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $log = array(); + + foreach ($data as $index => $entry) { + $log[$index] = array( + 'id' => $entry[0], + 'timestamp' => $entry[1], + 'duration' => $entry[2], + 'command' => $entry[3], + ); + } + + return $log; + } + + return $data; + } +} diff --git a/vendor/Predis/Command/ServerTime.php b/vendor/Predis/Command/ServerTime.php new file mode 100644 index 0000000..1797637 --- /dev/null +++ b/vendor/Predis/Command/ServerTime.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/time + * @author Daniele Alessandri + */ +class ServerTime extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'TIME'; + } +} diff --git a/vendor/Predis/Command/SetAdd.php b/vendor/Predis/Command/SetAdd.php new file mode 100644 index 0000000..f03e02b --- /dev/null +++ b/vendor/Predis/Command/SetAdd.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sadd + * @author Daniele Alessandri + */ +class SetAdd extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/Predis/Command/SetCardinality.php b/vendor/Predis/Command/SetCardinality.php new file mode 100644 index 0000000..8e6e48e --- /dev/null +++ b/vendor/Predis/Command/SetCardinality.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/scard + * @author Daniele Alessandri + */ +class SetCardinality extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SCARD'; + } +} diff --git a/vendor/Predis/Command/SetDifference.php b/vendor/Predis/Command/SetDifference.php new file mode 100644 index 0000000..14d3f21 --- /dev/null +++ b/vendor/Predis/Command/SetDifference.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sdiff + * @author Daniele Alessandri + */ +class SetDifference extends SetIntersection +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SDIFF'; + } +} diff --git a/vendor/Predis/Command/SetDifferenceStore.php b/vendor/Predis/Command/SetDifferenceStore.php new file mode 100644 index 0000000..1fae8b7 --- /dev/null +++ b/vendor/Predis/Command/SetDifferenceStore.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sdiffstore + * @author Daniele Alessandri + */ +class SetDifferenceStore extends SetIntersectionStore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SDIFFSTORE'; + } +} diff --git a/vendor/Predis/Command/SetIntersection.php b/vendor/Predis/Command/SetIntersection.php new file mode 100644 index 0000000..c6590d2 --- /dev/null +++ b/vendor/Predis/Command/SetIntersection.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sinter + * @author Daniele Alessandri + */ +class SetIntersection extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SINTER'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeArguments($arguments); + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::all($this, $prefix); + } +} diff --git a/vendor/Predis/Command/SetIntersectionStore.php b/vendor/Predis/Command/SetIntersectionStore.php new file mode 100644 index 0000000..4e764e0 --- /dev/null +++ b/vendor/Predis/Command/SetIntersectionStore.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sinterstore + * @author Daniele Alessandri + */ +class SetIntersectionStore extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SINTERSTORE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + return array_merge(array($arguments[0]), $arguments[1]); + } + + return $arguments; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::all($this, $prefix); + } +} diff --git a/vendor/Predis/Command/SetIsMember.php b/vendor/Predis/Command/SetIsMember.php new file mode 100644 index 0000000..0773520 --- /dev/null +++ b/vendor/Predis/Command/SetIsMember.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sismember + * @author Daniele Alessandri + */ +class SetIsMember extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SISMEMBER'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/SetMembers.php b/vendor/Predis/Command/SetMembers.php new file mode 100644 index 0000000..3ebef0a --- /dev/null +++ b/vendor/Predis/Command/SetMembers.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/smembers + * @author Daniele Alessandri + */ +class SetMembers extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SMEMBERS'; + } +} diff --git a/vendor/Predis/Command/SetMove.php b/vendor/Predis/Command/SetMove.php new file mode 100644 index 0000000..01678ec --- /dev/null +++ b/vendor/Predis/Command/SetMove.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/smove + * @author Daniele Alessandri + */ +class SetMove extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SMOVE'; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::skipLast($this, $prefix); + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/SetPop.php b/vendor/Predis/Command/SetPop.php new file mode 100644 index 0000000..ebfec0c --- /dev/null +++ b/vendor/Predis/Command/SetPop.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/spop + * @author Daniele Alessandri + */ +class SetPop extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SPOP'; + } +} diff --git a/vendor/Predis/Command/SetRandomMember.php b/vendor/Predis/Command/SetRandomMember.php new file mode 100644 index 0000000..a6b6062 --- /dev/null +++ b/vendor/Predis/Command/SetRandomMember.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/srandmember + * @author Daniele Alessandri + */ +class SetRandomMember extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SRANDMEMBER'; + } +} diff --git a/vendor/Predis/Command/SetRemove.php b/vendor/Predis/Command/SetRemove.php new file mode 100644 index 0000000..f7cb577 --- /dev/null +++ b/vendor/Predis/Command/SetRemove.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/srem + * @author Daniele Alessandri + */ +class SetRemove extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SREM'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/Predis/Command/SetUnion.php b/vendor/Predis/Command/SetUnion.php new file mode 100644 index 0000000..c9f322b --- /dev/null +++ b/vendor/Predis/Command/SetUnion.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sunion + * @author Daniele Alessandri + */ +class SetUnion extends SetIntersection +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUNION'; + } +} diff --git a/vendor/Predis/Command/SetUnionStore.php b/vendor/Predis/Command/SetUnionStore.php new file mode 100644 index 0000000..daf66c3 --- /dev/null +++ b/vendor/Predis/Command/SetUnionStore.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/sunionstore + * @author Daniele Alessandri + */ +class SetUnionStore extends SetIntersectionStore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUNIONSTORE'; + } +} diff --git a/vendor/Predis/Command/StringAppend.php b/vendor/Predis/Command/StringAppend.php new file mode 100644 index 0000000..8d14614 --- /dev/null +++ b/vendor/Predis/Command/StringAppend.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/append + * @author Daniele Alessandri + */ +class StringAppend extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'APPEND'; + } +} diff --git a/vendor/Predis/Command/StringBitCount.php b/vendor/Predis/Command/StringBitCount.php new file mode 100644 index 0000000..19d534b --- /dev/null +++ b/vendor/Predis/Command/StringBitCount.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bitcount + * @author Daniele Alessandri + */ +class StringBitCount extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITCOUNT'; + } +} diff --git a/vendor/Predis/Command/StringBitOp.php b/vendor/Predis/Command/StringBitOp.php new file mode 100644 index 0000000..0236bb1 --- /dev/null +++ b/vendor/Predis/Command/StringBitOp.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/bitop + * @author Daniele Alessandri + */ +class StringBitOp extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITOP'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (count($arguments) === 3 && is_array($arguments[2])) { + list($operation, $destination, ) = $arguments; + $arguments = $arguments[2]; + array_unshift($arguments, $operation, $destination); + } + + return $arguments; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::skipFirst($this, $prefix); + } +} diff --git a/vendor/Predis/Command/StringDecrement.php b/vendor/Predis/Command/StringDecrement.php new file mode 100644 index 0000000..0e45cd0 --- /dev/null +++ b/vendor/Predis/Command/StringDecrement.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/decr + * @author Daniele Alessandri + */ +class StringDecrement extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DECR'; + } +} diff --git a/vendor/Predis/Command/StringDecrementBy.php b/vendor/Predis/Command/StringDecrementBy.php new file mode 100644 index 0000000..e716e0e --- /dev/null +++ b/vendor/Predis/Command/StringDecrementBy.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/decrby + * @author Daniele Alessandri + */ +class StringDecrementBy extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DECRBY'; + } +} diff --git a/vendor/Predis/Command/StringGet.php b/vendor/Predis/Command/StringGet.php new file mode 100644 index 0000000..851c710 --- /dev/null +++ b/vendor/Predis/Command/StringGet.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/get + * @author Daniele Alessandri + */ +class StringGet extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GET'; + } +} diff --git a/vendor/Predis/Command/StringGetBit.php b/vendor/Predis/Command/StringGetBit.php new file mode 100644 index 0000000..954c39f --- /dev/null +++ b/vendor/Predis/Command/StringGetBit.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/getbit + * @author Daniele Alessandri + */ +class StringGetBit extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GETBIT'; + } +} diff --git a/vendor/Predis/Command/StringGetMultiple.php b/vendor/Predis/Command/StringGetMultiple.php new file mode 100644 index 0000000..71d9187 --- /dev/null +++ b/vendor/Predis/Command/StringGetMultiple.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/mget + * @author Daniele Alessandri + */ +class StringGetMultiple extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MGET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeArguments($arguments); + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::all($this, $prefix); + } +} diff --git a/vendor/Predis/Command/StringGetRange.php b/vendor/Predis/Command/StringGetRange.php new file mode 100644 index 0000000..aed4853 --- /dev/null +++ b/vendor/Predis/Command/StringGetRange.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/getrange + * @author Daniele Alessandri + */ +class StringGetRange extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GETRANGE'; + } +} diff --git a/vendor/Predis/Command/StringGetSet.php b/vendor/Predis/Command/StringGetSet.php new file mode 100644 index 0000000..d4b8e23 --- /dev/null +++ b/vendor/Predis/Command/StringGetSet.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/getset + * @author Daniele Alessandri + */ +class StringGetSet extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GETSET'; + } +} diff --git a/vendor/Predis/Command/StringIncrement.php b/vendor/Predis/Command/StringIncrement.php new file mode 100644 index 0000000..4848afc --- /dev/null +++ b/vendor/Predis/Command/StringIncrement.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/incr + * @author Daniele Alessandri + */ +class StringIncrement extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INCR'; + } +} diff --git a/vendor/Predis/Command/StringIncrementBy.php b/vendor/Predis/Command/StringIncrementBy.php new file mode 100644 index 0000000..7fa3188 --- /dev/null +++ b/vendor/Predis/Command/StringIncrementBy.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/incrby + * @author Daniele Alessandri + */ +class StringIncrementBy extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INCRBY'; + } +} diff --git a/vendor/Predis/Command/StringIncrementByFloat.php b/vendor/Predis/Command/StringIncrementByFloat.php new file mode 100644 index 0000000..7e14dff --- /dev/null +++ b/vendor/Predis/Command/StringIncrementByFloat.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/incrbyfloat + * @author Daniele Alessandri + */ +class StringIncrementByFloat extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INCRBYFLOAT'; + } +} diff --git a/vendor/Predis/Command/StringPreciseSetExpire.php b/vendor/Predis/Command/StringPreciseSetExpire.php new file mode 100644 index 0000000..da2cbad --- /dev/null +++ b/vendor/Predis/Command/StringPreciseSetExpire.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/psetex + * @author Daniele Alessandri + */ +class StringPreciseSetExpire extends StringSetExpire +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PSETEX'; + } +} diff --git a/vendor/Predis/Command/StringSet.php b/vendor/Predis/Command/StringSet.php new file mode 100644 index 0000000..eb3739b --- /dev/null +++ b/vendor/Predis/Command/StringSet.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/set + * @author Daniele Alessandri + */ +class StringSet extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SET'; + } +} diff --git a/vendor/Predis/Command/StringSetBit.php b/vendor/Predis/Command/StringSetBit.php new file mode 100644 index 0000000..a92b418 --- /dev/null +++ b/vendor/Predis/Command/StringSetBit.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/setbit + * @author Daniele Alessandri + */ +class StringSetBit extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETBIT'; + } +} diff --git a/vendor/Predis/Command/StringSetExpire.php b/vendor/Predis/Command/StringSetExpire.php new file mode 100644 index 0000000..c235e2b --- /dev/null +++ b/vendor/Predis/Command/StringSetExpire.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/setex + * @author Daniele Alessandri + */ +class StringSetExpire extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETEX'; + } +} diff --git a/vendor/Predis/Command/StringSetMultiple.php b/vendor/Predis/Command/StringSetMultiple.php new file mode 100644 index 0000000..cda5157 --- /dev/null +++ b/vendor/Predis/Command/StringSetMultiple.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/mset + * @author Daniele Alessandri + */ +class StringSetMultiple extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MSET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (count($arguments) === 1 && is_array($arguments[0])) { + $flattenedKVs = array(); + $args = $arguments[0]; + + foreach ($args as $k => $v) { + $flattenedKVs[] = $k; + $flattenedKVs[] = $v; + } + + return $flattenedKVs; + } + + return $arguments; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::interleaved($this, $prefix); + } +} diff --git a/vendor/Predis/Command/StringSetMultiplePreserve.php b/vendor/Predis/Command/StringSetMultiplePreserve.php new file mode 100644 index 0000000..961422d --- /dev/null +++ b/vendor/Predis/Command/StringSetMultiplePreserve.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/msetnx + * @author Daniele Alessandri + */ +class StringSetMultiplePreserve extends StringSetMultiple +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MSETNX'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/StringSetPreserve.php b/vendor/Predis/Command/StringSetPreserve.php new file mode 100644 index 0000000..d6e4a81 --- /dev/null +++ b/vendor/Predis/Command/StringSetPreserve.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/setnx + * @author Daniele Alessandri + */ +class StringSetPreserve extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETNX'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/StringSetRange.php b/vendor/Predis/Command/StringSetRange.php new file mode 100644 index 0000000..da30f32 --- /dev/null +++ b/vendor/Predis/Command/StringSetRange.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/setrange + * @author Daniele Alessandri + */ +class StringSetRange extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETRANGE'; + } +} diff --git a/vendor/Predis/Command/StringStrlen.php b/vendor/Predis/Command/StringStrlen.php new file mode 100644 index 0000000..1b839ad --- /dev/null +++ b/vendor/Predis/Command/StringStrlen.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/strlen + * @author Daniele Alessandri + */ +class StringStrlen extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'STRLEN'; + } +} diff --git a/vendor/Predis/Command/StringSubstr.php b/vendor/Predis/Command/StringSubstr.php new file mode 100644 index 0000000..671fc16 --- /dev/null +++ b/vendor/Predis/Command/StringSubstr.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/substr + * @author Daniele Alessandri + */ +class StringSubstr extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUBSTR'; + } +} diff --git a/vendor/Predis/Command/TransactionDiscard.php b/vendor/Predis/Command/TransactionDiscard.php new file mode 100644 index 0000000..67010b0 --- /dev/null +++ b/vendor/Predis/Command/TransactionDiscard.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/discard + * @author Daniele Alessandri + */ +class TransactionDiscard extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DISCARD'; + } +} diff --git a/vendor/Predis/Command/TransactionExec.php b/vendor/Predis/Command/TransactionExec.php new file mode 100644 index 0000000..5141454 --- /dev/null +++ b/vendor/Predis/Command/TransactionExec.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/exec + * @author Daniele Alessandri + */ +class TransactionExec extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXEC'; + } +} diff --git a/vendor/Predis/Command/TransactionMulti.php b/vendor/Predis/Command/TransactionMulti.php new file mode 100644 index 0000000..8f4ccf1 --- /dev/null +++ b/vendor/Predis/Command/TransactionMulti.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/multi + * @author Daniele Alessandri + */ +class TransactionMulti extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MULTI'; + } +} diff --git a/vendor/Predis/Command/TransactionUnwatch.php b/vendor/Predis/Command/TransactionUnwatch.php new file mode 100644 index 0000000..697e09f --- /dev/null +++ b/vendor/Predis/Command/TransactionUnwatch.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/unwatch + * @author Daniele Alessandri + */ +class TransactionUnwatch extends AbstractCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'UNWATCH'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/TransactionWatch.php b/vendor/Predis/Command/TransactionWatch.php new file mode 100644 index 0000000..a1dd9a2 --- /dev/null +++ b/vendor/Predis/Command/TransactionWatch.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/watch + * @author Daniele Alessandri + */ +class TransactionWatch extends AbstractCommand implements PrefixableCommandInterface +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'WATCH'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (isset($arguments[0]) && is_array($arguments[0])) { + return $arguments[0]; + } + + return $arguments; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + PrefixHelpers::all($this, $prefix); + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/Predis/Command/ZSetAdd.php b/vendor/Predis/Command/ZSetAdd.php new file mode 100644 index 0000000..e0b1c53 --- /dev/null +++ b/vendor/Predis/Command/ZSetAdd.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zadd + * @author Daniele Alessandri + */ +class ZSetAdd extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $flattened = array($arguments[0]); + + foreach($arguments[1] as $member => $score) { + $flattened[] = $score; + $flattened[] = $member; + } + + return $flattened; + } + + return $arguments; + } +} diff --git a/vendor/Predis/Command/ZSetCardinality.php b/vendor/Predis/Command/ZSetCardinality.php new file mode 100644 index 0000000..4e4432e --- /dev/null +++ b/vendor/Predis/Command/ZSetCardinality.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zcard + * @author Daniele Alessandri + */ +class ZSetCardinality extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZCARD'; + } +} diff --git a/vendor/Predis/Command/ZSetCount.php b/vendor/Predis/Command/ZSetCount.php new file mode 100644 index 0000000..2d8aa9c --- /dev/null +++ b/vendor/Predis/Command/ZSetCount.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zcount + * @author Daniele Alessandri + */ +class ZSetCount extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZCOUNT'; + } +} diff --git a/vendor/Predis/Command/ZSetIncrementBy.php b/vendor/Predis/Command/ZSetIncrementBy.php new file mode 100644 index 0000000..a32bfab --- /dev/null +++ b/vendor/Predis/Command/ZSetIncrementBy.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zincrby + * @author Daniele Alessandri + */ +class ZSetIncrementBy extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZINCRBY'; + } +} diff --git a/vendor/Predis/Command/ZSetIntersectionStore.php b/vendor/Predis/Command/ZSetIntersectionStore.php new file mode 100644 index 0000000..9f6b109 --- /dev/null +++ b/vendor/Predis/Command/ZSetIntersectionStore.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zinterstore + * @author Daniele Alessandri + */ +class ZSetIntersectionStore extends ZSetUnionStore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZINTERSTORE'; + } +} diff --git a/vendor/Predis/Command/ZSetRange.php b/vendor/Predis/Command/ZSetRange.php new file mode 100644 index 0000000..7403873 --- /dev/null +++ b/vendor/Predis/Command/ZSetRange.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrange + * @author Daniele Alessandri + */ +class ZSetRange extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANGE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + if (count($arguments) === 4) { + $lastType = gettype($arguments[3]); + + if ($lastType === 'string' && strtoupper($arguments[3]) === 'WITHSCORES') { + // Used for compatibility with older versions + $arguments[3] = array('WITHSCORES' => true); + $lastType = 'array'; + } + + if ($lastType === 'array') { + $options = $this->prepareOptions(array_pop($arguments)); + return array_merge($arguments, $options); + } + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * @return array + */ + protected function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (!empty($opts['WITHSCORES'])) { + $finalizedOpts[] = 'WITHSCORES'; + } + + return $finalizedOpts; + } + + /** + * Checks for the presence of the WITHSCORES modifier. + * + * @return Boolean + */ + protected function withScores() + { + $arguments = $this->getArguments(); + + if (count($arguments) < 4) { + return false; + } + + return strtoupper($arguments[3]) === 'WITHSCORES'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if ($this->withScores()) { + $result = array(); + + for ($i = 0; $i < count($data); $i++) { + $result[] = array($data[$i], $data[++$i]); + } + + return $result; + } + + return $data; + } +} diff --git a/vendor/Predis/Command/ZSetRangeByScore.php b/vendor/Predis/Command/ZSetRangeByScore.php new file mode 100644 index 0000000..cfe73aa --- /dev/null +++ b/vendor/Predis/Command/ZSetRangeByScore.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrangebyscore + * @author Daniele Alessandri + */ +class ZSetRangeByScore extends ZSetRange +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANGEBYSCORE'; + } + + /** + * {@inheritdoc} + */ + protected function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) { + $limit = array_change_key_case($opts['LIMIT'], CASE_UPPER); + + $finalizedOpts[] = 'LIMIT'; + $finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0]; + $finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1]; + } + + return array_merge($finalizedOpts, parent::prepareOptions($options)); + } + + /** + * {@inheritdoc} + */ + protected function withScores() + { + $arguments = $this->getArguments(); + + for ($i = 3; $i < count($arguments); $i++) { + switch (strtoupper($arguments[$i])) { + case 'WITHSCORES': + return true; + + case 'LIMIT': + $i += 2; + break; + } + } + + return false; + } +} diff --git a/vendor/Predis/Command/ZSetRank.php b/vendor/Predis/Command/ZSetRank.php new file mode 100644 index 0000000..41c2ae2 --- /dev/null +++ b/vendor/Predis/Command/ZSetRank.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrank + * @author Daniele Alessandri + */ +class ZSetRank extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANK'; + } +} diff --git a/vendor/Predis/Command/ZSetRemove.php b/vendor/Predis/Command/ZSetRemove.php new file mode 100644 index 0000000..ae7208d --- /dev/null +++ b/vendor/Predis/Command/ZSetRemove.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrem + * @author Daniele Alessandri + */ +class ZSetRemove extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREM'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/Predis/Command/ZSetRemoveRangeByRank.php b/vendor/Predis/Command/ZSetRemoveRangeByRank.php new file mode 100644 index 0000000..45b9028 --- /dev/null +++ b/vendor/Predis/Command/ZSetRemoveRangeByRank.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zremrangebyrank + * @author Daniele Alessandri + */ +class ZSetRemoveRangeByRank extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREMRANGEBYRANK'; + } +} diff --git a/vendor/Predis/Command/ZSetRemoveRangeByScore.php b/vendor/Predis/Command/ZSetRemoveRangeByScore.php new file mode 100644 index 0000000..a2b3ad8 --- /dev/null +++ b/vendor/Predis/Command/ZSetRemoveRangeByScore.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zremrangebyscore + * @author Daniele Alessandri + */ +class ZSetRemoveRangeByScore extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREMRANGEBYSCORE'; + } +} diff --git a/vendor/Predis/Command/ZSetReverseRange.php b/vendor/Predis/Command/ZSetReverseRange.php new file mode 100644 index 0000000..e7344e0 --- /dev/null +++ b/vendor/Predis/Command/ZSetReverseRange.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrevrange + * @author Daniele Alessandri + */ +class ZSetReverseRange extends ZSetRange +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANGE'; + } +} diff --git a/vendor/Predis/Command/ZSetReverseRangeByScore.php b/vendor/Predis/Command/ZSetReverseRangeByScore.php new file mode 100644 index 0000000..cded7c1 --- /dev/null +++ b/vendor/Predis/Command/ZSetReverseRangeByScore.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrevrangebyscore + * @author Daniele Alessandri + */ +class ZSetReverseRangeByScore extends ZSetRangeByScore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANGEBYSCORE'; + } +} diff --git a/vendor/Predis/Command/ZSetReverseRank.php b/vendor/Predis/Command/ZSetReverseRank.php new file mode 100644 index 0000000..feb1048 --- /dev/null +++ b/vendor/Predis/Command/ZSetReverseRank.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zrevrank + * @author Daniele Alessandri + */ +class ZSetReverseRank extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANK'; + } +} diff --git a/vendor/Predis/Command/ZSetScore.php b/vendor/Predis/Command/ZSetScore.php new file mode 100644 index 0000000..8455e4e --- /dev/null +++ b/vendor/Predis/Command/ZSetScore.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zscore + * @author Daniele Alessandri + */ +class ZSetScore extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZSCORE'; + } +} diff --git a/vendor/Predis/Command/ZSetUnionStore.php b/vendor/Predis/Command/ZSetUnionStore.php new file mode 100644 index 0000000..ba97176 --- /dev/null +++ b/vendor/Predis/Command/ZSetUnionStore.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/zunionstore + * @author Daniele Alessandri + */ +class ZSetUnionStore extends PrefixableCommand +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZUNIONSTORE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(Array $arguments) + { + $options = array(); + $argc = count($arguments); + + if ($argc > 2 && is_array($arguments[$argc - 1])) { + $options = $this->prepareOptions(array_pop($arguments)); + } + + if (is_array($arguments[1])) { + $arguments = array_merge( + array($arguments[0], count($arguments[1])), + $arguments[1] + ); + } + + return array_merge($arguments, $options); + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * @return array + */ + private function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (isset($opts['WEIGHTS']) && is_array($opts['WEIGHTS'])) { + $finalizedOpts[] = 'WEIGHTS'; + + foreach ($opts['WEIGHTS'] as $weight) { + $finalizedOpts[] = $weight; + } + } + + if (isset($opts['AGGREGATE'])) { + $finalizedOpts[] = 'AGGREGATE'; + $finalizedOpts[] = $opts['AGGREGATE']; + } + + return $finalizedOpts; + } + + /** + * {@inheritdoc} + */ + public function prefixKeys($prefix) + { + if ($arguments = $this->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + $length = ((int) $arguments[1]) + 2; + + for ($i = 2; $i < $length; $i++) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $this->setRawArguments($arguments); + } + } +} diff --git a/vendor/Predis/CommunicationException.php b/vendor/Predis/CommunicationException.php new file mode 100644 index 0000000..c9e75e2 --- /dev/null +++ b/vendor/Predis/CommunicationException.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +use Predis\Connection\SingleConnectionInterface; + +/** + * Base exception class for network-related errors. + * + * @author Daniele Alessandri + */ +abstract class CommunicationException extends PredisException +{ + private $connection; + + /** + * @param SingleConnectionInterface $connection Connection that generated the exception. + * @param string $message Error message. + * @param int $code Error code. + * @param \Exception $innerException Inner exception for wrapping the original error. + */ + public function __construct( + SingleConnectionInterface $connection, $message = null, $code = null, \Exception $innerException = null + ) { + parent::__construct($message, $code, $innerException); + $this->connection = $connection; + } + + /** + * Gets the connection that generated the exception. + * + * @return SingleConnectionInterface + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Indicates if the receiver should reset the underlying connection. + * + * @return Boolean + */ + public function shouldResetConnection() + { + return true; + } + + /** + * Offers a generic and reusable method to handle exceptions generated by + * a connection object. + * + * @param CommunicationException $exception Exception. + */ + public static function handle(CommunicationException $exception) + { + if ($exception->shouldResetConnection()) { + $connection = $exception->getConnection(); + + if ($connection->isConnected()) { + $connection->disconnect(); + } + } + + throw $exception; + } +} diff --git a/vendor/Predis/Connection/AbstractConnection.php b/vendor/Predis/Connection/AbstractConnection.php new file mode 100644 index 0000000..96a84ae --- /dev/null +++ b/vendor/Predis/Connection/AbstractConnection.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\ClientException; +use Predis\CommunicationException; +use Predis\NotSupportedException; +use Predis\Command\CommandInterface; +use Predis\Protocol\ProtocolException; + +/** + * Base class with the common logic used by connection classes to communicate with Redis. + * + * @author Daniele Alessandri + */ +abstract class AbstractConnection implements SingleConnectionInterface +{ + private $resource; + private $cachedId; + + protected $parameters; + protected $initCmds = array(); + + /** + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + */ + public function __construct(ConnectionParametersInterface $parameters) + { + $this->parameters = $this->checkParameters($parameters); + } + + /** + * Disconnects from the server and destroys the underlying resource when + * PHP's garbage collector kicks in. + */ + public function __destruct() + { + $this->disconnect(); + } + + /** + * Checks some of the parameters used to initialize the connection. + * + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + */ + protected function checkParameters(ConnectionParametersInterface $parameters) + { + switch ($parameters->scheme) { + case 'unix': + if (!isset($parameters->path)) { + throw new \InvalidArgumentException('Missing UNIX domain socket path'); + } + + case 'tcp': + return $parameters; + + default: + throw new \InvalidArgumentException("Invalid scheme: {$parameters->scheme}"); + } + } + + /** + * Creates the underlying resource used to communicate with Redis. + * + * @return mixed + */ + protected abstract function createResource(); + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return isset($this->resource); + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if ($this->isConnected()) { + throw new ClientException('Connection already estabilished'); + } + + $this->resource = $this->createResource(); + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + unset($this->resource); + } + + /** + * {@inheritdoc} + */ + public function pushInitCommand(CommandInterface $command) + { + $this->initCmds[] = $command; + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $this->writeCommand($command); + return $this->readResponse($command); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->read(); + } + + /** + * Helper method to handle connection errors. + * + * @param string $message Error message. + * @param int $code Error code. + */ + protected function onConnectionError($message, $code = null) + { + CommunicationException::handle(new ConnectionException($this, "$message [{$this->parameters->scheme}://{$this->getIdentifier()}]", $code)); + } + + /** + * Helper method to handle protocol errors. + * + * @param string $message Error message. + */ + protected function onProtocolError($message) + { + CommunicationException::handle(new ProtocolException($this, "$message [{$this->parameters->scheme}://{$this->getIdentifier()}]")); + } + + /** + * Helper method to handle not supported connection parameters. + * + * @param string $option Name of the option. + * @param mixed $parameters Parameters used to initialize the connection. + */ + protected function onInvalidOption($option, $parameters = null) + { + $class = get_called_class(); + $message = "Invalid option for connection $class: $option"; + + if (isset($parameters)) { + $message .= sprintf(' [%s => %s]', $option, $parameters->{$option}); + } + + throw new NotSupportedException($message); + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + if (isset($this->resource)) { + return $this->resource; + } + + $this->connect(); + + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets an identifier for the connection. + * + * @return string + */ + protected function getIdentifier() + { + if ($this->parameters->scheme === 'unix') { + return $this->parameters->path; + } + + return "{$this->parameters->host}:{$this->parameters->port}"; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + if (!isset($this->cachedId)) { + $this->cachedId = $this->getIdentifier(); + } + + return $this->cachedId; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('parameters', 'initCmds'); + } +} diff --git a/vendor/Predis/Connection/AggregatedConnectionInterface.php b/vendor/Predis/Connection/AggregatedConnectionInterface.php new file mode 100644 index 0000000..bd1065c --- /dev/null +++ b/vendor/Predis/Connection/AggregatedConnectionInterface.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; + +/** + * Defines a virtual connection composed by multiple connection objects. + * + * @author Daniele Alessandri + */ +interface AggregatedConnectionInterface extends ConnectionInterface +{ + /** + * Adds a connection instance to the aggregated connection. + * + * @param SingleConnectionInterface $connection Instance of a connection. + */ + public function add(SingleConnectionInterface $connection); + + /** + * Removes the specified connection instance from the aggregated + * connection. + * + * @param SingleConnectionInterface $connection Instance of a connection. + * @return Boolean Returns true if the connection was in the pool. + */ + public function remove(SingleConnectionInterface $connection); + + /** + * Gets the actual connection instance in charge of the specified command. + * + * @param CommandInterface $command Instance of a Redis command. + * @return SingleConnectionInterface + */ + public function getConnection(CommandInterface $command); + + /** + * Retrieves a connection instance from the aggregated connection + * using an alias. + * + * @param string $connectionId Alias of a connection + * @return SingleConnectionInterface + */ + public function getConnectionById($connectionId); +} diff --git a/vendor/Predis/Connection/ClusterConnectionInterface.php b/vendor/Predis/Connection/ClusterConnectionInterface.php new file mode 100644 index 0000000..956ef2c --- /dev/null +++ b/vendor/Predis/Connection/ClusterConnectionInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +/** + * Defines a cluster of Redis servers formed by aggregating multiple + * connection objects. + * + * @author Daniele Alessandri + */ +interface ClusterConnectionInterface extends AggregatedConnectionInterface +{ +} diff --git a/vendor/Predis/Connection/ComposableConnectionInterface.php b/vendor/Predis/Connection/ComposableConnectionInterface.php new file mode 100644 index 0000000..8b63536 --- /dev/null +++ b/vendor/Predis/Connection/ComposableConnectionInterface.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Protocol\ProtocolInterface; + +/** + * Defines a connection object used to communicate with a single Redis server + * that leverages an external protocol processor to handle pluggable protocol + * handlers. + * + * @author Daniele Alessandri + */ +interface ComposableConnectionInterface extends SingleConnectionInterface +{ + /** + * Sets the protocol processor used by the connection. + * + * @param ProtocolInterface $protocol Protocol processor. + */ + public function setProtocol(ProtocolInterface $protocol); + + /** + * Gets the protocol processor used by the connection. + */ + public function getProtocol(); + + /** + * Writes a buffer that contains a serialized Redis command. + * + * @param string $buffer Serialized Redis command. + */ + public function writeBytes($buffer); + + /** + * Reads a specified number of bytes from the connection. + * + * @param string + */ + public function readBytes($length); + + /** + * Reads a line from the connection. + * + * @param string + */ + public function readLine(); +} diff --git a/vendor/Predis/Connection/ComposableStreamConnection.php b/vendor/Predis/Connection/ComposableStreamConnection.php new file mode 100644 index 0000000..34aaa9b --- /dev/null +++ b/vendor/Predis/Connection/ComposableStreamConnection.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; +use Predis\Protocol\ProtocolInterface; +use Predis\Protocol\Text\TextProtocol; + +/** + * Connection abstraction to Redis servers based on PHP's stream that uses an + * external protocol processor defining the protocol used for the communication. + * + * @author Daniele Alessandri + */ +class ComposableStreamConnection extends StreamConnection implements ComposableConnectionInterface +{ + private $protocol; + + /** + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + * @param ProtocolInterface $protocol A protocol processor. + */ + public function __construct(ConnectionParametersInterface $parameters, ProtocolInterface $protocol = null) + { + $this->parameters = $this->checkParameters($parameters); + $this->protocol = $protocol ?: new TextProtocol(); + } + + /** + * {@inheritdoc} + */ + public function setProtocol(ProtocolInterface $protocol) + { + if ($protocol === null) { + throw new \InvalidArgumentException("The protocol instance cannot be a null value"); + } + + $this->protocol = $protocol; + } + + /** + * {@inheritdoc} + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * {@inheritdoc} + */ + public function writeBytes($buffer) + { + parent::writeBytes($buffer); + } + + /** + * {@inheritdoc} + */ + public function readBytes($length) + { + if ($length <= 0) { + throw new \InvalidArgumentException('Length parameter must be greater than 0'); + } + + $value = ''; + $socket = $this->getResource(); + + do { + $chunk = fread($socket, $length); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading bytes from the server'); + } + + $value .= $chunk; + } while (($length -= strlen($chunk)) > 0); + + return $value; + } + + /** + * {@inheritdoc} + */ + public function readLine() + { + $value = ''; + $socket = $this->getResource(); + + do { + $chunk = fgets($socket); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading line from the server'); + } + + $value .= $chunk; + } while (substr($value, -2) !== "\r\n"); + + return substr($value, 0, -2); + } + + /** + * {@inheritdoc} + */ + public function writeCommand(CommandInterface $command) + { + $this->protocol->write($this, $command); + } + + /** + * {@inheritdoc} + */ + public function read() + { + return $this->protocol->read($this); + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array_diff(array_merge(parent::__sleep(), array('protocol')), array('mbiterable')); + } +} diff --git a/vendor/Predis/Connection/ConnectionException.php b/vendor/Predis/Connection/ConnectionException.php new file mode 100644 index 0000000..ef2e9d7 --- /dev/null +++ b/vendor/Predis/Connection/ConnectionException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\CommunicationException; + +/** + * Exception class that identifies connection-related errors. + * + * @author Daniele Alessandri + */ +class ConnectionException extends CommunicationException +{ +} diff --git a/vendor/Predis/Connection/ConnectionFactory.php b/vendor/Predis/Connection/ConnectionFactory.php new file mode 100644 index 0000000..5527232 --- /dev/null +++ b/vendor/Predis/Connection/ConnectionFactory.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Profile\ServerProfile; +use Predis\Profile\ServerProfileInterface; + +/** + * Provides a default factory for Redis connections that maps URI schemes + * to connection classes implementing Predis\Connection\SingleConnectionInterface. + * + * @author Daniele Alessandri + */ +class ConnectionFactory implements ConnectionFactoryInterface +{ + protected $schemes; + protected $profile; + + /** + * Initializes a new instance of the default connection factory class used by Predis. + * + * @param ServerProfileInterface $profile Server profile used to initialize new connections. + */ + public function __construct(ServerProfileInterface $profile = null) + { + $this->schemes = $this->getDefaultSchemes(); + $this->profile = $profile; + } + + /** + * Returns a named array that maps URI schemes to connection classes. + * + * @return array Map of URI schemes and connection classes. + */ + protected function getDefaultSchemes() + { + return array( + 'tcp' => 'Predis\Connection\StreamConnection', + 'unix' => 'Predis\Connection\StreamConnection', + 'http' => 'Predis\Connection\WebdisConnection', + ); + } + + /** + * Checks if the provided argument represents a valid connection class + * implementing Predis\Connection\SingleConnectionInterface. Optionally, + * callable objects are used for lazy initialization of connection objects. + * + * @param mixed $initializer FQN of a connection class or a callable for lazy initialization. + * @return mixed + */ + protected function checkInitializer($initializer) + { + if (is_callable($initializer)) { + return $initializer; + } + + $initializerReflection = new \ReflectionClass($initializer); + + if (!$initializerReflection->isSubclassOf('Predis\Connection\SingleConnectionInterface')) { + throw new \InvalidArgumentException( + 'A connection initializer must be a valid connection class or a callable object' + ); + } + + return $initializer; + } + + /** + * {@inheritdoc} + */ + public function define($scheme, $initializer) + { + $this->schemes[$scheme] = $this->checkInitializer($initializer); + } + + /** + * {@inheritdoc} + */ + public function undefine($scheme) + { + unset($this->schemes[$scheme]); + } + + /** + * {@inheritdoc} + */ + public function create($parameters) + { + if (!$parameters instanceof ConnectionParametersInterface) { + $parameters = new ConnectionParameters($parameters ?: array()); + } + + $scheme = $parameters->scheme; + + if (!isset($this->schemes[$scheme])) { + throw new \InvalidArgumentException("Unknown connection scheme: $scheme"); + } + + $initializer = $this->schemes[$scheme]; + + if (is_callable($initializer)) { + $connection = call_user_func($initializer, $parameters, $this); + } else { + $connection = new $initializer($parameters); + $this->prepareConnection($connection); + } + + if (!$connection instanceof SingleConnectionInterface) { + throw new \InvalidArgumentException( + 'Objects returned by connection initializers must implement ' . + 'Predis\Connection\SingleConnectionInterface' + ); + } + + return $connection; + } + + /** + * {@inheritdoc} + */ + public function createAggregated(AggregatedConnectionInterface $connection, Array $parameters) + { + foreach ($parameters as $node) { + $connection->add($node instanceof SingleConnectionInterface ? $node : $this->create($node)); + } + + return $connection; + } + + /** + * Prepares a connection object after its initialization. + * + * @param SingleConnectionInterface $connection Instance of a connection object. + */ + protected function prepareConnection(SingleConnectionInterface $connection) + { + if (isset($this->profile)) { + $parameters = $connection->getParameters(); + + if (isset($parameters->password)) { + $command = $this->profile->createCommand('auth', array($parameters->password)); + $connection->pushInitCommand($command); + } + + if (isset($parameters->database)) { + $command = $this->profile->createCommand('select', array($parameters->database)); + $connection->pushInitCommand($command); + } + } + } + + /** + * Sets the server profile used to create initialization commands for connections. + * + * @param ServerProfileInterface $profile Server profile instance. + */ + public function setProfile(ServerProfileInterface $profile) + { + $this->profile = $profile; + } + + /** + * Returns the server profile used to create initialization commands for connections. + * + * @return ServerProfileInterface + */ + public function getProfile() + { + return $this->profile; + } +} diff --git a/vendor/Predis/Connection/ConnectionFactoryInterface.php b/vendor/Predis/Connection/ConnectionFactoryInterface.php new file mode 100644 index 0000000..0ac6be7 --- /dev/null +++ b/vendor/Predis/Connection/ConnectionFactoryInterface.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +/** + * Interface that must be implemented by classes that provide their own mechanism + * to create and initialize new instances of Predis\Connection\SingleConnectionInterface. + * + * @author Daniele Alessandri + */ +interface ConnectionFactoryInterface +{ + /** + * Defines or overrides the connection class identified by a scheme prefix. + * + * @param string $scheme URI scheme identifying the connection class. + * @param mixed $initializer FQN of a connection class or a callable object for lazy initialization. + */ + public function define($scheme, $initializer); + + /** + * Undefines the connection identified by a scheme prefix. + * + * @param string $scheme Parameters for the connection. + */ + public function undefine($scheme); + + /** + * Creates a new connection object. + * + * @param mixed $parameters Parameters for the connection. + * @return SingleConnectionInterface + */ + public function create($parameters); + + /** + * Prepares an aggregation of connection objects. + * + * @param AggregatedConnectionInterface $cluster Instance of an aggregated connection class. + * @param array $parameters List of parameters for each connection object. + * @return AggregatedConnectionInterface + */ + public function createAggregated(AggregatedConnectionInterface $cluster, Array $parameters); +} diff --git a/vendor/Predis/Connection/ConnectionInterface.php b/vendor/Predis/Connection/ConnectionInterface.php new file mode 100644 index 0000000..b30de09 --- /dev/null +++ b/vendor/Predis/Connection/ConnectionInterface.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; + +/** + * Defines a connection object used to communicate with one or multiple + * Redis servers. + * + * @author Daniele Alessandri + */ +interface ConnectionInterface +{ + /** + * Opens the connection. + */ + public function connect(); + + /** + * Closes the connection. + */ + public function disconnect(); + + /** + * Returns if the connection is open. + * + * @return Boolean + */ + public function isConnected(); + + /** + * Write a Redis command on the connection. + * + * @param CommandInterface $command Instance of a Redis command. + */ + public function writeCommand(CommandInterface $command); + + /** + * Reads the reply for a Redis command from the connection. + * + * @param CommandInterface $command Instance of a Redis command. + * @return mixed + */ + public function readResponse(CommandInterface $command); + + /** + * Writes a Redis command to the connection and reads back the reply. + * + * @param CommandInterface $command Instance of a Redis command. + * @return mixed + */ + public function executeCommand(CommandInterface $command); +} diff --git a/vendor/Predis/Connection/ConnectionParameters.php b/vendor/Predis/Connection/ConnectionParameters.php new file mode 100644 index 0000000..2127843 --- /dev/null +++ b/vendor/Predis/Connection/ConnectionParameters.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\ClientException; + +/** + * Handles parsing and validation of connection parameters. + * + * @author Daniele Alessandri + */ +class ConnectionParameters implements ConnectionParametersInterface +{ + private $parameters; + + private static $defaults = array( + 'scheme' => 'tcp', + 'host' => '127.0.0.1', + 'port' => 6379, + 'timeout' => 5.0, + ); + + /** + * @param string|array Connection parameters in the form of an URI string or a named array. + */ + public function __construct($parameters = array()) + { + if (!is_array($parameters)) { + $parameters = self::parseURI($parameters); + } + + $this->parameters = $this->filter($parameters) + $this->getDefaults(); + } + + /** + * Returns some default parameters with their values. + * + * @return array + */ + protected function getDefaults() + { + return self::$defaults; + } + + /** + * Returns cast functions for user-supplied parameter values. + * + * @return array + */ + protected function getValueCasters() + { + return array( + 'port' => 'self::castInteger', + 'async_connect' => 'self::castBoolean', + 'persistent' => 'self::castBoolean', + 'timeout' => 'self::castFloat', + 'read_write_timeout' => 'self::castFloat', + 'iterable_multibulk' => 'self::castBoolean', + ); + } + + /** + * Validates value as boolean. + * + * @param mixed $value Input value. + * @return boolean + */ + private static function castBoolean($value) + { + return (bool) $value; + } + + /** + * Validates value as float. + * + * @param mixed $value Input value. + * @return float + */ + private static function castFloat($value) + { + return (float) $value; + } + + /** + * Validates value as integer. + * + * @param mixed $value Input value. + * @return int + */ + private static function castInteger($value) + { + return (int) $value; + } + + /** + * Parses an URI string and returns an array of connection parameters. + * + * @param string $uri Connection string. + * @return array + */ + public static function parseURI($uri) + { + if (stripos($uri, 'unix') === 0) { + // Hack to support URIs for UNIX sockets with minimal effort. + $uri = str_ireplace('unix:///', 'unix://localhost/', $uri); + } + + if (!($parsed = @parse_url($uri)) || !isset($parsed['host'])) { + throw new ClientException("Invalid URI: $uri"); + } + + if (isset($parsed['query'])) { + foreach (explode('&', $parsed['query']) as $kv) { + @list($k, $v) = explode('=', $kv); + $parsed[$k] = $v; + } + + unset($parsed['query']); + } + + return $parsed; + } + + /** + * Validates and converts each value of the connection parameters array. + * + * @param array $parameters Connection parameters. + * @return array + */ + private function filter(Array $parameters) + { + if ($parameters) { + $casters = array_intersect_key($this->getValueCasters(), $parameters); + + foreach ($casters as $parameter => $caster) { + $parameters[$parameter] = call_user_func($caster, $parameters[$parameter]); + } + } + + return $parameters; + } + + /** + * {@inheritdoc} + */ + public function __get($parameter) + { + if (isset($this->{$parameter})) { + return $this->parameters[$parameter]; + } + } + + /** + * {@inheritdoc} + */ + public function __isset($parameter) + { + return isset($this->parameters[$parameter]); + } + + /** + * {@inheritdoc} + */ + public function toArray() + { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('parameters'); + } +} diff --git a/vendor/Predis/Connection/ConnectionParametersInterface.php b/vendor/Predis/Connection/ConnectionParametersInterface.php new file mode 100644 index 0000000..3e3cb38 --- /dev/null +++ b/vendor/Predis/Connection/ConnectionParametersInterface.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +/** + * Interface that must be implemented by classes that provide their own mechanism + * to parse and handle connection parameters. + * + * @author Daniele Alessandri + */ +interface ConnectionParametersInterface +{ + /** + * Checks if the specified parameters is set. + * + * @param string $property Name of the property. + * @return Boolean + */ + public function __isset($parameter); + + /** + * Returns the value of the specified parameter. + * + * @param string $parameter Name of the parameter. + * @return mixed + */ + public function __get($parameter); + + /** + * Returns an array representation of the connection parameters. + * + * @return array + */ + public function toArray(); +} diff --git a/vendor/Predis/Connection/MasterSlaveReplication.php b/vendor/Predis/Connection/MasterSlaveReplication.php new file mode 100644 index 0000000..91c8de4 --- /dev/null +++ b/vendor/Predis/Connection/MasterSlaveReplication.php @@ -0,0 +1,261 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; +use Predis\Replication\ReplicationStrategy; + +/** + * Aggregated connection class used by to handle replication with a + * group of servers in a master/slave configuration. + * + * @author Daniele Alessandri + */ +class MasterSlaveReplication implements ReplicationConnectionInterface +{ + protected $strategy; + protected $master; + protected $slaves; + protected $current; + + /** + * + */ + public function __construct(ReplicationStrategy $strategy = null) + { + $this->slaves = array(); + $this->strategy = $strategy ?: new ReplicationStrategy(); + } + + /** + * Checks if one master and at least one slave have been defined. + */ + protected function check() + { + if (!isset($this->master) || !$this->slaves) { + throw new \RuntimeException('Replication needs a master and at least one slave.'); + } + } + + /** + * Resets the connection state. + */ + protected function reset() + { + $this->current = null; + } + + /** + * {@inheritdoc} + */ + public function add(SingleConnectionInterface $connection) + { + $alias = $connection->getParameters()->alias; + + if ($alias === 'master') { + $this->master = $connection; + } else { + $this->slaves[$alias ?: count($this->slaves)] = $connection; + } + + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function remove(SingleConnectionInterface $connection) + { + if ($connection->getParameters()->alias === 'master') { + $this->master = null; + $this->reset(); + + return true; + } else { + if (($id = array_search($connection, $this->slaves, true)) !== false) { + unset($this->slaves[$id]); + $this->reset(); + + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + if ($this->current === null) { + $this->check(); + $this->current = $this->strategy->isReadOperation($command) ? $this->pickSlave() : $this->master; + + return $this->current; + } + + if ($this->current === $this->master) { + return $this->current; + } + + if (!$this->strategy->isReadOperation($command)) { + $this->current = $this->master; + } + + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionId) + { + if ($connectionId === 'master') { + return $this->master; + } + + if (isset($this->slaves[$connectionId])) { + return $this->slaves[$connectionId]; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function switchTo($connection) + { + $this->check(); + + if (!$connection instanceof SingleConnectionInterface) { + $connection = $this->getConnectionById($connection); + } + if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) { + throw new \InvalidArgumentException('The specified connection is not valid.'); + } + + $this->current = $connection; + } + + /** + * {@inheritdoc} + */ + public function getCurrent() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function getMaster() + { + return $this->master; + } + + /** + * {@inheritdoc} + */ + public function getSlaves() + { + return array_values($this->slaves); + } + + /** + * Returns the underlying replication strategy. + * + * @return ReplicationStrategy + */ + public function getReplicationStrategy() + { + return $this->strategy; + } + + /** + * Returns a random slave. + * + * @return SingleConnectionInterface + */ + protected function pickSlave() + { + return $this->slaves[array_rand($this->slaves)]; + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return $this->current ? $this->current->isConnected() : false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if ($this->current === null) { + $this->check(); + $this->current = $this->pickSlave(); + } + + $this->current->connect(); + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->master) { + $this->master->disconnect(); + } + + foreach ($this->slaves as $connection) { + $connection->disconnect(); + } + } + + /** + * {@inheritdoc} + */ + public function writeCommand(CommandInterface $command) + { + $this->getConnection($command)->writeCommand($command); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->getConnection($command)->readResponse($command); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + return $this->getConnection($command)->executeCommand($command); + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('master', 'slaves', 'strategy'); + } +} diff --git a/vendor/Predis/Connection/PhpiredisConnection.php b/vendor/Predis/Connection/PhpiredisConnection.php new file mode 100644 index 0000000..ac6ff40 --- /dev/null +++ b/vendor/Predis/Connection/PhpiredisConnection.php @@ -0,0 +1,389 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\NotSupportedException; +use Predis\ResponseError; +use Predis\ResponseQueued; +use Predis\Command\CommandInterface; + +/** + * This class provides the implementation of a Predis connection that uses the + * PHP socket extension for network communication and wraps the phpiredis C + * extension (PHP bindings for hiredis) to parse the Redis protocol. Everything + * is highly experimental (even the very same phpiredis since it is quite new), + * so use it at your own risk. + * + * This class is mainly intended to provide an optional low-overhead alternative + * for processing replies from Redis compared to the standard pure-PHP classes. + * Differences in speed when dealing with short inline replies are practically + * nonexistent, the actual speed boost is for long multibulk replies when this + * protocol processor can parse and return replies very fast. + * + * For instructions on how to build and install the phpiredis extension, please + * consult the repository of the project. + * + * The connection parameters supported by this class are: + * + * - scheme: it can be either 'tcp' or 'unix'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - timeout: timeout to perform the connection. + * - read_write_timeout: timeout of read / write operations. + * + * @link http://github.com/nrk/phpiredis + * @author Daniele Alessandri + */ +class PhpiredisConnection extends AbstractConnection +{ + const ERR_MSG_EXTENSION = 'The %s extension must be loaded in order to be able to use this connection class'; + + private $reader; + + /** + * {@inheritdoc} + */ + public function __construct(ConnectionParametersInterface $parameters) + { + $this->checkExtensions(); + $this->initializeReader(); + + parent::__construct($parameters); + } + + /** + * Disconnects from the server and destroys the underlying resource and the + * protocol reader resource when PHP's garbage collector kicks in. + */ + public function __destruct() + { + phpiredis_reader_destroy($this->reader); + + parent::__destruct(); + } + + /** + * Checks if the socket and phpiredis extensions are loaded in PHP. + */ + private function checkExtensions() + { + if (!function_exists('socket_create')) { + throw new NotSupportedException(sprintf(self::ERR_MSG_EXTENSION, 'socket')); + } + if (!function_exists('phpiredis_reader_create')) { + throw new NotSupportedException(sprintf(self::ERR_MSG_EXTENSION, 'phpiredis')); + } + } + + /** + * {@inheritdoc} + */ + protected function checkParameters(ConnectionParametersInterface $parameters) + { + if (isset($parameters->iterable_multibulk)) { + $this->onInvalidOption('iterable_multibulk', $parameters); + } + if (isset($parameters->persistent)) { + $this->onInvalidOption('persistent', $parameters); + } + + return parent::checkParameters($parameters); + } + + /** + * Initializes the protocol reader resource. + */ + private function initializeReader() + { + $reader = phpiredis_reader_create(); + + phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); + phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); + + $this->reader = $reader; + } + + /** + * Gets the handler used by the protocol reader to handle status replies. + * + * @return \Closure + */ + private function getStatusHandler() + { + return function ($payload) { + switch ($payload) { + case 'OK': + return true; + + case 'QUEUED': + return new ResponseQueued(); + + default: + return $payload; + } + }; + } + + /** + * Gets the handler used by the protocol reader to handle Redis errors. + * + * @param Boolean $throw_errors Specify if Redis errors throw exceptions. + * @return \Closure + */ + private function getErrorHandler() + { + return function ($errorMessage) { + return new ResponseError($errorMessage); + }; + } + + /** + * Helper method used to throw exceptions on socket errors. + */ + private function emitSocketError() + { + $errno = socket_last_error(); + $errstr = socket_strerror($errno); + + $this->disconnect(); + + $this->onConnectionError(trim($errstr), $errno); + } + + /** + * {@inheritdoc} + */ + protected function createResource() + { + $parameters = $this->parameters; + + $isUnix = $this->parameters->scheme === 'unix'; + $domain = $isUnix ? AF_UNIX : AF_INET; + $protocol = $isUnix ? 0 : SOL_TCP; + + $socket = @call_user_func('socket_create', $domain, SOCK_STREAM, $protocol); + if (!is_resource($socket)) { + $this->emitSocketError(); + } + + $this->setSocketOptions($socket, $parameters); + + return $socket; + } + + /** + * Sets options on the socket resource from the connection parameters. + * + * @param resource $socket Socket resource. + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + */ + private function setSocketOptions($socket, ConnectionParametersInterface $parameters) + { + if ($parameters->scheme !== 'tcp') { + return; + } + + if (!socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1)) { + $this->emitSocketError(); + } + + if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { + $this->emitSocketError(); + } + + if (isset($parameters->read_write_timeout)) { + $rwtimeout = $parameters->read_write_timeout; + $timeoutSec = floor($rwtimeout); + $timeoutUsec = ($rwtimeout - $timeoutSec) * 1000000; + + $timeout = array( + 'sec' => $timeoutSec, + 'usec' => $timeoutUsec, + ); + + if (!socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout)) { + $this->emitSocketError(); + } + + if (!socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout)) { + $this->emitSocketError(); + } + } + } + + /** + * Gets the address from the connection parameters. + * + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + * @return string + */ + private function getAddress(ConnectionParametersInterface $parameters) + { + if ($parameters->scheme === 'unix') { + return $parameters->path; + } + + $host = $parameters->host; + + if (ip2long($host) === false) { + if (($addresses = gethostbynamel($host)) === false) { + $this->onConnectionError("Cannot resolve the address of $host"); + } + return $addresses[array_rand($addresses)]; + } + + return $host; + } + + /** + * Opens the actual connection to the server with a timeout. + * + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + * @return string + */ + private function connectWithTimeout(ConnectionParametersInterface $parameters) + { + $host = self::getAddress($parameters); + $socket = $this->getResource(); + + socket_set_nonblock($socket); + + if (@socket_connect($socket, $host, $parameters->port) === false) { + $error = socket_last_error(); + if ($error != SOCKET_EINPROGRESS && $error != SOCKET_EALREADY) { + $this->emitSocketError(); + } + } + + socket_set_block($socket); + + $null = null; + $selectable = array($socket); + + $timeout = $parameters->timeout; + $timeoutSecs = floor($timeout); + $timeoutUSecs = ($timeout - $timeoutSecs) * 1000000; + + $selected = socket_select($selectable, $selectable, $null, $timeoutSecs, $timeoutUSecs); + + if ($selected === 2) { + $this->onConnectionError('Connection refused', SOCKET_ECONNREFUSED); + } + if ($selected === 0) { + $this->onConnectionError('Connection timed out', SOCKET_ETIMEDOUT); + } + if ($selected === false) { + $this->emitSocketError(); + } + } + + /** + * {@inheritdoc} + */ + public function connect() + { + parent::connect(); + + $this->connectWithTimeout($this->parameters); + + if ($this->initCmds) { + $this->sendInitializationCommands(); + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->isConnected()) { + socket_close($this->getResource()); + parent::disconnect(); + } + } + + /** + * Sends the initialization commands to Redis when the connection is opened. + */ + private function sendInitializationCommands() + { + foreach ($this->initCmds as $command) { + $this->writeCommand($command); + } + foreach ($this->initCmds as $command) { + $this->readResponse($command); + } + } + + /** + * {@inheritdoc} + */ + protected function write($buffer) + { + $socket = $this->getResource(); + + while (($length = strlen($buffer)) > 0) { + $written = socket_write($socket, $buffer, $length); + + if ($length === $written) { + return; + } + if ($written === false) { + $this->onConnectionError('Error while writing bytes to the server'); + } + + $buffer = substr($buffer, $written); + } + } + + /** + * {@inheritdoc} + */ + public function read() + { + $socket = $this->getResource(); + $reader = $this->reader; + + while (($state = phpiredis_reader_get_state($reader)) === PHPIREDIS_READER_STATE_INCOMPLETE) { + if (@socket_recv($socket, $buffer, 4096, 0) === false || $buffer === '') { + $this->emitSocketError(); + } + + phpiredis_reader_feed($reader, $buffer); + } + + if ($state === PHPIREDIS_READER_STATE_COMPLETE) { + return phpiredis_reader_get_reply($reader); + } else { + $this->onProtocolError(phpiredis_reader_get_error($reader)); + } + } + + /** + * {@inheritdoc} + */ + public function writeCommand(CommandInterface $command) + { + $cmdargs = $command->getArguments(); + array_unshift($cmdargs, $command->getId()); + $this->write(phpiredis_format_command($cmdargs)); + } + + /** + * {@inheritdoc} + */ + public function __wakeup() + { + $this->checkExtensions(); + $this->initializeReader(); + } +} diff --git a/vendor/Predis/Connection/PhpiredisStreamConnection.php b/vendor/Predis/Connection/PhpiredisStreamConnection.php new file mode 100644 index 0000000..a7df197 --- /dev/null +++ b/vendor/Predis/Connection/PhpiredisStreamConnection.php @@ -0,0 +1,197 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\NotSupportedException; +use Predis\ResponseError; +use Predis\ResponseQueued; +use Predis\Command\CommandInterface; + +/** + * This class provides the implementation of a Predis connection that uses PHP's + * streams for network communication and wraps the phpiredis C extension (PHP + * bindings for hiredis) to parse and serialize the Redis protocol. Everything + * is highly experimental (even the very same phpiredis since it is quite new), + * so use it at your own risk. + * + * This class is mainly intended to provide an optional low-overhead alternative + * for processing replies from Redis compared to the standard pure-PHP classes. + * Differences in speed when dealing with short inline replies are practically + * nonexistent, the actual speed boost is for long multibulk replies when this + * protocol processor can parse and return replies very fast. + * + * For instructions on how to build and install the phpiredis extension, please + * consult the repository of the project. + * + * The connection parameters supported by this class are: + * + * - scheme: it can be either 'tcp' or 'unix'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - timeout: timeout to perform the connection. + * - read_write_timeout: timeout of read / write operations. + * - async_connect: performs the connection asynchronously. + * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing. + * - persistent: the connection is left intact after a GC collection. + * + * @link https://github.com/nrk/phpiredis + * @author Daniele Alessandri + */ +class PhpiredisStreamConnection extends StreamConnection +{ + private $reader; + + /** + * {@inheritdoc} + */ + public function __construct(ConnectionParametersInterface $parameters) + { + $this->checkExtensions(); + $this->initializeReader(); + + parent::__construct($parameters); + } + + /** + * {@inheritdoc} + */ + public function __destruct() + { + phpiredis_reader_destroy($this->reader); + + parent::__destruct(); + } + + /** + * Checks if the phpiredis extension is loaded in PHP. + */ + protected function checkExtensions() + { + if (!function_exists('phpiredis_reader_create')) { + throw new NotSupportedException( + 'The phpiredis extension must be loaded in order to be able to use this connection class' + ); + } + } + + /** + * {@inheritdoc} + */ + protected function checkParameters(ConnectionParametersInterface $parameters) + { + if (isset($parameters->iterable_multibulk)) { + $this->onInvalidOption('iterable_multibulk', $parameters); + } + + return parent::checkParameters($parameters); + } + + /** + * Initializes the protocol reader resource. + */ + protected function initializeReader() + { + $reader = phpiredis_reader_create(); + + phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); + phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); + + $this->reader = $reader; + } + + /** + * Gets the handler used by the protocol reader to handle status replies. + * + * @return \Closure + */ + protected function getStatusHandler() + { + return function ($payload) { + switch ($payload) { + case 'OK': + return true; + + case 'QUEUED': + return new ResponseQueued(); + + default: + return $payload; + } + }; + } + + /** + * Gets the handler used by the protocol reader to handle Redis errors. + * + * @param Boolean $throw_errors Specify if Redis errors throw exceptions. + * @return \Closure + */ + protected function getErrorHandler() + { + return function ($errorMessage) { + return new ResponseError($errorMessage); + }; + } + + /** + * {@inheritdoc} + */ + public function read() + { + $socket = $this->getResource(); + $reader = $this->reader; + + while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) { + $buffer = fread($socket, 4096); + + if ($buffer === false || $buffer === '') { + $this->onConnectionError('Error while reading bytes from the server'); + return; + } + + phpiredis_reader_feed($reader, $buffer); + } + + if ($state === PHPIREDIS_READER_STATE_COMPLETE) { + return phpiredis_reader_get_reply($reader); + } else { + $this->onProtocolError(phpiredis_reader_get_error($reader)); + } + } + + /** + * {@inheritdoc} + */ + public function writeCommand(CommandInterface $command) + { + $cmdargs = $command->getArguments(); + array_unshift($cmdargs, $command->getId()); + $this->writeBytes(phpiredis_format_command($cmdargs)); + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array_diff(parent::__sleep(), array('mbiterable')); + } + + /** + * {@inheritdoc} + */ + public function __wakeup() + { + $this->checkExtensions(); + $this->initializeReader(); + } +} diff --git a/vendor/Predis/Connection/PredisCluster.php b/vendor/Predis/Connection/PredisCluster.php new file mode 100644 index 0000000..1290bce --- /dev/null +++ b/vendor/Predis/Connection/PredisCluster.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Cluster\CommandHashStrategyInterface; +use Predis\NotSupportedException; +use Predis\Cluster\PredisClusterHashStrategy; +use Predis\Cluster\Distribution\DistributionStrategyInterface; +use Predis\Cluster\Distribution\HashRing; +use Predis\Command\CommandInterface; + +/** + * Abstraction for a cluster of aggregated connections to various Redis servers + * implementing client-side sharding based on pluggable distribution strategies. + * + * @author Daniele Alessandri + * @todo Add the ability to remove connections from pool. + */ +class PredisCluster implements ClusterConnectionInterface, \IteratorAggregate, \Countable +{ + private $pool; + private $strategy; + private $distributor; + + /** + * @param DistributionStrategyInterface $distributor Distribution strategy used by the cluster. + */ + public function __construct(DistributionStrategyInterface $distributor = null) + { + $distributor = $distributor ?: new HashRing(); + + $this->pool = array(); + $this->strategy = new PredisClusterHashStrategy($distributor->getHashGenerator()); + $this->distributor = $distributor; + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + foreach ($this->pool as $connection) { + if ($connection->isConnected()) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + foreach ($this->pool as $connection) { + $connection->connect(); + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + foreach ($this->pool as $connection) { + $connection->disconnect(); + } + } + + /** + * {@inheritdoc} + */ + public function add(SingleConnectionInterface $connection) + { + $parameters = $connection->getParameters(); + + if (isset($parameters->alias)) { + $this->pool[$parameters->alias] = $connection; + } else { + $this->pool[] = $connection; + } + + $weight = isset($parameters->weight) ? $parameters->weight : null; + $this->distributor->add($connection, $weight); + } + + /** + * {@inheritdoc} + */ + public function remove(SingleConnectionInterface $connection) + { + if (($id = array_search($connection, $this->pool, true)) !== false) { + unset($this->pool[$id]); + $this->distributor->remove($connection); + + return true; + } + + return false; + } + + /** + * Removes a connection instance using its alias or index. + * + * @param string $connectionId Alias or index of a connection. + * @return Boolean Returns true if the connection was in the pool. + */ + public function removeById($connectionId) + { + if ($connection = $this->getConnectionById($connectionId)) { + return $this->remove($connection); + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + $hash = $this->strategy->getHash($command); + + if (!isset($hash)) { + throw new NotSupportedException("Cannot use {$command->getId()} with a cluster of connections"); + } + + $node = $this->distributor->get($hash); + + return $node; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionId) + { + return isset($this->pool[$connectionId]) ? $this->pool[$connectionId] : null; + } + + /** + * Retrieves a connection instance from the cluster using a key. + * + * @param string $key Key of a Redis value. + * @return SingleConnectionInterface + */ + public function getConnectionByKey($key) + { + $hash = $this->strategy->getKeyHash($key); + $node = $this->distributor->get($hash); + + return $node; + } + + /** + * Returns the underlying command hash strategy used to hash + * commands by their keys. + * + * @return CommandHashStrategyInterface + */ + public function getCommandHashStrategy() + { + return $this->strategy; + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->pool); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator($this->pool); + } + + /** + * {@inheritdoc} + */ + public function writeCommand(CommandInterface $command) + { + $this->getConnection($command)->writeCommand($command); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->getConnection($command)->readResponse($command); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + return $this->getConnection($command)->executeCommand($command); + } + + /** + * Executes the specified Redis command on all the nodes of a cluster. + * + * @param CommandInterface $command A Redis command. + * @return array + */ + public function executeCommandOnNodes(CommandInterface $command) + { + $replies = array(); + + foreach ($this->pool as $connection) { + $replies[] = $connection->executeCommand($command); + } + + return $replies; + } +} diff --git a/vendor/Predis/Connection/RedisCluster.php b/vendor/Predis/Connection/RedisCluster.php new file mode 100644 index 0000000..7be4562 --- /dev/null +++ b/vendor/Predis/Connection/RedisCluster.php @@ -0,0 +1,384 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\ClientException; +use Predis\Cluster\CommandHashStrategyInterface; +use Predis\NotSupportedException; +use Predis\ResponseErrorInterface; +use Predis\Cluster\RedisClusterHashStrategy; +use Predis\Command\CommandInterface; + +/** + * Abstraction for Redis cluster (Redis v3.0). + * + * @author Daniele Alessandri + */ +class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \Countable +{ + private $pool; + private $slots; + private $slotsMap; + private $slotsPerNode; + private $strategy; + private $connections; + + /** + * @param ConnectionFactoryInterface $connections Connection factory object. + */ + public function __construct(ConnectionFactoryInterface $connections = null) + { + $this->pool = array(); + $this->slots = array(); + $this->strategy = new RedisClusterHashStrategy(); + $this->connections = $connections ?: new ConnectionFactory(); + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + foreach ($this->pool as $connection) { + if ($connection->isConnected()) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + foreach ($this->pool as $connection) { + $connection->connect(); + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + foreach ($this->pool as $connection) { + $connection->disconnect(); + } + } + + /** + * {@inheritdoc} + */ + public function add(SingleConnectionInterface $connection) + { + $this->pool[(string) $connection] = $connection; + unset( + $this->slotsMap, + $this->slotsPerNode + ); + } + + /** + * {@inheritdoc} + */ + public function remove(SingleConnectionInterface $connection) + { + if (($id = array_search($connection, $this->pool, true)) !== false) { + unset( + $this->pool[$id], + $this->slotsMap, + $this->slotsPerNode + ); + + return true; + } + + return false; + } + + /** + * Removes a connection instance using its alias or index. + * + * @param string $connectionId Alias or index of a connection. + * @return Boolean Returns true if the connection was in the pool. + */ + public function removeById($connectionId) + { + if (isset($this->pool[$connectionId])) { + unset( + $this->pool[$connectionId], + $this->slotsMap, + $this->slotsPerNode + ); + + return true; + } + + return false; + } + + /** + * Builds the slots map for the cluster. + * + * @return array + */ + public function buildSlotsMap() + { + $this->slotsMap = array(); + $this->slotsPerNode = (int) (16384 / count($this->pool)); + + foreach ($this->pool as $connectionID => $connection) { + $parameters = $connection->getParameters(); + + if (!isset($parameters->slots)) { + continue; + } + + list($first, $last) = explode('-', $parameters->slots, 2); + $this->setSlots($first, $last, $connectionID); + } + + return $this->slotsMap; + } + + /** + * Returns the current slots map for the cluster. + * + * @return array + */ + public function getSlotsMap() + { + if (!isset($this->slotsMap)) { + $this->slotsMap = array(); + } + + return $this->slotsMap; + } + + /** + * Preassociate a connection to a set of slots to avoid runtime guessing. + * + * @todo Check type or existence of the specified connection. + * @todo Cluster loses the slots assigned with this methods when adding / removing connections. + * + * @param int $first Initial slot. + * @param int $last Last slot. + * @param SingleConnectionInterface|string $connection ID or connection instance. + */ + public function setSlots($first, $last, $connection) + { + if ($first < 0x0000 || $first > 0x3FFF || $last < 0x0000 || $last > 0x3FFF || $last < $first) { + throw new \OutOfBoundsException("Invalid slot values for $connection: [$first-$last]"); + } + + $this->slotsMap = $this->getSlotsMap() + array_fill($first, $last - $first + 1, (string) $connection); + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + $hash = $this->strategy->getHash($command); + + if (!isset($hash)) { + throw new NotSupportedException("Cannot use {$command->getId()} with redis-cluster"); + } + + $slot = $hash & 0x3FFF; + + if (isset($this->slots[$slot])) { + return $this->slots[$slot]; + } + + $this->slots[$slot] = $connection = $this->pool[$this->guessNode($slot)]; + + return $connection; + } + + /** + * Returns the connection associated to the specified slot. + * + * @param int $slot Slot ID. + * @return SingleConnectionInterface + */ + public function getConnectionBySlot($slot) + { + if ($slot < 0x0000 || $slot > 0x3FFF) { + throw new \OutOfBoundsException("Invalid slot value [$slot]"); + } + + if (isset($this->slots[$slot])) { + return $this->slots[$slot]; + } + + return $this->pool[$this->guessNode($slot)]; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionId) + { + return isset($this->pool[$connectionId]) ? $this->pool[$connectionId] : null; + } + + /** + * Tries guessing the correct node associated to the given slot using a precalculated + * slots map or the same logic used by redis-trib to initialize a redis cluster. + * + * @param int $slot Slot ID. + * @return string + */ + protected function guessNode($slot) + { + if (!isset($this->slotsMap)) { + $this->buildSlotsMap(); + } + + if (isset($this->slotsMap[$slot])) { + return $this->slotsMap[$slot]; + } + + $index = min((int) ($slot / $this->slotsPerNode), count($this->pool) - 1); + $nodes = array_keys($this->pool); + + return $nodes[$index]; + } + + /** + * Handles -MOVED or -ASK replies by re-executing the command on the server + * specified by the Redis reply. + * + * @param CommandInterface $command Command that generated the -MOVE or -ASK reply. + * @param string $request Type of request (either 'MOVED' or 'ASK'). + * @param string $details Parameters of the MOVED/ASK request. + * @return mixed + */ + protected function onMoveRequest(CommandInterface $command, $request, $details) + { + list($slot, $host) = explode(' ', $details, 2); + $connection = $this->getConnectionById($host); + + if (!isset($connection)) { + $parameters = array('host' => null, 'port' => null); + list($parameters['host'], $parameters['port']) = explode(':', $host, 2); + $connection = $this->connections->create($parameters); + } + + switch ($request) { + case 'MOVED': + $this->move($connection, $slot); + return $this->executeCommand($command); + + case 'ASK': + return $connection->executeCommand($command); + + default: + throw new ClientException("Unexpected request type for a move request: $request"); + } + } + + /** + * Assign the connection instance to a new slot and adds it to the + * pool if the connection was not already part of the pool. + * + * @param SingleConnectionInterface $connection Connection instance + * @param int $slot Target slot. + */ + protected function move(SingleConnectionInterface $connection, $slot) + { + $this->pool[(string) $connection] = $connection; + $this->slots[(int) $slot] = $connection; + } + + /** + * Returns the underlying command hash strategy used to hash + * commands by their keys. + * + * @return CommandHashStrategyInterface + */ + public function getCommandHashStrategy() + { + return $this->strategy; + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->pool); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator(array_values($this->pool)); + } + + /** + * Handles -ERR replies from Redis. + * + * @param CommandInterface $command Command that generated the -ERR reply. + * @param ResponseErrorInterface $error Redis error reply object. + * @return mixed + */ + protected function handleServerError(CommandInterface $command, ResponseErrorInterface $error) + { + list($type, $details) = explode(' ', $error->getMessage(), 2); + + switch ($type) { + case 'MOVED': + case 'ASK': + return $this->onMoveRequest($command, $type, $details); + + default: + return $error; + } + } + + /** + * {@inheritdoc} + */ + public function writeCommand(CommandInterface $command) + { + $this->getConnection($command)->writeCommand($command); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->getConnection($command)->readResponse($command); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $connection = $this->getConnection($command); + $reply = $connection->executeCommand($command); + + if ($reply instanceof ResponseErrorInterface) { + return $this->handleServerError($command, $reply); + } + + return $reply; + } +} diff --git a/vendor/Predis/Connection/ReplicationConnectionInterface.php b/vendor/Predis/Connection/ReplicationConnectionInterface.php new file mode 100644 index 0000000..7142dfa --- /dev/null +++ b/vendor/Predis/Connection/ReplicationConnectionInterface.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +/** + * Defines a group of Redis servers in a master/slave replication configuration. + * + * @author Daniele Alessandri + */ +interface ReplicationConnectionInterface extends AggregatedConnectionInterface +{ + /** + * Switches the internal connection object being used. + * + * @param string $connection Alias of a connection + */ + public function switchTo($connection); + + /** + * Retrieves the connection object currently being used. + * + * @return SingleConnectionInterface + */ + public function getCurrent(); + + /** + * Retrieves the connection object to the master Redis server. + * + * @return SingleConnectionInterface + */ + public function getMaster(); + + /** + * Retrieves a list of connection objects to slaves Redis servers. + * + * @return SingleConnectionInterface + */ + public function getSlaves(); +} diff --git a/vendor/Predis/Connection/SingleConnectionInterface.php b/vendor/Predis/Connection/SingleConnectionInterface.php new file mode 100644 index 0000000..9b080c2 --- /dev/null +++ b/vendor/Predis/Connection/SingleConnectionInterface.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\Command\CommandInterface; + +/** + * Defines a connection object used to communicate with a single Redis server. + * + * @author Daniele Alessandri + */ +interface SingleConnectionInterface extends ConnectionInterface +{ + /** + * Returns a string representation of the connection. + * + * @return string + */ + public function __toString(); + + /** + * Returns the underlying resource used to communicate with a Redis server. + * + * @return mixed + */ + public function getResource(); + + /** + * Gets the parameters used to initialize the connection object. + * + * @return ConnectionParametersInterface + */ + public function getParameters(); + + /** + * Pushes the instance of a Redis command to the queue of commands executed + * when the actual connection to a server is estabilished. + * + * @param CommandInterface $command Instance of a Redis command. + */ + public function pushInitCommand(CommandInterface $command); + + /** + * Reads a reply from the server. + * + * @return mixed + */ + public function read(); +} diff --git a/vendor/Predis/Connection/StreamConnection.php b/vendor/Predis/Connection/StreamConnection.php new file mode 100644 index 0000000..4271f7e --- /dev/null +++ b/vendor/Predis/Connection/StreamConnection.php @@ -0,0 +1,305 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\ResponseError; +use Predis\ResponseQueued; +use Predis\Command\CommandInterface; +use Predis\Iterator\MultiBulkResponseSimple; + +/** + * Standard connection to Redis servers implemented on top of PHP's streams. + * The connection parameters supported by this class are: + * + * - scheme: it can be either 'tcp' or 'unix'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - timeout: timeout to perform the connection. + * - read_write_timeout: timeout of read / write operations. + * - async_connect: performs the connection asynchronously. + * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing. + * - persistent: the connection is left intact after a GC collection. + * - iterable_multibulk: multibulk replies treated as iterable objects. + * + * @author Daniele Alessandri + */ +class StreamConnection extends AbstractConnection +{ + private $mbiterable; + + /** + * {@inheritdoc} + */ + public function __construct(ConnectionParametersInterface $parameters) + { + $this->mbiterable = (bool) $parameters->iterable_multibulk; + + parent::__construct($parameters); + } + + /** + * Disconnects from the server and destroys the underlying resource when + * PHP's garbage collector kicks in only if the connection has not been + * marked as persistent. + */ + public function __destruct() + { + if (isset($this->parameters) && !$this->parameters->persistent) { + $this->disconnect(); + } + } + + /** + * {@inheritdoc} + */ + protected function createResource() + { + $parameters = $this->parameters; + $initializer = "{$parameters->scheme}StreamInitializer"; + + return $this->$initializer($parameters); + } + + /** + * Initializes a TCP stream resource. + * + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + * @return resource + */ + private function tcpStreamInitializer(ConnectionParametersInterface $parameters) + { + $uri = "tcp://{$parameters->host}:{$parameters->port}/"; + $flags = STREAM_CLIENT_CONNECT; + + if (isset($parameters->async_connect) && $parameters->async_connect) { + $flags |= STREAM_CLIENT_ASYNC_CONNECT; + } + if (isset($parameters->persistent) && $parameters->persistent) { + $flags |= STREAM_CLIENT_PERSISTENT; + } + + $resource = @stream_socket_client($uri, $errno, $errstr, $parameters->timeout, $flags); + + if (!$resource) { + $this->onConnectionError(trim($errstr), $errno); + } + + if (isset($parameters->read_write_timeout)) { + $rwtimeout = $parameters->read_write_timeout; + $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1; + $timeoutSeconds = floor($rwtimeout); + $timeoutUSeconds = ($rwtimeout - $timeoutSeconds) * 1000000; + stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds); + } + + if (isset($parameters->tcp_nodelay) && version_compare(PHP_VERSION, '5.4.0') >= 0) { + $socket = socket_import_stream($resource); + socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay); + } + + return $resource; + } + + /** + * Initializes a UNIX stream resource. + * + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + * @return resource + */ + private function unixStreamInitializer(ConnectionParametersInterface $parameters) + { + $uri = "unix://{$parameters->path}"; + $flags = STREAM_CLIENT_CONNECT; + + if ($parameters->persistent) { + $flags |= STREAM_CLIENT_PERSISTENT; + } + + $resource = @stream_socket_client($uri, $errno, $errstr, $parameters->timeout, $flags); + + if (!$resource) { + $this->onConnectionError(trim($errstr), $errno); + } + + return $resource; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + parent::connect(); + + if ($this->initCmds) { + $this->sendInitializationCommands(); + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->isConnected()) { + fclose($this->getResource()); + parent::disconnect(); + } + } + + /** + * Sends the initialization commands to Redis when the connection is opened. + */ + private function sendInitializationCommands() + { + foreach ($this->initCmds as $command) { + $this->writeCommand($command); + } + foreach ($this->initCmds as $command) { + $this->readResponse($command); + } + } + + /** + * Performs a write operation on the stream of the buffer containing a + * command serialized with the Redis wire protocol. + * + * @param string $buffer Redis wire protocol representation of a command. + */ + protected function writeBytes($buffer) + { + $socket = $this->getResource(); + + while (($length = strlen($buffer)) > 0) { + $written = fwrite($socket, $buffer); + + if ($length === $written) { + return; + } + if ($written === false || $written === 0) { + $this->onConnectionError('Error while writing bytes to the server'); + } + + $buffer = substr($buffer, $written); + } + } + + /** + * {@inheritdoc} + */ + public function read() + { + $socket = $this->getResource(); + $chunk = fgets($socket); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading line from the server'); + } + + $prefix = $chunk[0]; + $payload = substr($chunk, 1, -2); + + switch ($prefix) { + case '+': // inline + switch ($payload) { + case 'OK': + return true; + + case 'QUEUED': + return new ResponseQueued(); + + default: + return $payload; + } + + case '$': // bulk + $size = (int) $payload; + if ($size === -1) { + return null; + } + + $bulkData = ''; + $bytesLeft = ($size += 2); + + do { + $chunk = fread($socket, min($bytesLeft, 4096)); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading bytes from the server'); + } + + $bulkData .= $chunk; + $bytesLeft = $size - strlen($bulkData); + } while ($bytesLeft > 0); + + return substr($bulkData, 0, -2); + + case '*': // multi bulk + $count = (int) $payload; + + if ($count === -1) { + return null; + } + if ($this->mbiterable) { + return new MultiBulkResponseSimple($this, $count); + } + + $multibulk = array(); + + for ($i = 0; $i < $count; $i++) { + $multibulk[$i] = $this->read(); + } + + return $multibulk; + + case ':': // integer + return (int) $payload; + + case '-': // error + return new ResponseError($payload); + + default: + $this->onProtocolError("Unknown prefix: '$prefix'"); + } + } + + /** + * {@inheritdoc} + */ + public function writeCommand(CommandInterface $command) + { + $commandId = $command->getId(); + $arguments = $command->getArguments(); + + $cmdlen = strlen($commandId); + $reqlen = count($arguments) + 1; + + $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandId}\r\n"; + + for ($i = 0; $i < $reqlen - 1; $i++) { + $argument = $arguments[$i]; + if(is_array($argument) || is_object($argument)) $argument = serialize($argument); + $arglen = strlen($argument); + $buffer .= "\${$arglen}\r\n{$argument}\r\n"; + } + + $this->writeBytes($buffer); + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array_merge(parent::__sleep(), array('mbiterable')); + } +} diff --git a/vendor/Predis/Connection/WebdisConnection.php b/vendor/Predis/Connection/WebdisConnection.php new file mode 100644 index 0000000..4c1ea89 --- /dev/null +++ b/vendor/Predis/Connection/WebdisConnection.php @@ -0,0 +1,335 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Connection; + +use Predis\NotSupportedException; +use Predis\ResponseError; +use Predis\Command\CommandInterface; +use Predis\Connection\ConnectionException; +use Predis\Protocol\ProtocolException; + +/** + * This class implements a Predis connection that actually talks with Webdis + * instead of connecting directly to Redis. It relies on the cURL extension to + * communicate with the web server and the phpiredis extension to parse the + * protocol of the replies returned in the http response bodies. + * + * Some features are not yet available or they simply cannot be implemented: + * - Pipelining commands. + * - Publish / Subscribe. + * - MULTI / EXEC transactions (not yet supported by Webdis). + * + * The connection parameters supported by this class are: + * + * - scheme: must be 'http'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - timeout: timeout to perform the connection. + * - user: username for authentication. + * - pass: password for authentication. + * + * @link http://webd.is + * @link http://github.com/nicolasff/webdis + * @link http://github.com/seppo0010/phpiredis + * @author Daniele Alessandri + */ +class WebdisConnection implements SingleConnectionInterface +{ + const ERR_MSG_EXTENSION = 'The %s extension must be loaded in order to be able to use this connection class'; + + private $parameters; + private $resource; + private $reader; + + /** + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + */ + public function __construct(ConnectionParametersInterface $parameters) + { + $this->checkExtensions(); + + if ($parameters->scheme !== 'http') { + throw new \InvalidArgumentException("Invalid scheme: {$parameters->scheme}"); + } + + $this->parameters = $parameters; + $this->resource = $this->initializeCurl($parameters); + $this->reader = $this->initializeReader($parameters); + } + + /** + * Frees the underlying cURL and protocol reader resources when PHP's + * garbage collector kicks in. + */ + public function __destruct() + { + curl_close($this->resource); + phpiredis_reader_destroy($this->reader); + } + + /** + * Helper method used to throw on unsupported methods. + */ + private function throwNotSupportedException($function) + { + $class = __CLASS__; + throw new NotSupportedException("The method $class::$function() is not supported"); + } + + /** + * Checks if the cURL and phpiredis extensions are loaded in PHP. + */ + private function checkExtensions() + { + if (!function_exists('curl_init')) { + throw new NotSupportedException(sprintf(self::ERR_MSG_EXTENSION, 'curl')); + } + + if (!function_exists('phpiredis_reader_create')) { + throw new NotSupportedException(sprintf(self::ERR_MSG_EXTENSION, 'phpiredis')); + } + } + + /** + * Initializes cURL. + * + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + * @return resource + */ + private function initializeCurl(ConnectionParametersInterface $parameters) + { + $options = array( + CURLOPT_FAILONERROR => true, + CURLOPT_CONNECTTIMEOUT_MS => $parameters->timeout * 1000, + CURLOPT_URL => "{$parameters->scheme}://{$parameters->host}:{$parameters->port}", + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_POST => true, + CURLOPT_WRITEFUNCTION => array($this, 'feedReader'), + ); + + if (isset($parameters->user, $parameters->pass)) { + $options[CURLOPT_USERPWD] = "{$parameters->user}:{$parameters->pass}"; + } + + curl_setopt_array($resource = curl_init(), $options); + + return $resource; + } + + /** + * Initializes phpiredis' protocol reader. + * + * @param ConnectionParametersInterface $parameters Parameters used to initialize the connection. + * @return resource + */ + private function initializeReader(ConnectionParametersInterface $parameters) + { + $reader = phpiredis_reader_create(); + + phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); + phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); + + return $reader; + } + + /** + * Gets the handler used by the protocol reader to handle status replies. + * + * @return \Closure + */ + protected function getStatusHandler() + { + return function ($payload) { + return $payload === 'OK' ? true : $payload; + }; + } + + /** + * Gets the handler used by the protocol reader to handle Redis errors. + * + * @return \Closure + */ + protected function getErrorHandler() + { + return function ($errorMessage) { + return new ResponseError($errorMessage); + }; + } + + /** + * Feeds phpredis' reader resource with the data read from the network. + * + * @param resource $resource Reader resource. + * @param string $buffer Buffer with the reply read from the network. + * @return int + */ + protected function feedReader($resource, $buffer) + { + phpiredis_reader_feed($this->reader, $buffer); + + return strlen($buffer); + } + + /** + * {@inheritdoc} + */ + public function connect() + { + // NOOP + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + // NOOP + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return true; + } + + /** + * Checks if the specified command is supported by this connection class. + * + * @param CommandInterface $command The instance of a Redis command. + * @return string + */ + protected function getCommandId(CommandInterface $command) + { + switch (($commandId = $command->getId())) { + case 'AUTH': + case 'SELECT': + case 'MULTI': + case 'EXEC': + case 'WATCH': + case 'UNWATCH': + case 'DISCARD': + case 'MONITOR': + throw new NotSupportedException("Disabled command: {$command->getId()}"); + + default: + return $commandId; + } + } + + /** + * {@inheritdoc} + */ + public function writeCommand(CommandInterface $command) + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $resource = $this->resource; + $commandId = $this->getCommandId($command); + + if ($arguments = $command->getArguments()) { + $arguments = implode('/', array_map('urlencode', $arguments)); + $serializedCommand = "$commandId/$arguments.raw"; + } else { + $serializedCommand = "$commandId.raw"; + } + + curl_setopt($resource, CURLOPT_POSTFIELDS, $serializedCommand); + + if (curl_exec($resource) === false) { + $error = curl_error($resource); + $errno = curl_errno($resource); + throw new ConnectionException($this, trim($error), $errno); + } + + if (phpiredis_reader_get_state($this->reader) !== PHPIREDIS_READER_STATE_COMPLETE) { + throw new ProtocolException($this, phpiredis_reader_get_error($this->reader)); + } + + return phpiredis_reader_get_reply($this->reader); + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function pushInitCommand(CommandInterface $command) + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function read() + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return "{$this->parameters->host}:{$this->parameters->port}"; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('parameters'); + } + + /** + * {@inheritdoc} + */ + public function __wakeup() + { + $this->checkExtensions(); + $parameters = $this->getParameters(); + + $this->resource = $this->initializeCurl($parameters); + $this->reader = $this->initializeReader($parameters); + } +} diff --git a/vendor/Predis/ExecutableContextInterface.php b/vendor/Predis/ExecutableContextInterface.php new file mode 100644 index 0000000..9b0017a --- /dev/null +++ b/vendor/Predis/ExecutableContextInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Defines the interface of a basic client object or abstraction that + * can send commands to Redis. + * + * @author Daniele Alessandri + */ +interface ExecutableContextInterface +{ + /** + * Starts the execution of the context. + * + * @param mixed $callable Optional callback for execution. + * @return array + */ + public function execute($callable = null); +} diff --git a/vendor/Predis/Helpers.php b/vendor/Predis/Helpers.php new file mode 100644 index 0000000..33fa33a --- /dev/null +++ b/vendor/Predis/Helpers.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Defines a few helper methods. + * + * @author Daniele Alessandri + * @deprecated Deprecated since v0.8.3. + */ +class Helpers +{ + /** + * Offers a generic and reusable method to handle exceptions generated by + * a connection object. + * + * @deprecated Deprecated since v0.8.3 - moved in Predis\CommunicationException::handle() + * @param CommunicationException $exception Exception. + */ + public static function onCommunicationException(CommunicationException $exception) + { + if ($exception->shouldResetConnection()) { + $connection = $exception->getConnection(); + + if ($connection->isConnected()) { + $connection->disconnect(); + } + } + + throw $exception; + } + + /** + * Normalizes the arguments array passed to a Redis command. + * + * @deprecated Deprecated since v0.8.3 - moved in Predis\Command\AbstractCommand::normalizeArguments() + * @param array $arguments Arguments for a command. + * @return array + */ + public static function filterArrayArguments(Array $arguments) + { + if (count($arguments) === 1 && is_array($arguments[0])) { + return $arguments[0]; + } + + return $arguments; + } + + /** + * Normalizes the arguments array passed to a variadic Redis command. + * + * @deprecated Deprecated since v0.8.3 - moved in Predis\Command\AbstractCommand::normalizeVariadic() + * @param array $arguments Arguments for a command. + * @return array + */ + public static function filterVariadicValues(Array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + return array_merge(array($arguments[0]), $arguments[1]); + } + + return $arguments; + } +} diff --git a/vendor/Predis/Iterator/MultiBulkResponse.php b/vendor/Predis/Iterator/MultiBulkResponse.php new file mode 100644 index 0000000..a7bbad8 --- /dev/null +++ b/vendor/Predis/Iterator/MultiBulkResponse.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Iterator; + +use Predis\ResponseObjectInterface; + +/** + * Iterator that abstracts the access to multibulk replies and allows + * them to be consumed by user's code in a streaming fashion. + * + * @author Daniele Alessandri + */ +abstract class MultiBulkResponse implements \Iterator, \Countable, ResponseObjectInterface +{ + protected $position; + protected $current; + protected $replySize; + + /** + * {@inheritdoc} + */ + public function rewind() + { + // NOOP + } + + /** + * {@inheritdoc} + */ + public function current() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + if (++$this->position < $this->replySize) { + $this->current = $this->getValue(); + } + + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function valid() + { + return $this->position < $this->replySize; + } + + /** + * Returns the number of items of the whole multibulk reply. + * + * This method should be used to get the size of the current multibulk + * reply without using iterator_count, which actually consumes the + * iterator to calculate the size (rewinding is not supported). + * + * @return int + */ + public function count() + { + return $this->replySize; + } + + /** + * Returns the current position of the iterator. + * + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + protected abstract function getValue(); +} diff --git a/vendor/Predis/Iterator/MultiBulkResponseSimple.php b/vendor/Predis/Iterator/MultiBulkResponseSimple.php new file mode 100644 index 0000000..2dee722 --- /dev/null +++ b/vendor/Predis/Iterator/MultiBulkResponseSimple.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Iterator; + +use Predis\Connection\SingleConnectionInterface; + +/** + * Streams a multibulk reply. + * + * @author Daniele Alessandri + */ +class MultiBulkResponseSimple extends MultiBulkResponse +{ + private $connection; + + /** + * @param SingleConnectionInterface $connection Connection to Redis. + * @param int $size Number of elements of the multibulk reply. + */ + public function __construct(SingleConnectionInterface $connection, $size) + { + $this->connection = $connection; + $this->position = 0; + $this->current = $size > 0 ? $this->getValue() : null; + $this->replySize = $size; + } + + /** + * Handles the synchronization of the client with the Redis protocol + * then PHP's garbage collector kicks in (e.g. then the iterator goes + * out of the scope of a foreach). + */ + public function __destruct() + { + $this->sync(true); + } + + /** + * Synchronizes the client with the queued elements that have not been + * read from the connection by consuming the rest of the multibulk reply, + * or simply by dropping the connection. + * + * @param Boolean $drop True to synchronize the client by dropping the connection. + * False to synchronize the client by consuming the multibulk reply. + */ + public function sync($drop = false) + { + if ($drop == true) { + if ($this->valid()) { + $this->position = $this->replySize; + $this->connection->disconnect(); + } + } else { + while ($this->valid()) { + $this->next(); + } + } + } + + /** + * Reads the next item of the multibulk reply from the server. + * + * @return mixed + */ + protected function getValue() + { + return $this->connection->read(); + } + + /** + * Returns an iterator that reads the multi-bulk response as + * list of tuples. + * + * @return MultiBulkResponseTuple + */ + public function asTuple() + { + return new MultiBulkResponseTuple($this); + } +} diff --git a/vendor/Predis/Iterator/MultiBulkResponseTuple.php b/vendor/Predis/Iterator/MultiBulkResponseTuple.php new file mode 100644 index 0000000..39d7828 --- /dev/null +++ b/vendor/Predis/Iterator/MultiBulkResponseTuple.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Iterator; + +/** + * Abstracts the access to a streamable list of tuples represented + * as a multibulk reply that alternates keys and values. + * + * @author Daniele Alessandri + */ +class MultiBulkResponseTuple extends MultiBulkResponse implements \OuterIterator +{ + private $iterator; + + /** + * @param MultiBulkResponseSimple $iterator Multibulk reply iterator. + */ + public function __construct(MultiBulkResponseSimple $iterator) + { + $this->checkPreconditions($iterator); + + $virtualSize = count($iterator) / 2; + $this->iterator = $iterator; + $this->position = $iterator->getPosition(); + $this->current = $virtualSize > 0 ? $this->getValue() : null; + $this->replySize = $virtualSize; + } + + /** + * Checks for valid preconditions. + * + * @param MultiBulkResponseSimple $iterator Multibulk reply iterator. + */ + protected function checkPreconditions(MultiBulkResponseSimple $iterator) + { + if ($iterator->getPosition() !== 0) { + throw new \RuntimeException('Cannot initialize a tuple iterator with an already initiated iterator'); + } + + if (($size = count($iterator)) % 2 !== 0) { + throw new \UnexpectedValueException("Invalid reply size for a tuple iterator [$size]"); + } + } + + /** + * {@inheritdoc} + */ + public function getInnerIterator() + { + return $this->iterator; + } + + /** + * {@inheritdoc} + */ + public function __destruct() + { + $this->iterator->sync(true); + } + + /** + * {@inheritdoc} + */ + protected function getValue() + { + $k = $this->iterator->current(); + $this->iterator->next(); + + $v = $this->iterator->current(); + $this->iterator->next(); + + return array($k, $v); + } +} diff --git a/vendor/Predis/Monitor/MonitorContext.php b/vendor/Predis/Monitor/MonitorContext.php new file mode 100644 index 0000000..a053b0b --- /dev/null +++ b/vendor/Predis/Monitor/MonitorContext.php @@ -0,0 +1,165 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Monitor; + +use Predis\ClientInterface; +use Predis\NotSupportedException; +use Predis\Connection\AggregatedConnectionInterface; + +/** + * Client-side abstraction of a Redis MONITOR context. + * + * @author Daniele Alessandri + */ +class MonitorContext implements \Iterator +{ + private $client; + private $isValid; + private $position; + + /** + * @param ClientInterface $client Client instance used by the context. + */ + public function __construct(ClientInterface $client) + { + $this->checkCapabilities($client); + $this->client = $client; + $this->openContext(); + } + + /** + * Automatically closes the context when PHP's garbage collector kicks in. + */ + public function __destruct() + { + $this->closeContext(); + } + + /** + * Checks if the passed client instance satisfies the required conditions + * needed to initialize a monitor context. + * + * @param ClientInterface $client Client instance used by the context. + */ + private function checkCapabilities(ClientInterface $client) + { + if ($client->getConnection() instanceof AggregatedConnectionInterface) { + throw new NotSupportedException('Cannot initialize a monitor context when using aggregated connections'); + } + if ($client->getProfile()->supportsCommand('monitor') === false) { + throw new NotSupportedException('The current profile does not support the MONITOR command'); + } + } + + /** + * Initializes the context and sends the MONITOR command to the server. + */ + protected function openContext() + { + $this->isValid = true; + $monitor = $this->client->createCommand('monitor'); + $this->client->executeCommand($monitor); + } + + /** + * Closes the context. Internally this is done by disconnecting from server + * since there is no way to terminate the stream initialized by MONITOR. + */ + public function closeContext() + { + $this->client->disconnect(); + $this->isValid = false; + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + // NOOP + } + + /** + * Returns the last message payload retrieved from the server. + * + * @return Object + */ + public function current() + { + return $this->getValue(); + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + $this->position++; + } + + /** + * Checks if the the context is still in a valid state to continue. + * + * @return Boolean + */ + public function valid() + { + return $this->isValid; + } + + /** + * Waits for a new message from the server generated by MONITOR and + * returns it when available. + * + * @return Object + */ + private function getValue() + { + $database = 0; + $client = null; + $event = $this->client->getConnection()->read(); + + $callback = function ($matches) use (&$database, &$client) { + if (2 === $count = count($matches)) { + // Redis <= 2.4 + $database = (int) $matches[1]; + } + + if (4 === $count) { + // Redis >= 2.6 + $database = (int) $matches[2]; + $client = $matches[3]; + } + + return ' '; + }; + + $event = preg_replace_callback('/ \(db (\d+)\) | \[(\d+) (.*?)\] /', $callback, $event, 1); + @list($timestamp, $command, $arguments) = explode(' ', $event, 3); + + return (object) array( + 'timestamp' => (float) $timestamp, + 'database' => $database, + 'client' => $client, + 'command' => substr($command, 1, -1), + 'arguments' => $arguments, + ); + } +} diff --git a/vendor/Predis/NotSupportedException.php b/vendor/Predis/NotSupportedException.php new file mode 100644 index 0000000..631b820 --- /dev/null +++ b/vendor/Predis/NotSupportedException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Exception class generated when trying to use features not + * supported by certain classes or abstractions. + * + * @author Daniele Alessandri + */ +class NotSupportedException extends PredisException +{ +} diff --git a/vendor/Predis/Option/AbstractOption.php b/vendor/Predis/Option/AbstractOption.php new file mode 100644 index 0000000..92506cd --- /dev/null +++ b/vendor/Predis/Option/AbstractOption.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +/** + * Implements a client option. + * + * @author Daniele Alessandri + */ +abstract class AbstractOption implements OptionInterface +{ + /** + * {@inheritdoc} + */ + public function filter(ClientOptionsInterface $options, $value) + { + return $value; + } + + /** + * {@inheritdoc} + */ + public function getDefault(ClientOptionsInterface $options) + { + return null; + } + + /** + * {@inheritdoc} + */ + public function __invoke(ClientOptionsInterface $options, $value) + { + if (isset($value)) { + return $this->filter($options, $value); + } + + return $this->getDefault($options); + } +} diff --git a/vendor/Predis/Option/ClientCluster.php b/vendor/Predis/Option/ClientCluster.php new file mode 100644 index 0000000..dff626f --- /dev/null +++ b/vendor/Predis/Option/ClientCluster.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +use Predis\Connection\ClusterConnectionInterface; +use Predis\Connection\PredisCluster; +use Predis\Connection\RedisCluster; + +/** + * Option class that returns a connection cluster to be used by a client. + * + * @author Daniele Alessandri + */ +class ClientCluster extends AbstractOption +{ + /** + * Checks if the specified value is a valid instance of ClusterConnectionInterface. + * + * @param ClusterConnectionInterface $cluster Instance of a connection cluster. + * @return ClusterConnectionInterface + */ + protected function checkInstance($cluster) + { + if (!$cluster instanceof ClusterConnectionInterface) { + throw new \InvalidArgumentException('Instance of Predis\Connection\ClusterConnectionInterface expected'); + } + + return $cluster; + } + + /** + * {@inheritdoc} + */ + public function filter(ClientOptionsInterface $options, $value) + { + if (is_callable($value)) { + return $this->checkInstance(call_user_func($value, $options, $this)); + } + + $initializer = $this->getInitializer($options, $value); + + return $this->checkInstance($initializer()); + } + + /** + * Returns an initializer for the specified FQN or type. + * + * @param string $fqnOrType Type of cluster or FQN of a class implementing ClusterConnectionInterface. + * @param ClientOptionsInterface $options Instance of the client options. + * @return \Closure + */ + protected function getInitializer(ClientOptionsInterface $options, $fqnOrType) + { + switch ($fqnOrType) { + case 'predis': + return function () { + return new PredisCluster(); + }; + + case 'redis': + return function () use ($options) { + $connectionFactory = $options->connections; + $cluster = new RedisCluster($connectionFactory); + + return $cluster; + }; + + default: + // TODO: we should not even allow non-string values here. + if (is_string($fqnOrType) && !class_exists($fqnOrType)) { + throw new \InvalidArgumentException("Class $fqnOrType does not exist"); + } + return function () use ($fqnOrType) { + return new $fqnOrType(); + }; + } + } + + /** + * {@inheritdoc} + */ + public function getDefault(ClientOptionsInterface $options) + { + return new PredisCluster(); + } +} diff --git a/vendor/Predis/Option/ClientConnectionFactory.php b/vendor/Predis/Option/ClientConnectionFactory.php new file mode 100644 index 0000000..e671e52 --- /dev/null +++ b/vendor/Predis/Option/ClientConnectionFactory.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +use Predis\Connection\ConnectionFactory; +use Predis\Connection\ConnectionFactoryInterface; + +/** + * Option class that returns a connection factory to be used by a client. + * + * @author Daniele Alessandri + */ +class ClientConnectionFactory extends AbstractOption +{ + /** + * {@inheritdoc} + */ + public function filter(ClientOptionsInterface $options, $value) + { + if ($value instanceof ConnectionFactoryInterface) { + return $value; + } + + if (is_array($value)) { + $factory = $this->getDefault($options); + + foreach ($value as $scheme => $initializer) { + $factory->define($scheme, $initializer); + } + + return $factory; + } + + if (is_callable($value)) { + $factory = call_user_func($value, $options, $this); + + if (!$factory instanceof ConnectionFactoryInterface) { + throw new \InvalidArgumentException('Instance of Predis\Connection\ConnectionFactoryInterface expected'); + } + + return $factory; + } + + if (@class_exists($value)) { + $factory = new $value(); + + if (!$factory instanceof ConnectionFactoryInterface) { + throw new \InvalidArgumentException("Class $value must be an instance of Predis\Connection\ConnectionFactoryInterface"); + } + + return $factory; + } + + throw new \InvalidArgumentException('Invalid value for the connections option'); + } + + /** + * {@inheritdoc} + */ + public function getDefault(ClientOptionsInterface $options) + { + return new ConnectionFactory($options->profile); + } +} diff --git a/vendor/Predis/Option/ClientExceptions.php b/vendor/Predis/Option/ClientExceptions.php new file mode 100644 index 0000000..ad422da --- /dev/null +++ b/vendor/Predis/Option/ClientExceptions.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +/** + * Option class used to specify if the client should throw server exceptions. + * + * @author Daniele Alessandri + */ +class ClientExceptions extends AbstractOption +{ + /** + * {@inheritdoc} + */ + public function filter(ClientOptionsInterface $options, $value) + { + return (bool) $value; + } + + /** + * {@inheritdoc} + */ + public function getDefault(ClientOptionsInterface $options) + { + return true; + } +} diff --git a/vendor/Predis/Option/ClientOptions.php b/vendor/Predis/Option/ClientOptions.php new file mode 100644 index 0000000..d835524 --- /dev/null +++ b/vendor/Predis/Option/ClientOptions.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +/** + * Class that manages client options with filtering and conversion. + * + * @author Daniele Alessandri + */ +class ClientOptions implements ClientOptionsInterface +{ + private $handlers; + private $defined; + private $options = array(); + + /** + * @param array $options Array of client options. + */ + public function __construct(Array $options = array()) + { + $this->handlers = $this->initialize($options); + $this->defined = array_fill_keys(array_keys($options), true); + } + + /** + * Ensures that the default options are initialized. + * + * @return array + */ + protected function getDefaultOptions() + { + return array( + 'profile' => new ClientProfile(), + 'connections' => new ClientConnectionFactory(), + 'cluster' => new ClientCluster(), + 'replication' => new ClientReplication(), + 'prefix' => new ClientPrefix(), + 'exceptions' => new ClientExceptions(), + ); + } + + /** + * Initializes client options handlers. + * + * @param array $options List of client options values. + * @return array + */ + protected function initialize(Array $options) + { + $handlers = $this->getDefaultOptions(); + + foreach ($options as $option => $value) { + if (isset($handlers[$option])) { + $handler = $handlers[$option]; + $handlers[$option] = function ($options) use ($handler, $value) { + return $handler->filter($options, $value); + }; + } else { + $this->options[$option] = $value; + } + } + + return $handlers; + } + + /** + * Checks if the specified option is set. + * + * @param string $option Name of the option. + * @return Boolean + */ + public function __isset($option) + { + return isset($this->defined[$option]); + } + + /** + * Returns the value of the specified option. + * + * @param string $option Name of the option. + * @return mixed + */ + public function __get($option) + { + if (isset($this->options[$option])) { + return $this->options[$option]; + } + + if (isset($this->handlers[$option])) { + $handler = $this->handlers[$option]; + $value = $handler instanceof OptionInterface ? $handler->getDefault($this) : $handler($this); + $this->options[$option] = $value; + + return $value; + } + } + + /** + * Returns the default value for the specified option. + * + * @param string|OptionInterface $option Name or instance of the option. + * @return mixed + */ + public function getDefault($option) + { + if ($option instanceof OptionInterface) { + return $option->getDefault($this); + } + + $options = $this->getDefaultOptions(); + + if (isset($options[$option])) { + return $options[$option]->getDefault($this); + } + } +} diff --git a/vendor/Predis/Option/ClientOptionsInterface.php b/vendor/Predis/Option/ClientOptionsInterface.php new file mode 100644 index 0000000..543e18a --- /dev/null +++ b/vendor/Predis/Option/ClientOptionsInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +/** + * Marker interface defining a client options bag. + * + * @author Daniele Alessandri + */ +interface ClientOptionsInterface +{ +} diff --git a/vendor/Predis/Option/ClientPrefix.php b/vendor/Predis/Option/ClientPrefix.php new file mode 100644 index 0000000..01a166a --- /dev/null +++ b/vendor/Predis/Option/ClientPrefix.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +use Predis\Command\Processor\KeyPrefixProcessor; + +/** + * Option class that handles the prefixing of keys in commands. + * + * @author Daniele Alessandri + */ +class ClientPrefix extends AbstractOption +{ + /** + * {@inheritdoc} + */ + public function filter(ClientOptionsInterface $options, $value) + { + return new KeyPrefixProcessor($value); + } +} diff --git a/vendor/Predis/Option/ClientProfile.php b/vendor/Predis/Option/ClientProfile.php new file mode 100644 index 0000000..660066d --- /dev/null +++ b/vendor/Predis/Option/ClientProfile.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +use Predis\Profile\ServerProfile; +use Predis\Profile\ServerProfileInterface; + +/** + * Option class that handles server profiles to be used by a client. + * + * @author Daniele Alessandri + */ +class ClientProfile extends AbstractOption +{ + /** + * {@inheritdoc} + */ + public function filter(ClientOptionsInterface $options, $value) + { + if (is_string($value)) { + $value = ServerProfile::get($value); + + if (isset($options->prefix)) { + $value->setProcessor($options->prefix); + } + } + + if (is_callable($value)) { + $value = call_user_func($value, $options, $this); + } + + if (!$value instanceof ServerProfileInterface) { + throw new \InvalidArgumentException('Invalid value for the profile option'); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getDefault(ClientOptionsInterface $options) + { + $profile = ServerProfile::getDefault(); + + if (isset($options->prefix)) { + $profile->setProcessor($options->prefix); + } + + return $profile; + } +} diff --git a/vendor/Predis/Option/ClientReplication.php b/vendor/Predis/Option/ClientReplication.php new file mode 100644 index 0000000..61e0268 --- /dev/null +++ b/vendor/Predis/Option/ClientReplication.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +use Predis\Connection\MasterSlaveReplication; +use Predis\Connection\ReplicationConnectionInterface; + +/** + * Option class that returns a replication connection be used by a client. + * + * @author Daniele Alessandri + */ +class ClientReplication extends AbstractOption +{ + /** + * Checks if the specified value is a valid instance of ReplicationConnectionInterface. + * + * @param ReplicationConnectionInterface $connection Instance of a replication connection. + * @return ReplicationConnectionInterface + */ + protected function checkInstance($connection) + { + if (!$connection instanceof ReplicationConnectionInterface) { + throw new \InvalidArgumentException('Instance of Predis\Connection\ReplicationConnectionInterface expected'); + } + + return $connection; + } + + /** + * {@inheritdoc} + */ + public function filter(ClientOptionsInterface $options, $value) + { + if (is_callable($value)) { + $connection = call_user_func($value, $options, $this); + + if (!$connection instanceof ReplicationConnectionInterface) { + throw new \InvalidArgumentException('Instance of Predis\Connection\ReplicationConnectionInterface expected'); + } + + return $connection; + } + + if (is_string($value)) { + if (!class_exists($value)) { + throw new \InvalidArgumentException("Class $value does not exist"); + } + + if (!($connection = new $value()) instanceof ReplicationConnectionInterface) { + throw new \InvalidArgumentException('Instance of Predis\Connection\ReplicationConnectionInterface expected'); + } + + return $connection; + } + + if ($value == true) { + return $this->getDefault($options); + } + } + + /** + * {@inheritdoc} + */ + public function getDefault(ClientOptionsInterface $options) + { + return new MasterSlaveReplication(); + } +} diff --git a/vendor/Predis/Option/CustomOption.php b/vendor/Predis/Option/CustomOption.php new file mode 100644 index 0000000..067907b --- /dev/null +++ b/vendor/Predis/Option/CustomOption.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +/** + * Implements a generic class used to dynamically define a client option. + * + * @author Daniele Alessandri + */ +class CustomOption implements OptionInterface +{ + private $filter; + private $default; + + /** + * @param array $options List of options + */ + public function __construct(Array $options = array()) + { + $this->filter = $this->ensureCallable($options, 'filter'); + $this->default = $this->ensureCallable($options, 'default'); + } + + /** + * Checks if the specified value in the options array is a callable object. + * + * @param array $options Array of options + * @param string $key Target option. + */ + private function ensureCallable($options, $key) + { + if (!isset($options[$key])) { + return; + } + + if (is_callable($callable = $options[$key])) { + return $callable; + } + + throw new \InvalidArgumentException("The parameter $key must be callable"); + } + + /** + * {@inheritdoc} + */ + public function filter(ClientOptionsInterface $options, $value) + { + if (isset($value)) { + if ($this->filter === null) { + return $value; + } + + return call_user_func($this->filter, $options, $value); + } + } + + /** + * {@inheritdoc} + */ + public function getDefault(ClientOptionsInterface $options) + { + if (!isset($this->default)) { + return; + } + + return call_user_func($this->default, $options); + } + + /** + * {@inheritdoc} + */ + public function __invoke(ClientOptionsInterface $options, $value) + { + if (isset($value)) { + return $this->filter($options, $value); + } + + return $this->getDefault($options); + } +} diff --git a/vendor/Predis/Option/OptionInterface.php b/vendor/Predis/Option/OptionInterface.php new file mode 100644 index 0000000..415b8c1 --- /dev/null +++ b/vendor/Predis/Option/OptionInterface.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Option; + +/** + * Interface that defines a client option. + * + * @author Daniele Alessandri + */ +interface OptionInterface +{ + /** + * Filters (and optionally converts) the passed value. + * + * @param mixed $value Input value. + * @return mixed + */ + public function filter(ClientOptionsInterface $options, $value); + + /** + * Returns a default value for the option. + * + * @param mixed $value Input value. + * @return mixed + */ + public function getDefault(ClientOptionsInterface $options); + + /** + * Filters a value and, if no value is specified, returns + * the default one defined by the option. + * + * @param mixed $value Input value. + * @return mixed + */ + public function __invoke(ClientOptionsInterface $options, $value); +} diff --git a/vendor/Predis/Pipeline/FireAndForgetExecutor.php b/vendor/Predis/Pipeline/FireAndForgetExecutor.php new file mode 100644 index 0000000..3e04799 --- /dev/null +++ b/vendor/Predis/Pipeline/FireAndForgetExecutor.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use SplQueue; +use Predis\Connection\ConnectionInterface; +use Predis\Connection\ReplicationConnectionInterface; + +/** + * Implements a pipeline executor strategy that writes a list of commands to + * the connection object but does not read back their replies. + * + * @author Daniele Alessandri + */ +class FireAndForgetExecutor implements PipelineExecutorInterface +{ + /** + * Allows the pipeline executor to perform operations on the + * connection before starting to execute the commands stored + * in the pipeline. + * + * @param ConnectionInterface $connection Connection instance. + */ + protected function checkConnection(ConnectionInterface $connection) + { + if ($connection instanceof ReplicationConnectionInterface) { + $connection->switchTo('master'); + } + } + + /** + * {@inheritdoc} + */ + public function execute(ConnectionInterface $connection, SplQueue $commands) + { + $this->checkConnection($connection); + + while (!$commands->isEmpty()) { + $connection->writeCommand($commands->dequeue()); + } + + $connection->disconnect(); + + return array(); + } +} diff --git a/vendor/Predis/Pipeline/MultiExecExecutor.php b/vendor/Predis/Pipeline/MultiExecExecutor.php new file mode 100644 index 0000000..d9550bd --- /dev/null +++ b/vendor/Predis/Pipeline/MultiExecExecutor.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use Iterator; +use SplQueue; +use Predis\ClientException; +use Predis\ResponseErrorInterface; +use Predis\ResponseObjectInterface; +use Predis\ServerException; +use Predis\Connection\ConnectionInterface; +use Predis\Connection\SingleConnectionInterface; +use Predis\Profile\ServerProfile; +use Predis\Profile\ServerProfileInterface; + +/** + * Implements a pipeline executor that wraps the whole pipeline + * in a MULTI / EXEC context to make sure that it is executed + * correctly. + * + * @author Daniele Alessandri + */ +class MultiExecExecutor implements PipelineExecutorInterface +{ + protected $profile; + + /** + * + */ + public function __construct(ServerProfileInterface $profile = null) + { + $this->setProfile($profile ?: ServerProfile::getDefault()); + } + + /** + * Allows the pipeline executor to perform operations on the + * connection before starting to execute the commands stored + * in the pipeline. + * + * @param ConnectionInterface $connection Connection instance. + */ + protected function checkConnection(ConnectionInterface $connection) + { + if (!$connection instanceof SingleConnectionInterface) { + $class = __CLASS__; + throw new ClientException("$class can be used only with single connections"); + } + } + + /** + * {@inheritdoc} + */ + public function execute(ConnectionInterface $connection, SplQueue $commands) + { + $this->checkConnection($connection); + + $cmd = $this->profile->createCommand('multi'); + $connection->executeCommand($cmd); + + foreach ($commands as $command) { + $connection->writeCommand($command); + } + + foreach ($commands as $command) { + $response = $connection->readResponse($command); + + if ($response instanceof ResponseErrorInterface) { + $cmd = $this->profile->createCommand('discard'); + $connection->executeCommand($cmd); + + throw new ServerException($response->getMessage()); + } + } + + $cmd = $this->profile->createCommand('exec'); + $responses = $connection->executeCommand($cmd); + + if (!isset($responses)) { + throw new ClientException('The underlying transaction has been aborted by the server'); + } + + if (count($responses) !== count($commands)) { + throw new ClientException("Invalid number of replies [expected: ".count($commands)." - actual: ".count($responses)."]"); + } + + $consumer = $responses instanceof Iterator ? 'consumeIteratorResponse' : 'consumeArrayResponse'; + + return $this->$consumer($commands, $responses); + } + + /** + * Consumes an iterator response returned by EXEC. + * + * @param SplQueue $commands Pipelined commands + * @param Iterator $responses Responses returned by EXEC. + * @return array + */ + protected function consumeIteratorResponse(SplQueue $commands, Iterator $responses) + { + $values = array(); + + foreach ($responses as $response) { + $command = $commands->dequeue(); + + if ($response instanceof ResponseObjectInterface) { + if ($response instanceof Iterator) { + $response = iterator_to_array($response); + $values[] = $command->parseResponse($response); + } else { + $values[] = $response; + } + } else { + $values[] = $command->parseResponse($response); + } + } + + return $values; + } + + /** + * Consumes an array response returned by EXEC. + * + * @param SplQueue $commands Pipelined commands + * @param Array $responses Responses returned by EXEC. + * @return array + */ + protected function consumeArrayResponse(SplQueue $commands, Array &$responses) + { + $size = count($commands); + $values = array(); + + for ($i = 0; $i < $size; $i++) { + $command = $commands->dequeue(); + $response = $responses[$i]; + + if ($response instanceof ResponseObjectInterface) { + $values[$i] = $response; + } else { + $values[$i] = $command->parseResponse($response); + } + + unset($responses[$i]); + } + + return $values; + } + + /** + * @param ServerProfileInterface $profile Server profile. + */ + public function setProfile(ServerProfileInterface $profile) + { + if (!$profile->supportsCommands(array('multi', 'exec', 'discard'))) { + throw new ClientException('The specified server profile must support MULTI, EXEC and DISCARD.'); + } + + $this->profile = $profile; + } +} diff --git a/vendor/Predis/Pipeline/PipelineContext.php b/vendor/Predis/Pipeline/PipelineContext.php new file mode 100644 index 0000000..92b09d8 --- /dev/null +++ b/vendor/Predis/Pipeline/PipelineContext.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use SplQueue; +use Predis\BasicClientInterface; +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\ExecutableContextInterface; +use Predis\Command\CommandInterface; + +/** + * Abstraction of a pipeline context where write and read operations + * of commands and their replies over the network are pipelined. + * + * @author Daniele Alessandri + */ +class PipelineContext implements BasicClientInterface, ExecutableContextInterface +{ + private $client; + private $executor; + private $pipeline; + + private $replies = array(); + private $running = false; + + /** + * @param ClientInterface $client Client instance used by the context. + * @param PipelineExecutorInterface $executor Pipeline executor instace. + */ + public function __construct(ClientInterface $client, PipelineExecutorInterface $executor = null) + { + $this->client = $client; + $this->executor = $executor ?: $this->createExecutor($client); + $this->pipeline = new SplQueue(); + } + + /** + * Returns a pipeline executor depending on the kind of the underlying + * connection and the passed options. + * + * @param ClientInterface $client Client instance used by the context. + * @return PipelineExecutorInterface + */ + protected function createExecutor(ClientInterface $client) + { + $options = $client->getOptions(); + + if (isset($options->exceptions)) { + return new StandardExecutor($options->exceptions); + } + + return new StandardExecutor(); + } + + /** + * Queues a command into the pipeline buffer. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * @return PipelineContext + */ + public function __call($method, $arguments) + { + $command = $this->client->createCommand($method, $arguments); + $this->recordCommand($command); + + return $this; + } + + /** + * Queues a command instance into the pipeline buffer. + * + * @param CommandInterface $command Command to queue in the buffer. + */ + protected function recordCommand(CommandInterface $command) + { + $this->pipeline->enqueue($command); + } + + /** + * Queues a command instance into the pipeline buffer. + * + * @param CommandInterface $command Command to queue in the buffer. + */ + public function executeCommand(CommandInterface $command) + { + $this->recordCommand($command); + } + + /** + * Flushes the buffer that holds the queued commands. + * + * @param Boolean $send Specifies if the commands in the buffer should be sent to Redis. + * @return PipelineContext + */ + public function flushPipeline($send = true) + { + if ($send && !$this->pipeline->isEmpty()) { + $connection = $this->client->getConnection(); + $replies = $this->executor->execute($connection, $this->pipeline); + $this->replies = array_merge($this->replies, $replies); + } else { + $this->pipeline = new SplQueue(); + } + + return $this; + } + + /** + * Marks the running status of the pipeline. + * + * @param Boolean $bool True if the pipeline is running. + * False if the pipeline is not running. + */ + private function setRunning($bool) + { + if ($bool === true && $this->running === true) { + throw new ClientException("This pipeline is already opened"); + } + + $this->running = $bool; + } + + /** + * Handles the actual execution of the whole pipeline. + * + * @param mixed $callable Optional callback for execution. + * @return array + */ + public function execute($callable = null) + { + if ($callable && !is_callable($callable)) { + throw new \InvalidArgumentException('Argument passed must be a callable object'); + } + + $this->setRunning(true); + $pipelineBlockException = null; + + try { + if ($callable !== null) { + call_user_func($callable, $this); + } + $this->flushPipeline(); + } catch (\Exception $exception) { + $pipelineBlockException = $exception; + } + + $this->setRunning(false); + + if ($pipelineBlockException !== null) { + throw $pipelineBlockException; + } + + return $this->replies; + } + + /** + * Returns the underlying client instance used by the pipeline object. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->client; + } + + /** + * Returns the underlying pipeline executor used by the pipeline object. + * + * @return PipelineExecutorInterface + */ + public function getExecutor() + { + return $this->executor; + } +} diff --git a/vendor/Predis/Pipeline/PipelineExecutorInterface.php b/vendor/Predis/Pipeline/PipelineExecutorInterface.php new file mode 100644 index 0000000..76f1c44 --- /dev/null +++ b/vendor/Predis/Pipeline/PipelineExecutorInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use SplQueue; +use Predis\Connection\ConnectionInterface; + +/** + * Defines a strategy to write a list of commands to the network + * and read back their replies. + * + * @author Daniele Alessandri + */ +interface PipelineExecutorInterface +{ + /** + * Writes a list of commands to the network and reads back their replies. + * + * @param ConnectionInterface $connection Connection to Redis. + * @param SplQueue $commands Commands queued for execution. + * @return array + */ + public function execute(ConnectionInterface $connection, SplQueue $commands); +} diff --git a/vendor/Predis/Pipeline/SafeClusterExecutor.php b/vendor/Predis/Pipeline/SafeClusterExecutor.php new file mode 100644 index 0000000..b362230 --- /dev/null +++ b/vendor/Predis/Pipeline/SafeClusterExecutor.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use SplQueue; +use Predis\CommunicationException; +use Predis\Connection\ConnectionInterface; + +/** + * Implements a pipeline executor strategy for connection clusters that does + * not fail when an error is encountered, but adds the returned error in the + * replies array. + * + * @author Daniele Alessandri + */ +class SafeClusterExecutor implements PipelineExecutorInterface +{ + /** + * {@inheritdoc} + */ + public function execute(ConnectionInterface $connection, SplQueue $commands) + { + $size = count($commands); + $values = array(); + $connectionExceptions = array(); + + foreach ($commands as $command) { + $cmdConnection = $connection->getConnection($command); + + if (isset($connectionExceptions[spl_object_hash($cmdConnection)])) { + continue; + } + + try { + $cmdConnection->writeCommand($command); + } catch (CommunicationException $exception) { + $connectionExceptions[spl_object_hash($cmdConnection)] = $exception; + } + } + + for ($i = 0; $i < $size; $i++) { + $command = $commands->dequeue(); + + $cmdConnection = $connection->getConnection($command); + $connectionObjectHash = spl_object_hash($cmdConnection); + + if (isset($connectionExceptions[$connectionObjectHash])) { + $values[$i] = $connectionExceptions[$connectionObjectHash]; + continue; + } + + try { + $response = $cmdConnection->readResponse($command); + $values[$i] = $response instanceof \Iterator ? iterator_to_array($response) : $response; + } catch (CommunicationException $exception) { + $values[$i] = $exception; + $connectionExceptions[$connectionObjectHash] = $exception; + } + } + + return $values; + } +} diff --git a/vendor/Predis/Pipeline/SafeExecutor.php b/vendor/Predis/Pipeline/SafeExecutor.php new file mode 100644 index 0000000..2d693c0 --- /dev/null +++ b/vendor/Predis/Pipeline/SafeExecutor.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use SplQueue; +use Predis\CommunicationException; +use Predis\Connection\ConnectionInterface; + +/** + * Implements a pipeline executor strategy that does not fail when an error is + * encountered, but adds the returned error in the replies array. + * + * @author Daniele Alessandri + */ +class SafeExecutor implements PipelineExecutorInterface +{ + /** + * {@inheritdoc} + */ + public function execute(ConnectionInterface $connection, SplQueue $commands) + { + $size = count($commands); + $values = array(); + + foreach ($commands as $command) { + try { + $connection->writeCommand($command); + } catch (CommunicationException $exception) { + return array_fill(0, $size, $exception); + } + } + + for ($i = 0; $i < $size; $i++) { + $command = $commands->dequeue(); + + try { + $response = $connection->readResponse($command); + $values[$i] = $response instanceof \Iterator ? iterator_to_array($response) : $response; + } catch (CommunicationException $exception) { + $toAdd = count($commands) - count($values); + $values = array_merge($values, array_fill(0, $toAdd, $exception)); + break; + } + } + + return $values; + } +} diff --git a/vendor/Predis/Pipeline/StandardExecutor.php b/vendor/Predis/Pipeline/StandardExecutor.php new file mode 100644 index 0000000..805395a --- /dev/null +++ b/vendor/Predis/Pipeline/StandardExecutor.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Pipeline; + +use Iterator; +use SplQueue; +use Predis\ResponseErrorInterface; +use Predis\ResponseObjectInterface; +use Predis\ServerException; +use Predis\Command\CommandInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Connection\ReplicationConnectionInterface; + +/** + * Implements the standard pipeline executor strategy used + * to write a list of commands and read their replies over + * a connection to Redis. + * + * @author Daniele Alessandri + */ +class StandardExecutor implements PipelineExecutorInterface +{ + protected $exceptions; + + /** + * @param bool $exceptions Specifies if the executor should throw exceptions on server errors. + */ + public function __construct($exceptions = true) + { + $this->exceptions = (bool) $exceptions; + } + + /** + * Allows the pipeline executor to perform operations on the + * connection before starting to execute the commands stored + * in the pipeline. + * + * @param ConnectionInterface $connection Connection instance. + */ + protected function checkConnection(ConnectionInterface $connection) + { + if ($connection instanceof ReplicationConnectionInterface) { + $connection->switchTo('master'); + } + } + + /** + * Handles a response object. + * + * @param ConnectionInterface $connection + * @param CommandInterface $command + * @param ResponseObjectInterface $response + * @return mixed + */ + protected function onResponseObject(ConnectionInterface $connection, CommandInterface $command, ResponseObjectInterface $response) + { + if ($response instanceof ResponseErrorInterface) { + return $this->onResponseError($connection, $response); + } + + if ($response instanceof Iterator) { + return $command->parseResponse(iterator_to_array($response)); + } + + return $response; + } + + /** + * Handles -ERR responses returned by Redis. + * + * @param ConnectionInterface $connection The connection that returned the error. + * @param ResponseErrorInterface $response The error response instance. + */ + protected function onResponseError(ConnectionInterface $connection, ResponseErrorInterface $response) + { + if (!$this->exceptions) { + return $response; + } + + // Force disconnection to prevent protocol desynchronization. + $connection->disconnect(); + $message = $response->getMessage(); + + throw new ServerException($message); + } + + /** + * {@inheritdoc} + */ + public function execute(ConnectionInterface $connection, SplQueue $commands) + { + $this->checkConnection($connection); + + foreach ($commands as $command) { + $connection->writeCommand($command); + } + + $values = array(); + + while (!$commands->isEmpty()) { + $command = $commands->dequeue(); + $response = $connection->readResponse($command); + + if ($response instanceof ResponseObjectInterface) { + $values[] = $this->onResponseObject($connection, $command, $response); + } else { + $values[] = $command->parseResponse($response); + } + } + + return $values; + } +} diff --git a/vendor/Predis/PredisException.php b/vendor/Predis/PredisException.php new file mode 100644 index 0000000..122bde1 --- /dev/null +++ b/vendor/Predis/PredisException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Base exception class for Predis-related errors. + * + * @author Daniele Alessandri + */ +abstract class PredisException extends \Exception +{ +} diff --git a/vendor/Predis/Profile/ServerProfile.php b/vendor/Predis/Profile/ServerProfile.php new file mode 100644 index 0000000..9adb2f4 --- /dev/null +++ b/vendor/Predis/Profile/ServerProfile.php @@ -0,0 +1,227 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +use Predis\ClientException; +use Predis\Command\Processor\CommandProcessingInterface; +use Predis\Command\Processor\CommandProcessorInterface; + +/** + * Base class that implements common functionalities of server profiles. + * + * @author Daniele Alessandri + */ +abstract class ServerProfile implements ServerProfileInterface, CommandProcessingInterface +{ + private static $profiles; + + private $commands; + private $processor; + + /** + * + */ + public function __construct() + { + $this->commands = $this->getSupportedCommands(); + } + + /** + * Returns a map of all the commands supported by the profile and their + * actual PHP classes. + * + * @return array + */ + protected abstract function getSupportedCommands(); + + /** + * Returns the default server profile. + * + * @return ServerProfileInterface + */ + public static function getDefault() + { + return self::get('default'); + } + + /** + * Returns the development server profile. + * + * @return ServerProfileInterface + */ + public static function getDevelopment() + { + return self::get('dev'); + } + + /** + * Returns a map of all the server profiles supported by default and their + * actual PHP classes. + * + * @return array + */ + private static function getDefaultProfiles() + { + return array( + '1.2' => 'Predis\Profile\ServerVersion12', + '2.0' => 'Predis\Profile\ServerVersion20', + '2.2' => 'Predis\Profile\ServerVersion22', + '2.4' => 'Predis\Profile\ServerVersion24', + '2.6' => 'Predis\Profile\ServerVersion26', + 'default' => 'Predis\Profile\ServerVersion26', + 'dev' => 'Predis\Profile\ServerVersionNext', + ); + } + + /** + * Registers a new server profile. + * + * @param string $alias Profile version or alias. + * @param string $profileClass FQN of a class implementing Predis\Profile\ServerProfileInterface. + */ + public static function define($alias, $profileClass) + { + if (!isset(self::$profiles)) { + self::$profiles = self::getDefaultProfiles(); + } + + $profileReflection = new \ReflectionClass($profileClass); + + if (!$profileReflection->isSubclassOf('Predis\Profile\ServerProfileInterface')) { + throw new \InvalidArgumentException("Cannot register '$profileClass' as it is not a valid profile class"); + } + + self::$profiles[$alias] = $profileClass; + } + + /** + * Returns the specified server profile. + * + * @param string $version Profile version or alias. + * @return ServerProfileInterface + */ + public static function get($version) + { + if (!isset(self::$profiles)) { + self::$profiles = self::getDefaultProfiles(); + } + + if (!isset(self::$profiles[$version])) { + throw new ClientException("Unknown server profile: $version"); + } + + $profile = self::$profiles[$version]; + + return new $profile(); + } + + /** + * {@inheritdoc} + */ + public function supportsCommands(Array $commands) + { + foreach ($commands as $command) { + if (!$this->supportsCommand($command)) { + return false; + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsCommand($command) + { + return isset($this->commands[strtolower($command)]); + } + + /** + * Returns the FQN of the class that represent the specified command ID + * registered in the current server profile. + * + * @param string $command Command ID. + * @return string + */ + public function getCommandClass($command) + { + if (isset($this->commands[$command = strtolower($command)])) { + return $this->commands[$command]; + } + } + + /** + * {@inheritdoc} + */ + public function createCommand($method, $arguments = array()) + { + $method = strtolower($method); + + if (!isset($this->commands[$method])) { + throw new ClientException("'$method' is not a registered Redis command"); + } + + $commandClass = $this->commands[$method]; + $command = new $commandClass(); + $command->setArguments($arguments); + + if (isset($this->processor)) { + $this->processor->process($command); + } + + return $command; + } + + /** + * Defines a new commands in the server profile. + * + * @param string $alias Command ID. + * @param string $command FQN of a class implementing Predis\Command\CommandInterface. + */ + public function defineCommand($alias, $command) + { + $commandReflection = new \ReflectionClass($command); + + if (!$commandReflection->isSubclassOf('Predis\Command\CommandInterface')) { + throw new \InvalidArgumentException("Cannot register '$command' as it is not a valid Redis command"); + } + + $this->commands[strtolower($alias)] = $command; + } + + /** + * {@inheritdoc} + */ + public function setProcessor(CommandProcessorInterface $processor = null) + { + $this->processor = $processor; + } + + /** + * {@inheritdoc} + */ + public function getProcessor() + { + return $this->processor; + } + + /** + * Returns the version of server profile as its string representation. + * + * @return string + */ + public function __toString() + { + return $this->getVersion(); + } +} diff --git a/vendor/Predis/Profile/ServerProfileInterface.php b/vendor/Predis/Profile/ServerProfileInterface.php new file mode 100644 index 0000000..39477cb --- /dev/null +++ b/vendor/Predis/Profile/ServerProfileInterface.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +use Predis\Command\CommandInterface; + +/** + * A server profile defines features and commands supported by certain + * versions of Redis. Instances of Predis\Client should use a server + * profile matching the version of Redis in use. + * + * @author Daniele Alessandri + */ +interface ServerProfileInterface +{ + /** + * Gets a profile version corresponding to a Redis version. + * + * @return string + */ + public function getVersion(); + + /** + * Checks if the profile supports the specified command. + * + * @param string $command Command ID. + * @return Boolean + */ + public function supportsCommand($command); + + /** + * Checks if the profile supports the specified list of commands. + * + * @param array $commands List of command IDs. + * @return string + */ + public function supportsCommands(Array $commands); + + /** + * Creates a new command instance. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * @return CommandInterface + */ + public function createCommand($method, $arguments = array()); +} diff --git a/vendor/Predis/Profile/ServerVersion12.php b/vendor/Predis/Profile/ServerVersion12.php new file mode 100644 index 0000000..4e339a7 --- /dev/null +++ b/vendor/Predis/Profile/ServerVersion12.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis v1.2.x. + * + * @author Daniele Alessandri + */ +class ServerVersion12 extends ServerProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '1.2'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'exists' => 'Predis\Command\KeyExists', + 'del' => 'Predis\Command\KeyDelete', + 'type' => 'Predis\Command\KeyType', + 'keys' => 'Predis\Command\KeyKeysV12x', + 'randomkey' => 'Predis\Command\KeyRandom', + 'rename' => 'Predis\Command\KeyRename', + 'renamenx' => 'Predis\Command\KeyRenamePreserve', + 'expire' => 'Predis\Command\KeyExpire', + 'expireat' => 'Predis\Command\KeyExpireAt', + 'ttl' => 'Predis\Command\KeyTimeToLive', + 'move' => 'Predis\Command\KeyMove', + 'sort' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'set' => 'Predis\Command\StringSet', + 'setnx' => 'Predis\Command\StringSetPreserve', + 'mset' => 'Predis\Command\StringSetMultiple', + 'msetnx' => 'Predis\Command\StringSetMultiplePreserve', + 'get' => 'Predis\Command\StringGet', + 'mget' => 'Predis\Command\StringGetMultiple', + 'getset' => 'Predis\Command\StringGetSet', + 'incr' => 'Predis\Command\StringIncrement', + 'incrby' => 'Predis\Command\StringIncrementBy', + 'decr' => 'Predis\Command\StringDecrement', + 'decrby' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'rpush' => 'Predis\Command\ListPushTail', + 'lpush' => 'Predis\Command\ListPushHead', + 'llen' => 'Predis\Command\ListLength', + 'lrange' => 'Predis\Command\ListRange', + 'ltrim' => 'Predis\Command\ListTrim', + 'lindex' => 'Predis\Command\ListIndex', + 'lset' => 'Predis\Command\ListSet', + 'lrem' => 'Predis\Command\ListRemove', + 'lpop' => 'Predis\Command\ListPopFirst', + 'rpop' => 'Predis\Command\ListPopLast', + 'rpoplpush' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'sadd' => 'Predis\Command\SetAdd', + 'srem' => 'Predis\Command\SetRemove', + 'spop' => 'Predis\Command\SetPop', + 'smove' => 'Predis\Command\SetMove', + 'scard' => 'Predis\Command\SetCardinality', + 'sismember' => 'Predis\Command\SetIsMember', + 'sinter' => 'Predis\Command\SetIntersection', + 'sinterstore' => 'Predis\Command\SetIntersectionStore', + 'sunion' => 'Predis\Command\SetUnion', + 'sunionstore' => 'Predis\Command\SetUnionStore', + 'sdiff' => 'Predis\Command\SetDifference', + 'sdiffstore' => 'Predis\Command\SetDifferenceStore', + 'smembers' => 'Predis\Command\SetMembers', + 'srandmember' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'zadd' => 'Predis\Command\ZSetAdd', + 'zincrby' => 'Predis\Command\ZSetIncrementBy', + 'zrem' => 'Predis\Command\ZSetRemove', + 'zrange' => 'Predis\Command\ZSetRange', + 'zrevrange' => 'Predis\Command\ZSetReverseRange', + 'zrangebyscore' => 'Predis\Command\ZSetRangeByScore', + 'zcard' => 'Predis\Command\ZSetCardinality', + 'zscore' => 'Predis\Command\ZSetScore', + 'zremrangebyscore' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'ping' => 'Predis\Command\ConnectionPing', + 'auth' => 'Predis\Command\ConnectionAuth', + 'select' => 'Predis\Command\ConnectionSelect', + 'echo' => 'Predis\Command\ConnectionEcho', + 'quit' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'info' => 'Predis\Command\ServerInfo', + 'slaveof' => 'Predis\Command\ServerSlaveOf', + 'monitor' => 'Predis\Command\ServerMonitor', + 'dbsize' => 'Predis\Command\ServerDatabaseSize', + 'flushdb' => 'Predis\Command\ServerFlushDatabase', + 'flushall' => 'Predis\Command\ServerFlushAll', + 'save' => 'Predis\Command\ServerSave', + 'bgsave' => 'Predis\Command\ServerBackgroundSave', + 'lastsave' => 'Predis\Command\ServerLastSave', + 'shutdown' => 'Predis\Command\ServerShutdown', + 'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF', + ); + } +} diff --git a/vendor/Predis/Profile/ServerVersion20.php b/vendor/Predis/Profile/ServerVersion20.php new file mode 100644 index 0000000..42b0b08 --- /dev/null +++ b/vendor/Predis/Profile/ServerVersion20.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis v2.0.x. + * + * @author Daniele Alessandri + */ +class ServerVersion20 extends ServerProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.0'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'exists' => 'Predis\Command\KeyExists', + 'del' => 'Predis\Command\KeyDelete', + 'type' => 'Predis\Command\KeyType', + 'keys' => 'Predis\Command\KeyKeys', + 'randomkey' => 'Predis\Command\KeyRandom', + 'rename' => 'Predis\Command\KeyRename', + 'renamenx' => 'Predis\Command\KeyRenamePreserve', + 'expire' => 'Predis\Command\KeyExpire', + 'expireat' => 'Predis\Command\KeyExpireAt', + 'ttl' => 'Predis\Command\KeyTimeToLive', + 'move' => 'Predis\Command\KeyMove', + 'sort' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'set' => 'Predis\Command\StringSet', + 'setnx' => 'Predis\Command\StringSetPreserve', + 'mset' => 'Predis\Command\StringSetMultiple', + 'msetnx' => 'Predis\Command\StringSetMultiplePreserve', + 'get' => 'Predis\Command\StringGet', + 'mget' => 'Predis\Command\StringGetMultiple', + 'getset' => 'Predis\Command\StringGetSet', + 'incr' => 'Predis\Command\StringIncrement', + 'incrby' => 'Predis\Command\StringIncrementBy', + 'decr' => 'Predis\Command\StringDecrement', + 'decrby' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'rpush' => 'Predis\Command\ListPushTail', + 'lpush' => 'Predis\Command\ListPushHead', + 'llen' => 'Predis\Command\ListLength', + 'lrange' => 'Predis\Command\ListRange', + 'ltrim' => 'Predis\Command\ListTrim', + 'lindex' => 'Predis\Command\ListIndex', + 'lset' => 'Predis\Command\ListSet', + 'lrem' => 'Predis\Command\ListRemove', + 'lpop' => 'Predis\Command\ListPopFirst', + 'rpop' => 'Predis\Command\ListPopLast', + 'rpoplpush' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'sadd' => 'Predis\Command\SetAdd', + 'srem' => 'Predis\Command\SetRemove', + 'spop' => 'Predis\Command\SetPop', + 'smove' => 'Predis\Command\SetMove', + 'scard' => 'Predis\Command\SetCardinality', + 'sismember' => 'Predis\Command\SetIsMember', + 'sinter' => 'Predis\Command\SetIntersection', + 'sinterstore' => 'Predis\Command\SetIntersectionStore', + 'sunion' => 'Predis\Command\SetUnion', + 'sunionstore' => 'Predis\Command\SetUnionStore', + 'sdiff' => 'Predis\Command\SetDifference', + 'sdiffstore' => 'Predis\Command\SetDifferenceStore', + 'smembers' => 'Predis\Command\SetMembers', + 'srandmember' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'zadd' => 'Predis\Command\ZSetAdd', + 'zincrby' => 'Predis\Command\ZSetIncrementBy', + 'zrem' => 'Predis\Command\ZSetRemove', + 'zrange' => 'Predis\Command\ZSetRange', + 'zrevrange' => 'Predis\Command\ZSetReverseRange', + 'zrangebyscore' => 'Predis\Command\ZSetRangeByScore', + 'zcard' => 'Predis\Command\ZSetCardinality', + 'zscore' => 'Predis\Command\ZSetScore', + 'zremrangebyscore' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'ping' => 'Predis\Command\ConnectionPing', + 'auth' => 'Predis\Command\ConnectionAuth', + 'select' => 'Predis\Command\ConnectionSelect', + 'echo' => 'Predis\Command\ConnectionEcho', + 'quit' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'info' => 'Predis\Command\ServerInfo', + 'slaveof' => 'Predis\Command\ServerSlaveOf', + 'monitor' => 'Predis\Command\ServerMonitor', + 'dbsize' => 'Predis\Command\ServerDatabaseSize', + 'flushdb' => 'Predis\Command\ServerFlushDatabase', + 'flushall' => 'Predis\Command\ServerFlushAll', + 'save' => 'Predis\Command\ServerSave', + 'bgsave' => 'Predis\Command\ServerBackgroundSave', + 'lastsave' => 'Predis\Command\ServerLastSave', + 'shutdown' => 'Predis\Command\ServerShutdown', + 'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF', + + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'setex' => 'Predis\Command\StringSetExpire', + 'append' => 'Predis\Command\StringAppend', + 'substr' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'blpop' => 'Predis\Command\ListPopFirstBlocking', + 'brpop' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'zunionstore' => 'Predis\Command\ZSetUnionStore', + 'zinterstore' => 'Predis\Command\ZSetIntersectionStore', + 'zcount' => 'Predis\Command\ZSetCount', + 'zrank' => 'Predis\Command\ZSetRank', + 'zrevrank' => 'Predis\Command\ZSetReverseRank', + 'zremrangebyrank' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'hset' => 'Predis\Command\HashSet', + 'hsetnx' => 'Predis\Command\HashSetPreserve', + 'hmset' => 'Predis\Command\HashSetMultiple', + 'hincrby' => 'Predis\Command\HashIncrementBy', + 'hget' => 'Predis\Command\HashGet', + 'hmget' => 'Predis\Command\HashGetMultiple', + 'hdel' => 'Predis\Command\HashDelete', + 'hexists' => 'Predis\Command\HashExists', + 'hlen' => 'Predis\Command\HashLength', + 'hkeys' => 'Predis\Command\HashKeys', + 'hvals' => 'Predis\Command\HashValues', + 'hgetall' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'multi' => 'Predis\Command\TransactionMulti', + 'exec' => 'Predis\Command\TransactionExec', + 'discard' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'subscribe' => 'Predis\Command\PubSubSubscribe', + 'unsubscribe' => 'Predis\Command\PubSubUnsubscribe', + 'psubscribe' => 'Predis\Command\PubSubSubscribeByPattern', + 'punsubscribe' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'publish' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'config' => 'Predis\Command\ServerConfig', + ); + } +} diff --git a/vendor/Predis/Profile/ServerVersion22.php b/vendor/Predis/Profile/ServerVersion22.php new file mode 100644 index 0000000..e97f422 --- /dev/null +++ b/vendor/Predis/Profile/ServerVersion22.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis v2.2.x. + * + * @author Daniele Alessandri + */ +class ServerVersion22 extends ServerProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.2'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'exists' => 'Predis\Command\KeyExists', + 'del' => 'Predis\Command\KeyDelete', + 'type' => 'Predis\Command\KeyType', + 'keys' => 'Predis\Command\KeyKeys', + 'randomkey' => 'Predis\Command\KeyRandom', + 'rename' => 'Predis\Command\KeyRename', + 'renamenx' => 'Predis\Command\KeyRenamePreserve', + 'expire' => 'Predis\Command\KeyExpire', + 'expireat' => 'Predis\Command\KeyExpireAt', + 'ttl' => 'Predis\Command\KeyTimeToLive', + 'move' => 'Predis\Command\KeyMove', + 'sort' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'set' => 'Predis\Command\StringSet', + 'setnx' => 'Predis\Command\StringSetPreserve', + 'mset' => 'Predis\Command\StringSetMultiple', + 'msetnx' => 'Predis\Command\StringSetMultiplePreserve', + 'get' => 'Predis\Command\StringGet', + 'mget' => 'Predis\Command\StringGetMultiple', + 'getset' => 'Predis\Command\StringGetSet', + 'incr' => 'Predis\Command\StringIncrement', + 'incrby' => 'Predis\Command\StringIncrementBy', + 'decr' => 'Predis\Command\StringDecrement', + 'decrby' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'rpush' => 'Predis\Command\ListPushTail', + 'lpush' => 'Predis\Command\ListPushHead', + 'llen' => 'Predis\Command\ListLength', + 'lrange' => 'Predis\Command\ListRange', + 'ltrim' => 'Predis\Command\ListTrim', + 'lindex' => 'Predis\Command\ListIndex', + 'lset' => 'Predis\Command\ListSet', + 'lrem' => 'Predis\Command\ListRemove', + 'lpop' => 'Predis\Command\ListPopFirst', + 'rpop' => 'Predis\Command\ListPopLast', + 'rpoplpush' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'sadd' => 'Predis\Command\SetAdd', + 'srem' => 'Predis\Command\SetRemove', + 'spop' => 'Predis\Command\SetPop', + 'smove' => 'Predis\Command\SetMove', + 'scard' => 'Predis\Command\SetCardinality', + 'sismember' => 'Predis\Command\SetIsMember', + 'sinter' => 'Predis\Command\SetIntersection', + 'sinterstore' => 'Predis\Command\SetIntersectionStore', + 'sunion' => 'Predis\Command\SetUnion', + 'sunionstore' => 'Predis\Command\SetUnionStore', + 'sdiff' => 'Predis\Command\SetDifference', + 'sdiffstore' => 'Predis\Command\SetDifferenceStore', + 'smembers' => 'Predis\Command\SetMembers', + 'srandmember' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'zadd' => 'Predis\Command\ZSetAdd', + 'zincrby' => 'Predis\Command\ZSetIncrementBy', + 'zrem' => 'Predis\Command\ZSetRemove', + 'zrange' => 'Predis\Command\ZSetRange', + 'zrevrange' => 'Predis\Command\ZSetReverseRange', + 'zrangebyscore' => 'Predis\Command\ZSetRangeByScore', + 'zcard' => 'Predis\Command\ZSetCardinality', + 'zscore' => 'Predis\Command\ZSetScore', + 'zremrangebyscore' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'ping' => 'Predis\Command\ConnectionPing', + 'auth' => 'Predis\Command\ConnectionAuth', + 'select' => 'Predis\Command\ConnectionSelect', + 'echo' => 'Predis\Command\ConnectionEcho', + 'quit' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'info' => 'Predis\Command\ServerInfo', + 'slaveof' => 'Predis\Command\ServerSlaveOf', + 'monitor' => 'Predis\Command\ServerMonitor', + 'dbsize' => 'Predis\Command\ServerDatabaseSize', + 'flushdb' => 'Predis\Command\ServerFlushDatabase', + 'flushall' => 'Predis\Command\ServerFlushAll', + 'save' => 'Predis\Command\ServerSave', + 'bgsave' => 'Predis\Command\ServerBackgroundSave', + 'lastsave' => 'Predis\Command\ServerLastSave', + 'shutdown' => 'Predis\Command\ServerShutdown', + 'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF', + + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'setex' => 'Predis\Command\StringSetExpire', + 'append' => 'Predis\Command\StringAppend', + 'substr' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'blpop' => 'Predis\Command\ListPopFirstBlocking', + 'brpop' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'zunionstore' => 'Predis\Command\ZSetUnionStore', + 'zinterstore' => 'Predis\Command\ZSetIntersectionStore', + 'zcount' => 'Predis\Command\ZSetCount', + 'zrank' => 'Predis\Command\ZSetRank', + 'zrevrank' => 'Predis\Command\ZSetReverseRank', + 'zremrangebyrank' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'hset' => 'Predis\Command\HashSet', + 'hsetnx' => 'Predis\Command\HashSetPreserve', + 'hmset' => 'Predis\Command\HashSetMultiple', + 'hincrby' => 'Predis\Command\HashIncrementBy', + 'hget' => 'Predis\Command\HashGet', + 'hmget' => 'Predis\Command\HashGetMultiple', + 'hdel' => 'Predis\Command\HashDelete', + 'hexists' => 'Predis\Command\HashExists', + 'hlen' => 'Predis\Command\HashLength', + 'hkeys' => 'Predis\Command\HashKeys', + 'hvals' => 'Predis\Command\HashValues', + 'hgetall' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'multi' => 'Predis\Command\TransactionMulti', + 'exec' => 'Predis\Command\TransactionExec', + 'discard' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'subscribe' => 'Predis\Command\PubSubSubscribe', + 'unsubscribe' => 'Predis\Command\PubSubUnsubscribe', + 'psubscribe' => 'Predis\Command\PubSubSubscribeByPattern', + 'punsubscribe' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'publish' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'config' => 'Predis\Command\ServerConfig', + + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'persist' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'strlen' => 'Predis\Command\StringStrlen', + 'setrange' => 'Predis\Command\StringSetRange', + 'getrange' => 'Predis\Command\StringGetRange', + 'setbit' => 'Predis\Command\StringSetBit', + 'getbit' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'rpushx' => 'Predis\Command\ListPushTailX', + 'lpushx' => 'Predis\Command\ListPushHeadX', + 'linsert' => 'Predis\Command\ListInsert', + 'brpoplpush' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'zrevrangebyscore' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'watch' => 'Predis\Command\TransactionWatch', + 'unwatch' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'object' => 'Predis\Command\ServerObject', + 'slowlog' => 'Predis\Command\ServerSlowlog', + ); + } +} diff --git a/vendor/Predis/Profile/ServerVersion24.php b/vendor/Predis/Profile/ServerVersion24.php new file mode 100644 index 0000000..20569c6 --- /dev/null +++ b/vendor/Predis/Profile/ServerVersion24.php @@ -0,0 +1,210 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis v2.4.x. + * + * @author Daniele Alessandri + */ +class ServerVersion24 extends ServerProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.4'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'exists' => 'Predis\Command\KeyExists', + 'del' => 'Predis\Command\KeyDelete', + 'type' => 'Predis\Command\KeyType', + 'keys' => 'Predis\Command\KeyKeys', + 'randomkey' => 'Predis\Command\KeyRandom', + 'rename' => 'Predis\Command\KeyRename', + 'renamenx' => 'Predis\Command\KeyRenamePreserve', + 'expire' => 'Predis\Command\KeyExpire', + 'expireat' => 'Predis\Command\KeyExpireAt', + 'ttl' => 'Predis\Command\KeyTimeToLive', + 'move' => 'Predis\Command\KeyMove', + 'sort' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'set' => 'Predis\Command\StringSet', + 'setnx' => 'Predis\Command\StringSetPreserve', + 'mset' => 'Predis\Command\StringSetMultiple', + 'msetnx' => 'Predis\Command\StringSetMultiplePreserve', + 'get' => 'Predis\Command\StringGet', + 'mget' => 'Predis\Command\StringGetMultiple', + 'getset' => 'Predis\Command\StringGetSet', + 'incr' => 'Predis\Command\StringIncrement', + 'incrby' => 'Predis\Command\StringIncrementBy', + 'decr' => 'Predis\Command\StringDecrement', + 'decrby' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'rpush' => 'Predis\Command\ListPushTail', + 'lpush' => 'Predis\Command\ListPushHead', + 'llen' => 'Predis\Command\ListLength', + 'lrange' => 'Predis\Command\ListRange', + 'ltrim' => 'Predis\Command\ListTrim', + 'lindex' => 'Predis\Command\ListIndex', + 'lset' => 'Predis\Command\ListSet', + 'lrem' => 'Predis\Command\ListRemove', + 'lpop' => 'Predis\Command\ListPopFirst', + 'rpop' => 'Predis\Command\ListPopLast', + 'rpoplpush' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'sadd' => 'Predis\Command\SetAdd', + 'srem' => 'Predis\Command\SetRemove', + 'spop' => 'Predis\Command\SetPop', + 'smove' => 'Predis\Command\SetMove', + 'scard' => 'Predis\Command\SetCardinality', + 'sismember' => 'Predis\Command\SetIsMember', + 'sinter' => 'Predis\Command\SetIntersection', + 'sinterstore' => 'Predis\Command\SetIntersectionStore', + 'sunion' => 'Predis\Command\SetUnion', + 'sunionstore' => 'Predis\Command\SetUnionStore', + 'sdiff' => 'Predis\Command\SetDifference', + 'sdiffstore' => 'Predis\Command\SetDifferenceStore', + 'smembers' => 'Predis\Command\SetMembers', + 'srandmember' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'zadd' => 'Predis\Command\ZSetAdd', + 'zincrby' => 'Predis\Command\ZSetIncrementBy', + 'zrem' => 'Predis\Command\ZSetRemove', + 'zrange' => 'Predis\Command\ZSetRange', + 'zrevrange' => 'Predis\Command\ZSetReverseRange', + 'zrangebyscore' => 'Predis\Command\ZSetRangeByScore', + 'zcard' => 'Predis\Command\ZSetCardinality', + 'zscore' => 'Predis\Command\ZSetScore', + 'zremrangebyscore' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'ping' => 'Predis\Command\ConnectionPing', + 'auth' => 'Predis\Command\ConnectionAuth', + 'select' => 'Predis\Command\ConnectionSelect', + 'echo' => 'Predis\Command\ConnectionEcho', + 'quit' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'info' => 'Predis\Command\ServerInfo', + 'slaveof' => 'Predis\Command\ServerSlaveOf', + 'monitor' => 'Predis\Command\ServerMonitor', + 'dbsize' => 'Predis\Command\ServerDatabaseSize', + 'flushdb' => 'Predis\Command\ServerFlushDatabase', + 'flushall' => 'Predis\Command\ServerFlushAll', + 'save' => 'Predis\Command\ServerSave', + 'bgsave' => 'Predis\Command\ServerBackgroundSave', + 'lastsave' => 'Predis\Command\ServerLastSave', + 'shutdown' => 'Predis\Command\ServerShutdown', + 'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF', + + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'setex' => 'Predis\Command\StringSetExpire', + 'append' => 'Predis\Command\StringAppend', + 'substr' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'blpop' => 'Predis\Command\ListPopFirstBlocking', + 'brpop' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'zunionstore' => 'Predis\Command\ZSetUnionStore', + 'zinterstore' => 'Predis\Command\ZSetIntersectionStore', + 'zcount' => 'Predis\Command\ZSetCount', + 'zrank' => 'Predis\Command\ZSetRank', + 'zrevrank' => 'Predis\Command\ZSetReverseRank', + 'zremrangebyrank' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'hset' => 'Predis\Command\HashSet', + 'hsetnx' => 'Predis\Command\HashSetPreserve', + 'hmset' => 'Predis\Command\HashSetMultiple', + 'hincrby' => 'Predis\Command\HashIncrementBy', + 'hget' => 'Predis\Command\HashGet', + 'hmget' => 'Predis\Command\HashGetMultiple', + 'hdel' => 'Predis\Command\HashDelete', + 'hexists' => 'Predis\Command\HashExists', + 'hlen' => 'Predis\Command\HashLength', + 'hkeys' => 'Predis\Command\HashKeys', + 'hvals' => 'Predis\Command\HashValues', + 'hgetall' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'multi' => 'Predis\Command\TransactionMulti', + 'exec' => 'Predis\Command\TransactionExec', + 'discard' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'subscribe' => 'Predis\Command\PubSubSubscribe', + 'unsubscribe' => 'Predis\Command\PubSubUnsubscribe', + 'psubscribe' => 'Predis\Command\PubSubSubscribeByPattern', + 'punsubscribe' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'publish' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'config' => 'Predis\Command\ServerConfig', + + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'persist' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'strlen' => 'Predis\Command\StringStrlen', + 'setrange' => 'Predis\Command\StringSetRange', + 'getrange' => 'Predis\Command\StringGetRange', + 'setbit' => 'Predis\Command\StringSetBit', + 'getbit' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'rpushx' => 'Predis\Command\ListPushTailX', + 'lpushx' => 'Predis\Command\ListPushHeadX', + 'linsert' => 'Predis\Command\ListInsert', + 'brpoplpush' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'zrevrangebyscore' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'watch' => 'Predis\Command\TransactionWatch', + 'unwatch' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'object' => 'Predis\Command\ServerObject', + 'slowlog' => 'Predis\Command\ServerSlowlog', + + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'client' => 'Predis\Command\ServerClient', + ); + } +} diff --git a/vendor/Predis/Profile/ServerVersion26.php b/vendor/Predis/Profile/ServerVersion26.php new file mode 100644 index 0000000..af3c0d2 --- /dev/null +++ b/vendor/Predis/Profile/ServerVersion26.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for Redis v2.6.x. + * + * @author Daniele Alessandri + */ +class ServerVersion26 extends ServerProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.6'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'exists' => 'Predis\Command\KeyExists', + 'del' => 'Predis\Command\KeyDelete', + 'type' => 'Predis\Command\KeyType', + 'keys' => 'Predis\Command\KeyKeys', + 'randomkey' => 'Predis\Command\KeyRandom', + 'rename' => 'Predis\Command\KeyRename', + 'renamenx' => 'Predis\Command\KeyRenamePreserve', + 'expire' => 'Predis\Command\KeyExpire', + 'expireat' => 'Predis\Command\KeyExpireAt', + 'ttl' => 'Predis\Command\KeyTimeToLive', + 'move' => 'Predis\Command\KeyMove', + 'sort' => 'Predis\Command\KeySort', + 'dump' => 'Predis\Command\KeyDump', + 'restore' => 'Predis\Command\KeyRestore', + + /* commands operating on string values */ + 'set' => 'Predis\Command\StringSet', + 'setnx' => 'Predis\Command\StringSetPreserve', + 'mset' => 'Predis\Command\StringSetMultiple', + 'msetnx' => 'Predis\Command\StringSetMultiplePreserve', + 'get' => 'Predis\Command\StringGet', + 'mget' => 'Predis\Command\StringGetMultiple', + 'getset' => 'Predis\Command\StringGetSet', + 'incr' => 'Predis\Command\StringIncrement', + 'incrby' => 'Predis\Command\StringIncrementBy', + 'decr' => 'Predis\Command\StringDecrement', + 'decrby' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'rpush' => 'Predis\Command\ListPushTail', + 'lpush' => 'Predis\Command\ListPushHead', + 'llen' => 'Predis\Command\ListLength', + 'lrange' => 'Predis\Command\ListRange', + 'ltrim' => 'Predis\Command\ListTrim', + 'lindex' => 'Predis\Command\ListIndex', + 'lset' => 'Predis\Command\ListSet', + 'lrem' => 'Predis\Command\ListRemove', + 'lpop' => 'Predis\Command\ListPopFirst', + 'rpop' => 'Predis\Command\ListPopLast', + 'rpoplpush' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'sadd' => 'Predis\Command\SetAdd', + 'srem' => 'Predis\Command\SetRemove', + 'spop' => 'Predis\Command\SetPop', + 'smove' => 'Predis\Command\SetMove', + 'scard' => 'Predis\Command\SetCardinality', + 'sismember' => 'Predis\Command\SetIsMember', + 'sinter' => 'Predis\Command\SetIntersection', + 'sinterstore' => 'Predis\Command\SetIntersectionStore', + 'sunion' => 'Predis\Command\SetUnion', + 'sunionstore' => 'Predis\Command\SetUnionStore', + 'sdiff' => 'Predis\Command\SetDifference', + 'sdiffstore' => 'Predis\Command\SetDifferenceStore', + 'smembers' => 'Predis\Command\SetMembers', + 'srandmember' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'zadd' => 'Predis\Command\ZSetAdd', + 'zincrby' => 'Predis\Command\ZSetIncrementBy', + 'zrem' => 'Predis\Command\ZSetRemove', + 'zrange' => 'Predis\Command\ZSetRange', + 'zrevrange' => 'Predis\Command\ZSetReverseRange', + 'zrangebyscore' => 'Predis\Command\ZSetRangeByScore', + 'zcard' => 'Predis\Command\ZSetCardinality', + 'zscore' => 'Predis\Command\ZSetScore', + 'zremrangebyscore' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'ping' => 'Predis\Command\ConnectionPing', + 'auth' => 'Predis\Command\ConnectionAuth', + 'select' => 'Predis\Command\ConnectionSelect', + 'echo' => 'Predis\Command\ConnectionEcho', + 'quit' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'info' => 'Predis\Command\ServerInfo', + 'slaveof' => 'Predis\Command\ServerSlaveOf', + 'monitor' => 'Predis\Command\ServerMonitor', + 'dbsize' => 'Predis\Command\ServerDatabaseSize', + 'flushdb' => 'Predis\Command\ServerFlushDatabase', + 'flushall' => 'Predis\Command\ServerFlushAll', + 'save' => 'Predis\Command\ServerSave', + 'bgsave' => 'Predis\Command\ServerBackgroundSave', + 'lastsave' => 'Predis\Command\ServerLastSave', + 'shutdown' => 'Predis\Command\ServerShutdown', + 'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF', + + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'setex' => 'Predis\Command\StringSetExpire', + 'append' => 'Predis\Command\StringAppend', + 'substr' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'blpop' => 'Predis\Command\ListPopFirstBlocking', + 'brpop' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'zunionstore' => 'Predis\Command\ZSetUnionStore', + 'zinterstore' => 'Predis\Command\ZSetIntersectionStore', + 'zcount' => 'Predis\Command\ZSetCount', + 'zrank' => 'Predis\Command\ZSetRank', + 'zrevrank' => 'Predis\Command\ZSetReverseRank', + 'zremrangebyrank' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'hset' => 'Predis\Command\HashSet', + 'hsetnx' => 'Predis\Command\HashSetPreserve', + 'hmset' => 'Predis\Command\HashSetMultiple', + 'hincrby' => 'Predis\Command\HashIncrementBy', + 'hget' => 'Predis\Command\HashGet', + 'hmget' => 'Predis\Command\HashGetMultiple', + 'hdel' => 'Predis\Command\HashDelete', + 'hexists' => 'Predis\Command\HashExists', + 'hlen' => 'Predis\Command\HashLength', + 'hkeys' => 'Predis\Command\HashKeys', + 'hvals' => 'Predis\Command\HashValues', + 'hgetall' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'multi' => 'Predis\Command\TransactionMulti', + 'exec' => 'Predis\Command\TransactionExec', + 'discard' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'subscribe' => 'Predis\Command\PubSubSubscribe', + 'unsubscribe' => 'Predis\Command\PubSubUnsubscribe', + 'psubscribe' => 'Predis\Command\PubSubSubscribeByPattern', + 'punsubscribe' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'publish' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'config' => 'Predis\Command\ServerConfig', + + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'persist' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'strlen' => 'Predis\Command\StringStrlen', + 'setrange' => 'Predis\Command\StringSetRange', + 'getrange' => 'Predis\Command\StringGetRange', + 'setbit' => 'Predis\Command\StringSetBit', + 'getbit' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'rpushx' => 'Predis\Command\ListPushTailX', + 'lpushx' => 'Predis\Command\ListPushHeadX', + 'linsert' => 'Predis\Command\ListInsert', + 'brpoplpush' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'zrevrangebyscore' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'watch' => 'Predis\Command\TransactionWatch', + 'unwatch' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'object' => 'Predis\Command\ServerObject', + 'slowlog' => 'Predis\Command\ServerSlowlog', + + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'client' => 'Predis\Command\ServerClient', + + + /* ---------------- Redis 2.6 ---------------- */ + + /* commands operating on the key space */ + 'pttl' => 'Predis\Command\KeyPreciseTimeToLive', + 'pexpire' => 'Predis\Command\KeyPreciseExpire', + 'pexpireat' => 'Predis\Command\KeyPreciseExpireAt', + + /* commands operating on string values */ + 'psetex' => 'Predis\Command\StringPreciseSetExpire', + 'incrbyfloat' => 'Predis\Command\StringIncrementByFloat', + 'bitop' => 'Predis\Command\StringBitOp', + 'bitcount' => 'Predis\Command\StringBitCount', + + /* commands operating on hashes */ + 'hincrbyfloat' => 'Predis\Command\HashIncrementByFloat', + + /* scripting */ + 'eval' => 'Predis\Command\ServerEval', + 'evalsha' => 'Predis\Command\ServerEvalSHA', + 'script' => 'Predis\Command\ServerScript', + + /* remote server control commands */ + 'info' => 'Predis\Command\ServerInfoV26x', + 'time' => 'Predis\Command\ServerTime', + ); + } +} diff --git a/vendor/Predis/Profile/ServerVersionNext.php b/vendor/Predis/Profile/ServerVersionNext.php new file mode 100644 index 0000000..695388a --- /dev/null +++ b/vendor/Predis/Profile/ServerVersionNext.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Profile; + +/** + * Server profile for the current unstable version of Redis. + * + * @author Daniele Alessandri + */ +class ServerVersionNext extends ServerVersion26 +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.8'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array_merge(parent::getSupportedCommands(), array()); + } +} diff --git a/vendor/Predis/Protocol/CommandSerializerInterface.php b/vendor/Predis/Protocol/CommandSerializerInterface.php new file mode 100644 index 0000000..2671aec --- /dev/null +++ b/vendor/Predis/Protocol/CommandSerializerInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +use Predis\Command\CommandInterface; + +/** + * Interface that defines a custom serializer for Redis commands. + * + * @author Daniele Alessandri + */ +interface CommandSerializerInterface +{ + /** + * Serializes a Redis command. + * + * @param CommandInterface $command Redis command. + * @return string + */ + public function serialize(CommandInterface $command); +} diff --git a/vendor/Predis/Protocol/ComposableProtocolInterface.php b/vendor/Predis/Protocol/ComposableProtocolInterface.php new file mode 100644 index 0000000..56990a0 --- /dev/null +++ b/vendor/Predis/Protocol/ComposableProtocolInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +/** + * Interface that defines a customizable protocol processor that serializes + * Redis commands and parses replies returned by the server to PHP objects + * using a pluggable set of classes defining the underlying wire protocol. + * + * @author Daniele Alessandri + */ +interface ComposableProtocolInterface extends ProtocolInterface +{ + /** + * Sets the command serializer to be used by the protocol processor. + * + * @param CommandSerializerInterface $serializer Command serializer. + */ + public function setSerializer(CommandSerializerInterface $serializer); + + /** + * Returns the command serializer used by the protocol processor. + * + * @return CommandSerializerInterface + */ + public function getSerializer(); + + /** + * Sets the response reader to be used by the protocol processor. + * + * @param ResponseReaderInterface $reader Response reader. + */ + public function setReader(ResponseReaderInterface $reader); + + /** + * Returns the response reader used by the protocol processor. + * + * @return ResponseReaderInterface + */ + public function getReader(); +} diff --git a/vendor/Predis/Protocol/ProtocolException.php b/vendor/Predis/Protocol/ProtocolException.php new file mode 100644 index 0000000..6539021 --- /dev/null +++ b/vendor/Predis/Protocol/ProtocolException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +use Predis\CommunicationException; + +/** + * Exception class that identifies errors encountered while + * handling the Redis wire protocol. + * + * @author Daniele Alessandri + */ +class ProtocolException extends CommunicationException +{ +} diff --git a/vendor/Predis/Protocol/ProtocolInterface.php b/vendor/Predis/Protocol/ProtocolInterface.php new file mode 100644 index 0000000..5595d35 --- /dev/null +++ b/vendor/Predis/Protocol/ProtocolInterface.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +use Predis\Command\CommandInterface; +use Predis\Connection\ComposableConnectionInterface; + +/** + * Interface that defines a protocol processor that serializes Redis commands + * and parses replies returned by the server to PHP objects. + * + * @author Daniele Alessandri + */ +interface ProtocolInterface extends ResponseReaderInterface +{ + /** + * Writes a Redis command on the specified connection. + * + * @param ComposableConnectionInterface $connection Connection to Redis. + * @param CommandInterface $command Redis command. + */ + public function write(ComposableConnectionInterface $connection, CommandInterface $command); + + /** + * Sets the options for the protocol processor. + * + * @param string $option Name of the option. + * @param mixed $value Value of the option. + */ + public function setOption($option, $value); +} diff --git a/vendor/Predis/Protocol/ResponseHandlerInterface.php b/vendor/Predis/Protocol/ResponseHandlerInterface.php new file mode 100644 index 0000000..0ea2ca4 --- /dev/null +++ b/vendor/Predis/Protocol/ResponseHandlerInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +use Predis\Connection\ComposableConnectionInterface; + +/** + * Interface that defines an handler able to parse a reply. + * + * @author Daniele Alessandri + */ +interface ResponseHandlerInterface +{ + /** + * Parses a type of reply returned by Redis and reads more data from the + * connection if needed. + * + * @param ComposableConnectionInterface $connection Connection to Redis. + * @param string $payload Initial payload of the reply. + * @return mixed + */ + function handle(ComposableConnectionInterface $connection, $payload); +} diff --git a/vendor/Predis/Protocol/ResponseReaderInterface.php b/vendor/Predis/Protocol/ResponseReaderInterface.php new file mode 100644 index 0000000..0428112 --- /dev/null +++ b/vendor/Predis/Protocol/ResponseReaderInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol; + +use Predis\Connection\ComposableConnectionInterface; + +/** + * Interface that defines a response reader able to parse replies returned by + * Redis and deserialize them to PHP objects. + * + * @author Daniele Alessandri + */ +interface ResponseReaderInterface +{ + /** + * Reads replies from a connection to Redis and deserializes them. + * + * @param ComposableConnectionInterface $connection Connection to Redis. + * @return mixed + */ + public function read(ComposableConnectionInterface $connection); +} diff --git a/vendor/Predis/Protocol/Text/ComposableTextProtocol.php b/vendor/Predis/Protocol/Text/ComposableTextProtocol.php new file mode 100644 index 0000000..1429990 --- /dev/null +++ b/vendor/Predis/Protocol/Text/ComposableTextProtocol.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\Command\CommandInterface; +use Predis\Connection\ComposableConnectionInterface; +use Predis\Protocol\ResponseReaderInterface; +use Predis\Protocol\CommandSerializerInterface; +use Predis\Protocol\ComposableProtocolInterface; + +/** + * Implements a customizable protocol processor that uses the standard Redis + * wire protocol to serialize Redis commands and parse replies returned by + * the server using a pluggable set of classes. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class ComposableTextProtocol implements ComposableProtocolInterface +{ + private $serializer; + private $reader; + + /** + * @param array $options Set of options used to initialize the protocol processor. + */ + public function __construct(Array $options = array()) + { + $this->setSerializer(new TextCommandSerializer()); + $this->setReader(new TextResponseReader()); + + if (count($options) > 0) { + $this->initializeOptions($options); + } + } + + /** + * Initializes the protocol processor using a set of options. + * + * @param array $options Set of options. + */ + private function initializeOptions(Array $options) + { + foreach ($options as $k => $v) { + $this->setOption($k, $v); + } + } + + /** + * {@inheritdoc} + */ + public function setOption($option, $value) + { + switch ($option) { + case 'iterable_multibulk': + $handler = $value ? new ResponseMultiBulkStreamHandler() : new ResponseMultiBulkHandler(); + $this->reader->setHandler(TextProtocol::PREFIX_MULTI_BULK, $handler); + break; + + default: + throw new \InvalidArgumentException("The option $option is not supported by the current protocol"); + } + } + + /** + * {@inheritdoc} + */ + public function serialize(CommandInterface $command) + { + return $this->serializer->serialize($command); + } + + /** + * {@inheritdoc} + */ + public function write(ComposableConnectionInterface $connection, CommandInterface $command) + { + $connection->writeBytes($this->serializer->serialize($command)); + } + + /** + * {@inheritdoc} + */ + public function read(ComposableConnectionInterface $connection) + { + return $this->reader->read($connection); + } + + /** + * {@inheritdoc} + */ + public function setSerializer(CommandSerializerInterface $serializer) + { + $this->serializer = $serializer; + } + + /** + * {@inheritdoc} + */ + public function getSerializer() + { + return $this->serializer; + } + + /** + * {@inheritdoc} + */ + public function setReader(ResponseReaderInterface $reader) + { + $this->reader = $reader; + } + + /** + * {@inheritdoc} + */ + public function getReader() + { + return $this->reader; + } +} diff --git a/vendor/Predis/Protocol/Text/ResponseBulkHandler.php b/vendor/Predis/Protocol/Text/ResponseBulkHandler.php new file mode 100644 index 0000000..7b9a965 --- /dev/null +++ b/vendor/Predis/Protocol/Text/ResponseBulkHandler.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\CommunicationException; +use Predis\Connection\ComposableConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ResponseHandlerInterface; + +/** + * Implements a response handler for bulk replies using the standard wire + * protocol defined by Redis. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class ResponseBulkHandler implements ResponseHandlerInterface +{ + /** + * Handles a bulk reply returned by Redis. + * + * @param ComposableConnectionInterface $connection Connection to Redis. + * @param string $lengthString Bytes size of the bulk reply. + * @return string + */ + public function handle(ComposableConnectionInterface $connection, $lengthString) + { + $length = (int) $lengthString; + + if ("$length" !== $lengthString) { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$lengthString' as bulk length" + )); + } + + if ($length >= 0) { + return substr($connection->readBytes($length + 2), 0, -2); + } + + if ($length == -1) { + return null; + } + } +} diff --git a/vendor/Predis/Protocol/Text/ResponseErrorHandler.php b/vendor/Predis/Protocol/Text/ResponseErrorHandler.php new file mode 100644 index 0000000..a1e27d3 --- /dev/null +++ b/vendor/Predis/Protocol/Text/ResponseErrorHandler.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\ResponseError; +use Predis\Connection\ComposableConnectionInterface; +use Predis\Protocol\ResponseHandlerInterface; + +/** + * Implements a response handler for error replies using the standard wire + * protocol defined by Redis. + * + * This handler returns a reply object to notify the user that an error has + * occurred on the server. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class ResponseErrorHandler implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(ComposableConnectionInterface $connection, $errorMessage) + { + return new ResponseError($errorMessage); + } +} diff --git a/vendor/Predis/Protocol/Text/ResponseIntegerHandler.php b/vendor/Predis/Protocol/Text/ResponseIntegerHandler.php new file mode 100644 index 0000000..36556f3 --- /dev/null +++ b/vendor/Predis/Protocol/Text/ResponseIntegerHandler.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\CommunicationException; +use Predis\Connection\ComposableConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ResponseHandlerInterface; + +/** + * Implements a response handler for integer replies using the standard wire + * protocol defined by Redis. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class ResponseIntegerHandler implements ResponseHandlerInterface +{ + /** + * Handles an integer reply returned by Redis. + * + * @param ComposableConnectionInterface $connection Connection to Redis. + * @param string $number String representation of an integer. + * @return int + */ + public function handle(ComposableConnectionInterface $connection, $number) + { + if (is_numeric($number)) { + return (int) $number; + } + + if ($number !== 'nil') { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$number' as numeric response" + )); + } + + return null; + } +} diff --git a/vendor/Predis/Protocol/Text/ResponseMultiBulkHandler.php b/vendor/Predis/Protocol/Text/ResponseMultiBulkHandler.php new file mode 100644 index 0000000..2f31f32 --- /dev/null +++ b/vendor/Predis/Protocol/Text/ResponseMultiBulkHandler.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\CommunicationException; +use Predis\Connection\ComposableConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ResponseHandlerInterface; + +/** + * Implements a response handler for multi-bulk replies using the standard + * wire protocol defined by Redis. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class ResponseMultiBulkHandler implements ResponseHandlerInterface +{ + /** + * Handles a multi-bulk reply returned by Redis. + * + * @param ComposableConnectionInterface $connection Connection to Redis. + * @param string $lengthString Number of items in the multi-bulk reply. + * @return array + */ + public function handle(ComposableConnectionInterface $connection, $lengthString) + { + $length = (int) $lengthString; + + if ("$length" !== $lengthString) { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$lengthString' as multi-bulk length" + )); + } + + if ($length === -1) { + return null; + } + + $list = array(); + + if ($length > 0) { + $handlersCache = array(); + $reader = $connection->getProtocol()->getReader(); + + for ($i = 0; $i < $length; $i++) { + $header = $connection->readLine(); + $prefix = $header[0]; + + if (isset($handlersCache[$prefix])) { + $handler = $handlersCache[$prefix]; + } else { + $handler = $reader->getHandler($prefix); + $handlersCache[$prefix] = $handler; + } + + $list[$i] = $handler->handle($connection, substr($header, 1)); + } + } + + return $list; + } +} diff --git a/vendor/Predis/Protocol/Text/ResponseMultiBulkStreamHandler.php b/vendor/Predis/Protocol/Text/ResponseMultiBulkStreamHandler.php new file mode 100644 index 0000000..77bc19f --- /dev/null +++ b/vendor/Predis/Protocol/Text/ResponseMultiBulkStreamHandler.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\CommunicationException; +use Predis\Connection\ComposableConnectionInterface; +use Predis\Iterator\MultiBulkResponseSimple; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ResponseHandlerInterface; + +/** + * Implements a response handler for iterable multi-bulk replies using the + * standard wire protocol defined by Redis. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class ResponseMultiBulkStreamHandler implements ResponseHandlerInterface +{ + /** + * Handles a multi-bulk reply returned by Redis in a streamable fashion. + * + * @param ComposableConnectionInterface $connection Connection to Redis. + * @param string $lengthString Number of items in the multi-bulk reply. + * @return MultiBulkResponseSimple + */ + public function handle(ComposableConnectionInterface $connection, $lengthString) + { + $length = (int) $lengthString; + + if ("$length" != $lengthString) { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$lengthString' as multi-bulk length" + )); + } + + return new MultiBulkResponseSimple($connection, $length); + } +} diff --git a/vendor/Predis/Protocol/Text/ResponseStatusHandler.php b/vendor/Predis/Protocol/Text/ResponseStatusHandler.php new file mode 100644 index 0000000..978996a --- /dev/null +++ b/vendor/Predis/Protocol/Text/ResponseStatusHandler.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\ResponseQueued; +use Predis\Connection\ComposableConnectionInterface; +use Predis\Protocol\ResponseHandlerInterface; + +/** + * Implements a response handler for status replies using the standard wire + * protocol defined by Redis. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class ResponseStatusHandler implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(ComposableConnectionInterface $connection, $status) + { + switch ($status) { + case 'OK': + return true; + + case 'QUEUED': + return new ResponseQueued(); + + default: + return $status; + } + } +} diff --git a/vendor/Predis/Protocol/Text/TextCommandSerializer.php b/vendor/Predis/Protocol/Text/TextCommandSerializer.php new file mode 100644 index 0000000..c16b277 --- /dev/null +++ b/vendor/Predis/Protocol/Text/TextCommandSerializer.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\Command\CommandInterface; +use Predis\Protocol\CommandSerializerInterface; + +/** + * Implements a pluggable command serializer using the standard wire protocol + * defined by Redis. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class TextCommandSerializer implements CommandSerializerInterface +{ + /** + * {@inheritdoc} + */ + public function serialize(CommandInterface $command) + { + $commandId = $command->getId(); + $arguments = $command->getArguments(); + + $cmdlen = strlen($commandId); + $reqlen = count($arguments) + 1; + + $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandId}\r\n"; + + for ($i = 0; $i < $reqlen - 1; $i++) { + $argument = $arguments[$i]; + $arglen = strlen($argument); + $buffer .= "\${$arglen}\r\n{$argument}\r\n"; + } + + return $buffer; + } +} diff --git a/vendor/Predis/Protocol/Text/TextProtocol.php b/vendor/Predis/Protocol/Text/TextProtocol.php new file mode 100644 index 0000000..04d5bca --- /dev/null +++ b/vendor/Predis/Protocol/Text/TextProtocol.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\CommunicationException; +use Predis\ResponseError; +use Predis\ResponseQueued; +use Predis\Command\CommandInterface; +use Predis\Connection\ComposableConnectionInterface; +use Predis\Iterator\MultiBulkResponseSimple; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ProtocolInterface; + +/** + * Implements a protocol processor for the standard wire protocol defined by Redis. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class TextProtocol implements ProtocolInterface +{ + const NEWLINE = "\r\n"; + const OK = 'OK'; + const ERROR = 'ERR'; + const QUEUED = 'QUEUED'; + const NULL = 'nil'; + + const PREFIX_STATUS = '+'; + const PREFIX_ERROR = '-'; + const PREFIX_INTEGER = ':'; + const PREFIX_BULK = '$'; + const PREFIX_MULTI_BULK = '*'; + + const BUFFER_SIZE = 4096; + + private $mbiterable; + private $serializer; + + /** + * + */ + public function __construct() + { + $this->mbiterable = false; + $this->serializer = new TextCommandSerializer(); + } + + /** + * {@inheritdoc} + */ + public function write(ComposableConnectionInterface $connection, CommandInterface $command) + { + $connection->writeBytes($this->serializer->serialize($command)); + } + + /** + * {@inheritdoc} + */ + public function read(ComposableConnectionInterface $connection) + { + $chunk = $connection->readLine(); + $prefix = $chunk[0]; + $payload = substr($chunk, 1); + + switch ($prefix) { + case '+': // inline + switch ($payload) { + case 'OK': + return true; + + case 'QUEUED': + return new ResponseQueued(); + + default: + return $payload; + } + + case '$': // bulk + $size = (int) $payload; + if ($size === -1) { + return null; + } + return substr($connection->readBytes($size + 2), 0, -2); + + case '*': // multi bulk + $count = (int) $payload; + + if ($count === -1) { + return null; + } + if ($this->mbiterable) { + return new MultiBulkResponseSimple($connection, $count); + } + + $multibulk = array(); + + for ($i = 0; $i < $count; $i++) { + $multibulk[$i] = $this->read($connection); + } + + return $multibulk; + + case ':': // integer + return (int) $payload; + + case '-': // error + return new ResponseError($payload); + + default: + CommunicationException::handle(new ProtocolException( + $connection, "Unknown prefix: '$prefix'" + )); + } + } + + /** + * {@inheritdoc} + */ + public function setOption($option, $value) + { + switch ($option) { + case 'iterable_multibulk': + $this->mbiterable = (bool) $value; + break; + } + } +} diff --git a/vendor/Predis/Protocol/Text/TextResponseReader.php b/vendor/Predis/Protocol/Text/TextResponseReader.php new file mode 100644 index 0000000..5364a21 --- /dev/null +++ b/vendor/Predis/Protocol/Text/TextResponseReader.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Protocol\Text; + +use Predis\CommunicationException; +use Predis\Connection\ComposableConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ResponseHandlerInterface; +use Predis\Protocol\ResponseReaderInterface; + +/** + * Implements a pluggable response reader using the standard wire protocol + * defined by Redis. + * + * @link http://redis.io/topics/protocol + * @author Daniele Alessandri + */ +class TextResponseReader implements ResponseReaderInterface +{ + private $handlers; + + /** + * + */ + public function __construct() + { + $this->handlers = $this->getDefaultHandlers(); + } + + /** + * Returns the default set of response handlers for all the type of replies + * that can be returned by Redis. + */ + private function getDefaultHandlers() + { + return array( + TextProtocol::PREFIX_STATUS => new ResponseStatusHandler(), + TextProtocol::PREFIX_ERROR => new ResponseErrorHandler(), + TextProtocol::PREFIX_INTEGER => new ResponseIntegerHandler(), + TextProtocol::PREFIX_BULK => new ResponseBulkHandler(), + TextProtocol::PREFIX_MULTI_BULK => new ResponseMultiBulkHandler(), + ); + } + + /** + * Sets a response handler for a certain prefix that identifies a type of + * reply that can be returned by Redis. + * + * @param string $prefix Identifier for a type of reply. + * @param ResponseHandlerInterface $handler Response handler for the reply. + */ + public function setHandler($prefix, ResponseHandlerInterface $handler) + { + $this->handlers[$prefix] = $handler; + } + + /** + * Returns the response handler associated to a certain type of reply that + * can be returned by Redis. + * + * @param string $prefix Identifier for a type of reply. + * @return ResponseHandlerInterface + */ + public function getHandler($prefix) + { + if (isset($this->handlers[$prefix])) { + return $this->handlers[$prefix]; + } + } + + /** + * {@inheritdoc} + */ + public function read(ComposableConnectionInterface $connection) + { + $header = $connection->readLine(); + + if ($header === '') { + $this->protocolError($connection, 'Unexpected empty header'); + } + + $prefix = $header[0]; + + if (!isset($this->handlers[$prefix])) { + $this->protocolError($connection, "Unknown prefix: '$prefix'"); + } + + $handler = $this->handlers[$prefix]; + + return $handler->handle($connection, substr($header, 1)); + } + + /** + * Helper method used to handle a protocol error generated while reading a + * reply from a connection to Redis. + * + * @param ComposableConnectionInterface $connection Connection to Redis that generated the error. + * @param string $message Error message. + */ + private function protocolError(ComposableConnectionInterface $connection, $message) + { + CommunicationException::handle(new ProtocolException($connection, $message)); + } +} diff --git a/vendor/Predis/PubSub/AbstractPubSubContext.php b/vendor/Predis/PubSub/AbstractPubSubContext.php new file mode 100644 index 0000000..87186b2 --- /dev/null +++ b/vendor/Predis/PubSub/AbstractPubSubContext.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\PubSub; + +/** + * Client-side abstraction of a Publish / Subscribe context. + * + * @author Daniele Alessandri + */ +abstract class AbstractPubSubContext implements \Iterator +{ + const SUBSCRIBE = 'subscribe'; + const UNSUBSCRIBE = 'unsubscribe'; + const PSUBSCRIBE = 'psubscribe'; + const PUNSUBSCRIBE = 'punsubscribe'; + const MESSAGE = 'message'; + const PMESSAGE = 'pmessage'; + + const STATUS_VALID = 1; // 0b0001 + const STATUS_SUBSCRIBED = 2; // 0b0010 + const STATUS_PSUBSCRIBED = 4; // 0b0100 + + private $position = null; + private $statusFlags = self::STATUS_VALID; + + /** + * Automatically closes the context when PHP's garbage collector kicks in. + */ + public function __destruct() + { + $this->closeContext(true); + } + + /** + * Checks if the specified flag is valid in the state of the context. + * + * @param int $value Flag. + * @return Boolean + */ + protected function isFlagSet($value) + { + return ($this->statusFlags & $value) === $value; + } + + /** + * Subscribes to the specified channels. + * + * @param mixed $arg,... One or more channel names. + */ + public function subscribe(/* arguments */) + { + $this->writeCommand(self::SUBSCRIBE, func_get_args()); + $this->statusFlags |= self::STATUS_SUBSCRIBED; + } + + /** + * Unsubscribes from the specified channels. + * + * @param mixed $arg,... One or more channel names. + */ + public function unsubscribe(/* arguments */) + { + $this->writeCommand(self::UNSUBSCRIBE, func_get_args()); + } + + /** + * Subscribes to the specified channels using a pattern. + * + * @param mixed $arg,... One or more channel name patterns. + */ + public function psubscribe(/* arguments */) + { + $this->writeCommand(self::PSUBSCRIBE, func_get_args()); + $this->statusFlags |= self::STATUS_PSUBSCRIBED; + } + + /** + * Unsubscribes from the specified channels using a pattern. + * + * @param mixed $arg,... One or more channel name patterns. + */ + public function punsubscribe(/* arguments */) + { + $this->writeCommand(self::PUNSUBSCRIBE, func_get_args()); + } + + /** + * Closes the context by unsubscribing from all the subscribed channels. + * Optionally, the context can be forcefully closed by dropping the + * underlying connection. + * + * @param Boolean $force Forcefully close the context by closing the connection. + * @return Boolean Returns false if there are no pending messages. + */ + public function closeContext($force = false) + { + if (!$this->valid()) { + return false; + } + + if ($force) { + $this->invalidate(); + $this->disconnect(); + } else { + if ($this->isFlagSet(self::STATUS_SUBSCRIBED)) { + $this->unsubscribe(); + } + if ($this->isFlagSet(self::STATUS_PSUBSCRIBED)) { + $this->punsubscribe(); + } + } + + return !$force; + } + + /** + * Closes the underlying connection on forced disconnection. + */ + protected abstract function disconnect(); + + /** + * Writes a Redis command on the underlying connection. + * + * @param string $method ID of the command. + * @param array $arguments List of arguments. + */ + protected abstract function writeCommand($method, $arguments); + + /** + * {@inheritdoc} + */ + public function rewind() + { + // NOOP + } + + /** + * Returns the last message payload retrieved from the server and generated + * by one of the active subscriptions. + * + * @return array + */ + public function current() + { + return $this->getValue(); + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + if ($this->valid()) { + $this->position++; + } + + return $this->position; + } + + /** + * Checks if the the context is still in a valid state to continue. + * + * @return Boolean + */ + public function valid() + { + $isValid = $this->isFlagSet(self::STATUS_VALID); + $subscriptionFlags = self::STATUS_SUBSCRIBED | self::STATUS_PSUBSCRIBED; + $hasSubscriptions = ($this->statusFlags & $subscriptionFlags) > 0; + + return $isValid && $hasSubscriptions; + } + + /** + * Resets the state of the context. + */ + protected function invalidate() + { + $this->statusFlags = 0; // 0b0000; + } + + /** + * Waits for a new message from the server generated by one of the active + * subscriptions and returns it when available. + * + * @return array + */ + protected abstract function getValue(); +} diff --git a/vendor/Predis/PubSub/DispatcherLoop.php b/vendor/Predis/PubSub/DispatcherLoop.php new file mode 100644 index 0000000..3254897 --- /dev/null +++ b/vendor/Predis/PubSub/DispatcherLoop.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\PubSub; + +use Predis\ClientInterface; + +/** + * Method-dispatcher loop built around the client-side abstraction of a Redis + * Publish / Subscribe context. + * + * @author Daniele Alessandri + */ +class DispatcherLoop +{ + private $pubSubContext; + + protected $callbacks; + protected $defaultCallback; + protected $subscriptionCallback; + + /** + * @param ClientInterface $client Client instance used by the context. + */ + public function __construct(ClientInterface $client) + { + $this->callbacks = array(); + $this->pubSubContext = $client->pubSub(); + } + + /** + * Checks if the passed argument is a valid callback. + * + * @param mixed $callable A callback. + */ + protected function validateCallback($callable) + { + if (!is_callable($callable)) { + throw new \InvalidArgumentException("A valid callable object must be provided"); + } + } + + /** + * Returns the underlying Publish / Subscribe context. + * + * @return PubSubContext + */ + public function getPubSubContext() + { + return $this->pubSubContext; + } + + /** + * Sets a callback that gets invoked upon new subscriptions. + * + * @param mixed $callable A callback. + */ + public function subscriptionCallback($callable = null) + { + if (isset($callable)) { + $this->validateCallback($callable); + } + + $this->subscriptionCallback = $callable; + } + + /** + * Sets a callback that gets invoked when a message is received on a + * channel that does not have an associated callback. + * + * @param mixed $callable A callback. + */ + public function defaultCallback($callable = null) + { + if (isset($callable)) { + $this->validateCallback($callable); + } + + $this->subscriptionCallback = $callable; + } + + /** + * Binds a callback to a channel. + * + * @param string $channel Channel name. + * @param Callable $callback A callback. + */ + public function attachCallback($channel, $callback) + { + $callbackName = $this->getPrefixKeys() . $channel; + + $this->validateCallback($callback); + $this->callbacks[$callbackName] = $callback; + $this->pubSubContext->subscribe($channel); + } + + /** + * Stops listening to a channel and removes the associated callback. + * + * @param string $channel Redis channel. + */ + public function detachCallback($channel) + { + $callbackName = $this->getPrefixKeys() . $channel; + + if (isset($this->callbacks[$callbackName])) { + unset($this->callbacks[$callbackName]); + $this->pubSubContext->unsubscribe($channel); + } + } + + /** + * Starts the dispatcher loop. + */ + public function run() + { + foreach ($this->pubSubContext as $message) { + $kind = $message->kind; + + if ($kind !== PubSubContext::MESSAGE && $kind !== PubSubContext::PMESSAGE) { + if (isset($this->subscriptionCallback)) { + $callback = $this->subscriptionCallback; + call_user_func($callback, $message); + } + + continue; + } + + if (isset($this->callbacks[$message->channel])) { + $callback = $this->callbacks[$message->channel]; + call_user_func($callback, $message->payload); + } else if (isset($this->defaultCallback)) { + $callback = $this->defaultCallback; + call_user_func($callback, $message); + } + } + } + + /** + * Terminates the dispatcher loop. + */ + public function stop() + { + $this->pubSubContext->closeContext(); + } + + /** + * Return the prefix of the keys + * + * @return string + */ + protected function getPrefixKeys() + { + $options = $this->pubSubContext->getClient()->getOptions(); + + if (isset($options->prefix)) { + return $options->prefix->getPrefix(); + } + + return ''; + } +} diff --git a/vendor/Predis/PubSub/PubSubContext.php b/vendor/Predis/PubSub/PubSubContext.php new file mode 100644 index 0000000..50f8dc1 --- /dev/null +++ b/vendor/Predis/PubSub/PubSubContext.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\PubSub; + +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\Command\AbstractCommand as Command; +use Predis\NotSupportedException; +use Predis\Connection\AggregatedConnectionInterface; + +/** + * Client-side abstraction of a Publish / Subscribe context. + * + * @author Daniele Alessandri + */ +class PubSubContext extends AbstractPubSubContext +{ + private $client; + private $options; + + /** + * @param ClientInterface $client Client instance used by the context. + * @param array $options Options for the context initialization. + */ + public function __construct(ClientInterface $client, Array $options = null) + { + $this->checkCapabilities($client); + $this->options = $options ?: array(); + $this->client = $client; + + $this->genericSubscribeInit('subscribe'); + $this->genericSubscribeInit('psubscribe'); + } + + /** + * Returns the underlying client instance used by the pub/sub iterator. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->client; + } + + /** + * Checks if the passed client instance satisfies the required conditions + * needed to initialize a Publish / Subscribe context. + * + * @param ClientInterface $client Client instance used by the context. + */ + private function checkCapabilities(ClientInterface $client) + { + if ($client->getConnection() instanceof AggregatedConnectionInterface) { + throw new NotSupportedException('Cannot initialize a PUB/SUB context when using aggregated connections'); + } + + $commands = array('publish', 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe'); + + if ($client->getProfile()->supportsCommands($commands) === false) { + throw new NotSupportedException('The current profile does not support PUB/SUB related commands'); + } + } + + /** + * This method shares the logic to handle both SUBSCRIBE and PSUBSCRIBE. + * + * @param string $subscribeAction Type of subscription. + */ + private function genericSubscribeInit($subscribeAction) + { + if (isset($this->options[$subscribeAction])) { + $this->$subscribeAction($this->options[$subscribeAction]); + } + } + + /** + * {@inheritdoc} + */ + protected function writeCommand($method, $arguments) + { + $arguments = Command::normalizeArguments($arguments); + $command = $this->client->createCommand($method, $arguments); + $this->client->getConnection()->writeCommand($command); + } + + /** + * {@inheritdoc} + */ + protected function disconnect() + { + $this->client->disconnect(); + } + + /** + * {@inheritdoc} + */ + protected function getValue() + { + $response = $this->client->getConnection()->read(); + + switch ($response[0]) { + case self::SUBSCRIBE: + case self::UNSUBSCRIBE: + case self::PSUBSCRIBE: + case self::PUNSUBSCRIBE: + if ($response[2] === 0) { + $this->invalidate(); + } + + case self::MESSAGE: + return (object) array( + 'kind' => $response[0], + 'channel' => $response[1], + 'payload' => $response[2], + ); + + case self::PMESSAGE: + return (object) array( + 'kind' => $response[0], + 'pattern' => $response[1], + 'channel' => $response[2], + 'payload' => $response[3], + ); + + default: + $message = "Received an unknown message type {$response[0]} inside of a pubsub context"; + throw new ClientException($message); + } + } +} diff --git a/vendor/Predis/Replication/ReplicationStrategy.php b/vendor/Predis/Replication/ReplicationStrategy.php new file mode 100644 index 0000000..3d45eaf --- /dev/null +++ b/vendor/Predis/Replication/ReplicationStrategy.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Replication; + +use Predis\NotSupportedException; +use Predis\Command\CommandInterface; + +/** + * Defines a strategy for master/reply replication. + * + * @author Daniele Alessandri + */ +class ReplicationStrategy +{ + protected $disallowed; + protected $readonly; + protected $readonlySHA1; + + /** + * + */ + public function __construct() + { + $this->disallowed = $this->getDisallowedOperations(); + $this->readonly = $this->getReadOnlyOperations(); + $this->readonlySHA1 = array(); + } + + /** + * Returns if the specified command performs a read-only operation + * against a key stored on Redis. + * + * @param CommandInterface $command Instance of Redis command. + * @return Boolean + */ + public function isReadOperation(CommandInterface $command) + { + if (isset($this->disallowed[$id = $command->getId()])) { + throw new NotSupportedException("The command $id is not allowed in replication mode"); + } + + if (isset($this->readonly[$id])) { + if (true === $readonly = $this->readonly[$id]) { + return true; + } + + return call_user_func($readonly, $command); + } + + if (($eval = $id === 'EVAL') || $id === 'EVALSHA') { + $sha1 = $eval ? sha1($command->getArgument(0)) : $command->getArgument(0); + + if (isset($this->readonlySHA1[$sha1])) { + if (true === $readonly = $this->readonlySHA1[$sha1]) { + return true; + } + + return call_user_func($readonly, $command); + } + } + + return false; + } + + /** + * Returns if the specified command is disallowed in a master/slave + * replication context. + * + * @param CommandInterface $command Instance of Redis command. + * @return Boolean + */ + public function isDisallowedOperation(CommandInterface $command) + { + return isset($this->disallowed[$command->getId()]); + } + + /** + * Checks if a SORT command is a readable operation by parsing the arguments + * array of the specified commad instance. + * + * @param CommandInterface $command Instance of Redis command. + * @return Boolean + */ + protected function isSortReadOnly(CommandInterface $command) + { + $arguments = $command->getArguments(); + return ($c = count($arguments)) === 1 ? true : $arguments[$c - 2] !== 'STORE'; + } + + /** + * Marks a command as a read-only operation. When the behaviour of a + * command can be decided only at runtime depending on its arguments, + * a callable object can be provided to dynamically check if the passed + * instance of a command performs write operations or not. + * + * @param string $commandID ID of the command. + * @param mixed $readonly A boolean or a callable object. + */ + public function setCommandReadOnly($commandID, $readonly = true) + { + $commandID = strtoupper($commandID); + + if ($readonly) { + $this->readonly[$commandID] = $readonly; + } else { + unset($this->readonly[$commandID]); + } + } + + /** + * Marks a Lua script for EVAL and EVALSHA as a read-only operation. When + * the behaviour of a script can be decided only at runtime depending on + * its arguments, a callable object can be provided to dynamically check + * if the passed instance of EVAL or EVALSHA performs write operations or + * not. + * + * @param string $script Body of the Lua script. + * @param mixed $readonly A boolean or a callable object. + */ + public function setScriptReadOnly($script, $readonly = true) + { + $sha1 = sha1($script); + + if ($readonly) { + $this->readonlySHA1[$sha1] = $readonly; + } else { + unset($this->readonlySHA1[$sha1]); + } + } + + /** + * Returns the default list of disallowed commands. + * + * @return array + */ + protected function getDisallowedOperations() + { + return array( + 'SHUTDOWN' => true, + 'INFO' => true, + 'DBSIZE' => true, + 'LASTSAVE' => true, + 'CONFIG' => true, + 'MONITOR' => true, + 'SLAVEOF' => true, + 'SAVE' => true, + 'BGSAVE' => true, + 'BGREWRITEAOF' => true, + 'SLOWLOG' => true, + ); + } + + /** + * Returns the default list of commands performing read-only operations. + * + * @return array + */ + protected function getReadOnlyOperations() + { + return array( + 'EXISTS' => true, + 'TYPE' => true, + 'KEYS' => true, + 'RANDOMKEY' => true, + 'TTL' => true, + 'GET' => true, + 'MGET' => true, + 'SUBSTR' => true, + 'STRLEN' => true, + 'GETRANGE' => true, + 'GETBIT' => true, + 'LLEN' => true, + 'LRANGE' => true, + 'LINDEX' => true, + 'SCARD' => true, + 'SISMEMBER' => true, + 'SINTER' => true, + 'SUNION' => true, + 'SDIFF' => true, + 'SMEMBERS' => true, + 'SRANDMEMBER' => true, + 'ZRANGE' => true, + 'ZREVRANGE' => true, + 'ZRANGEBYSCORE' => true, + 'ZREVRANGEBYSCORE' => true, + 'ZCARD' => true, + 'ZSCORE' => true, + 'ZCOUNT' => true, + 'ZRANK' => true, + 'ZREVRANK' => true, + 'HGET' => true, + 'HMGET' => true, + 'HEXISTS' => true, + 'HLEN' => true, + 'HKEYS' => true, + 'HVALS' => true, + 'HGETALL' => true, + 'PING' => true, + 'AUTH' => true, + 'SELECT' => true, + 'ECHO' => true, + 'QUIT' => true, + 'OBJECT' => true, + 'BITCOUNT' => true, + 'TIME' => true, + 'SORT' => array($this, 'isSortReadOnly'), + ); + } +} diff --git a/vendor/Predis/ResponseError.php b/vendor/Predis/ResponseError.php new file mode 100644 index 0000000..f6f310e --- /dev/null +++ b/vendor/Predis/ResponseError.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Represents an error returned by Redis (-ERR replies) during the execution + * of a command on the server. + * + * @author Daniele Alessandri + */ +class ResponseError implements ResponseErrorInterface +{ + private $message; + + /** + * @param string $message Error message returned by Redis + */ + public function __construct($message) + { + $this->message = $message; + } + + /** + * {@inheritdoc} + */ + public function getMessage() + { + return $this->message; + } + + /** + * {@inheritdoc} + */ + public function getErrorType() + { + list($errorType, ) = explode(' ', $this->getMessage(), 2); + return $errorType; + } + + /** + * Converts the object to its string representation. + * + * @return string + */ + public function __toString() + { + return $this->getMessage(); + } +} diff --git a/vendor/Predis/ResponseErrorInterface.php b/vendor/Predis/ResponseErrorInterface.php new file mode 100644 index 0000000..8c1a005 --- /dev/null +++ b/vendor/Predis/ResponseErrorInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Represents an error returned by Redis (replies identified by "-" in the + * Redis response protocol) during the execution of an operation on the server. + * + * @author Daniele Alessandri + */ +interface ResponseErrorInterface extends ResponseObjectInterface +{ + /** + * Returns the error message + * + * @return string + */ + public function getMessage(); + + /** + * Returns the error type (e.g. ERR, ASK, MOVED) + * + * @return string + */ + public function getErrorType(); +} diff --git a/vendor/Predis/ResponseObjectInterface.php b/vendor/Predis/ResponseObjectInterface.php new file mode 100644 index 0000000..ba81783 --- /dev/null +++ b/vendor/Predis/ResponseObjectInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Represents a complex reply object from Redis. + * + * @author Daniele Alessandri + */ +interface ResponseObjectInterface +{ +} diff --git a/vendor/Predis/ResponseQueued.php b/vendor/Predis/ResponseQueued.php new file mode 100644 index 0000000..c9786fb --- /dev/null +++ b/vendor/Predis/ResponseQueued.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Represents a +QUEUED response returned by Redis as a reply to each command + * executed inside a MULTI/ EXEC transaction. + * + * @author Daniele Alessandri + */ +class ResponseQueued implements ResponseObjectInterface +{ + /** + * Converts the object to its string representation. + * + * @return string + */ + public function __toString() + { + return 'QUEUED'; + } + + /** + * Returns the value of the specified property. + * + * @param string $property Name of the property. + * @return mixed + */ + public function __get($property) + { + return $property === 'queued'; + } + + /** + * Checks if the specified property is set. + * + * @param string $property Name of the property. + * @return Boolean + */ + public function __isset($property) + { + return $property === 'queued'; + } +} diff --git a/vendor/Predis/ServerException.php b/vendor/Predis/ServerException.php new file mode 100644 index 0000000..0a60c7e --- /dev/null +++ b/vendor/Predis/ServerException.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis; + +/** + * Exception class that identifies server-side Redis errors. + * + * @author Daniele Alessandri + */ +class ServerException extends PredisException implements ResponseErrorInterface +{ + /** + * Gets the type of the error returned by Redis. + * + * @return string + */ + public function getErrorType() + { + list($errorType, ) = explode(' ', $this->getMessage(), 2); + + return $errorType; + } + + /** + * Converts the exception to an instance of ResponseError. + * + * @return ResponseError + */ + public function toResponseError() + { + return new ResponseError($this->getMessage()); + } +} diff --git a/vendor/Predis/Session/SessionHandler.php b/vendor/Predis/Session/SessionHandler.php new file mode 100644 index 0000000..eba793a --- /dev/null +++ b/vendor/Predis/Session/SessionHandler.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Session; + +use SessionHandlerInterface; +use Predis\ClientInterface; + +/** + * Session handler class that relies on Predis\Client to store PHP's sessions + * data into one or multiple Redis servers. + * + * This class is mostly intended for PHP 5.4 but it can be used under PHP 5.3 provided + * that a polyfill for `SessionHandlerInterface` is defined by either you or an external + * package such as `symfony/http-foundation`. + * + * @author Daniele Alessandri + */ +class SessionHandler implements SessionHandlerInterface +{ + protected $client; + protected $ttl; + + /** + * @param ClientInterface $client Fully initialized client instance. + * @param array $options Session handler options. + */ + public function __construct(ClientInterface $client, Array $options = array()) + { + $this->client = $client; + $this->ttl = (int) (isset($options['gc_maxlifetime']) ? $options['gc_maxlifetime'] : ini_get('session.gc_maxlifetime')); + } + + /** + * Registers the handler instance as the current session handler. + */ + public function register() + { + if (version_compare(PHP_VERSION, '5.4.0') >= 0) { + session_set_save_handler($this, true); + } else { + session_set_save_handler( + array($this, 'open'), + array($this, 'close'), + array($this, 'read'), + array($this, 'write'), + array($this, 'destroy'), + array($this, 'gc') + ); + } + } + + /** + * {@inheritdoc} + */ + public function open($save_path, $session_id) + { + // NOOP + + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + // NOOP + + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // NOOP + + return true; + } + + /** + * {@inheritdoc} + */ + public function read($session_id) + { + if ($data = $this->client->get($session_id)) { + return $data; + } + + return ''; + } + /** + * {@inheritdoc} + */ + public function write($session_id, $session_data) + { + $this->client->setex($session_id, $this->ttl, $session_data); + + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($session_id) + { + $this->client->del($session_id); + + return true; + } + + /** + * Returns the underlying client instance. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->client; + } + + /** + * Returns the session max lifetime value. + * + * @return int + */ + public function getMaxLifeTime() + { + return $this->ttl; + } +} diff --git a/vendor/Predis/Transaction/AbortedMultiExecException.php b/vendor/Predis/Transaction/AbortedMultiExecException.php new file mode 100644 index 0000000..bccaa87 --- /dev/null +++ b/vendor/Predis/Transaction/AbortedMultiExecException.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Transaction; + +use Predis\PredisException; + +/** + * Exception class that identifies MULTI / EXEC transactions aborted by Redis. + * + * @author Daniele Alessandri + */ +class AbortedMultiExecException extends PredisException +{ + private $transaction; + + /** + * @param MultiExecContext $transaction Transaction that generated the exception. + * @param string $message Error message. + * @param int $code Error code. + */ + public function __construct(MultiExecContext $transaction, $message, $code = null) + { + parent::__construct($message, $code); + $this->transaction = $transaction; + } + + /** + * Returns the transaction that generated the exception. + * + * @return MultiExecContext + */ + public function getTransaction() + { + return $this->transaction; + } +} diff --git a/vendor/Predis/Transaction/MultiExecContext.php b/vendor/Predis/Transaction/MultiExecContext.php new file mode 100644 index 0000000..882c2b4 --- /dev/null +++ b/vendor/Predis/Transaction/MultiExecContext.php @@ -0,0 +1,449 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Transaction; + +use SplQueue; +use Predis\BasicClientInterface; +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\CommunicationException; +use Predis\ExecutableContextInterface; +use Predis\NotSupportedException; +use Predis\ResponseErrorInterface; +use Predis\ResponseQueued; +use Predis\ServerException; +use Predis\Command\CommandInterface; +use Predis\Connection\AggregatedConnectionInterface; +use Predis\Protocol\ProtocolException; + +/** + * Client-side abstraction of a Redis transaction based on MULTI / EXEC. + * + * @author Daniele Alessandri + */ +class MultiExecContext implements BasicClientInterface, ExecutableContextInterface +{ + const STATE_RESET = 0; // 0b00000 + const STATE_INITIALIZED = 1; // 0b00001 + const STATE_INSIDEBLOCK = 2; // 0b00010 + const STATE_DISCARDED = 4; // 0b00100 + const STATE_CAS = 8; // 0b01000 + const STATE_WATCH = 16; // 0b10000 + + private $state; + private $canWatch; + + protected $client; + protected $options; + protected $commands; + + /** + * @param ClientInterface $client Client instance used by the context. + * @param array $options Options for the context initialization. + */ + public function __construct(ClientInterface $client, Array $options = null) + { + $this->checkCapabilities($client); + $this->options = $options ?: array(); + $this->client = $client; + $this->reset(); + } + + /** + * Sets the internal state flags. + * + * @param int $flags Set of flags + */ + protected function setState($flags) + { + $this->state = $flags; + } + + /** + * Gets the internal state flags. + * + * @return int + */ + protected function getState() + { + return $this->state; + } + + /** + * Sets one or more flags. + * + * @param int $flags Set of flags + */ + protected function flagState($flags) + { + $this->state |= $flags; + } + + /** + * Resets one or more flags. + * + * @param int $flags Set of flags + */ + protected function unflagState($flags) + { + $this->state &= ~$flags; + } + + /** + * Checks is a flag is set. + * + * @param int $flags Flag + * @return Boolean + */ + protected function checkState($flags) + { + return ($this->state & $flags) === $flags; + } + + /** + * Checks if the passed client instance satisfies the required conditions + * needed to initialize a transaction context. + * + * @param ClientInterface $client Client instance used by the context. + */ + private function checkCapabilities(ClientInterface $client) + { + if ($client->getConnection() instanceof AggregatedConnectionInterface) { + throw new NotSupportedException('Cannot initialize a MULTI/EXEC context when using aggregated connections'); + } + + $profile = $client->getProfile(); + + if ($profile->supportsCommands(array('multi', 'exec', 'discard')) === false) { + throw new NotSupportedException('The current profile does not support MULTI, EXEC and DISCARD'); + } + + $this->canWatch = $profile->supportsCommands(array('watch', 'unwatch')); + } + + /** + * Checks if WATCH and UNWATCH are supported by the server profile. + */ + private function isWatchSupported() + { + if ($this->canWatch === false) { + throw new NotSupportedException('The current profile does not support WATCH and UNWATCH'); + } + } + + /** + * Resets the state of a transaction. + */ + protected function reset() + { + $this->setState(self::STATE_RESET); + $this->commands = new SplQueue(); + } + + /** + * Initializes a new transaction. + */ + protected function initialize() + { + if ($this->checkState(self::STATE_INITIALIZED)) { + return; + } + + $options = $this->options; + + if (isset($options['cas']) && $options['cas']) { + $this->flagState(self::STATE_CAS); + } + if (isset($options['watch'])) { + $this->watch($options['watch']); + } + + $cas = $this->checkState(self::STATE_CAS); + $discarded = $this->checkState(self::STATE_DISCARDED); + + if (!$cas || ($cas && $discarded)) { + $this->client->multi(); + + if ($discarded) { + $this->unflagState(self::STATE_CAS); + } + } + + $this->unflagState(self::STATE_DISCARDED); + $this->flagState(self::STATE_INITIALIZED); + } + + /** + * Dynamically invokes a Redis command with the specified arguments. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * @return mixed + */ + public function __call($method, $arguments) + { + $command = $this->client->createCommand($method, $arguments); + $response = $this->executeCommand($command); + + return $response; + } + + /** + * Executes the specified Redis command. + * + * @param CommandInterface $command A Redis command. + * @return mixed + */ + public function executeCommand(CommandInterface $command) + { + $this->initialize(); + $response = $this->client->executeCommand($command); + + if ($this->checkState(self::STATE_CAS)) { + return $response; + } + + if (!$response instanceof ResponseQueued) { + $this->onProtocolError('The server did not respond with a QUEUED status reply'); + } + + $this->commands->enqueue($command); + + return $this; + } + + /** + * Executes WATCH on one or more keys. + * + * @param string|array $keys One or more keys. + * @return mixed + */ + public function watch($keys) + { + $this->isWatchSupported(); + + if ($this->checkState(self::STATE_INITIALIZED) && !$this->checkState(self::STATE_CAS)) { + throw new ClientException('WATCH after MULTI is not allowed'); + } + + $reply = $this->client->watch($keys); + $this->flagState(self::STATE_WATCH); + + return $reply; + } + + /** + * Finalizes the transaction on the server by executing MULTI on the server. + * + * @return MultiExecContext + */ + public function multi() + { + if ($this->checkState(self::STATE_INITIALIZED | self::STATE_CAS)) { + $this->unflagState(self::STATE_CAS); + $this->client->multi(); + } else { + $this->initialize(); + } + + return $this; + } + + /** + * Executes UNWATCH. + * + * @return MultiExecContext + */ + public function unwatch() + { + $this->isWatchSupported(); + $this->unflagState(self::STATE_WATCH); + $this->__call('unwatch', array()); + + return $this; + } + + /** + * Resets a transaction by UNWATCHing the keys that are being WATCHed and + * DISCARDing the pending commands that have been already sent to the server. + * + * @return MultiExecContext + */ + public function discard() + { + if ($this->checkState(self::STATE_INITIALIZED)) { + $command = $this->checkState(self::STATE_CAS) ? 'unwatch' : 'discard'; + $this->client->$command(); + $this->reset(); + $this->flagState(self::STATE_DISCARDED); + } + + return $this; + } + + /** + * Executes the whole transaction. + * + * @return mixed + */ + public function exec() + { + return $this->execute(); + } + + /** + * Checks the state of the transaction before execution. + * + * @param mixed $callable Callback for execution. + */ + private function checkBeforeExecution($callable) + { + if ($this->checkState(self::STATE_INSIDEBLOCK)) { + throw new ClientException("Cannot invoke 'execute' or 'exec' inside an active client transaction block"); + } + + if ($callable) { + if (!is_callable($callable)) { + throw new \InvalidArgumentException('Argument passed must be a callable object'); + } + + if (!$this->commands->isEmpty()) { + $this->discard(); + throw new ClientException('Cannot execute a transaction block after using fluent interface'); + } + } + + if (isset($this->options['retry']) && !isset($callable)) { + $this->discard(); + throw new \InvalidArgumentException('Automatic retries can be used only when a transaction block is provided'); + } + } + + /** + * Handles the actual execution of the whole transaction. + * + * @param mixed $callable Optional callback for execution. + * @return array + */ + public function execute($callable = null) + { + $this->checkBeforeExecution($callable); + + $reply = null; + $values = array(); + $attempts = isset($this->options['retry']) ? (int) $this->options['retry'] : 0; + + do { + if ($callable !== null) { + $this->executeTransactionBlock($callable); + } + + if ($this->commands->isEmpty()) { + if ($this->checkState(self::STATE_WATCH)) { + $this->discard(); + } + + return; + } + + $reply = $this->client->exec(); + + if ($reply === null) { + if ($attempts === 0) { + $message = 'The current transaction has been aborted by the server'; + throw new AbortedMultiExecException($this, $message); + } + + $this->reset(); + + if (isset($this->options['on_retry']) && is_callable($this->options['on_retry'])) { + call_user_func($this->options['on_retry'], $this, $attempts); + } + + continue; + } + + break; + } while ($attempts-- > 0); + + $exec = $reply instanceof \Iterator ? iterator_to_array($reply) : $reply; + $commands = $this->commands; + + $size = count($exec); + if ($size !== count($commands)) { + $this->onProtocolError("EXEC returned an unexpected number of replies"); + } + + $clientOpts = $this->client->getOptions(); + $useExceptions = isset($clientOpts->exceptions) ? $clientOpts->exceptions : true; + + for ($i = 0; $i < $size; $i++) { + $commandReply = $exec[$i]; + + if ($commandReply instanceof ResponseErrorInterface && $useExceptions) { + $message = $commandReply->getMessage(); + throw new ServerException($message); + } + + if ($commandReply instanceof \Iterator) { + $commandReply = iterator_to_array($commandReply); + } + + $values[$i] = $commands->dequeue()->parseResponse($commandReply); + } + + return $values; + } + + /** + * Passes the current transaction context to a callable block for execution. + * + * @param mixed $callable Callback. + */ + protected function executeTransactionBlock($callable) + { + $blockException = null; + $this->flagState(self::STATE_INSIDEBLOCK); + + try { + call_user_func($callable, $this); + } catch (CommunicationException $exception) { + $blockException = $exception; + } catch (ServerException $exception) { + $blockException = $exception; + } catch (\Exception $exception) { + $blockException = $exception; + $this->discard(); + } + + $this->unflagState(self::STATE_INSIDEBLOCK); + + if ($blockException !== null) { + throw $blockException; + } + } + + /** + * Helper method that handles protocol errors encountered inside a transaction. + * + * @param string $message Error message. + */ + private function onProtocolError($message) + { + // Since a MULTI/EXEC block cannot be initialized when using aggregated + // connections, we can safely assume that Predis\Client::getConnection() + // will always return an instance of Predis\Connection\SingleConnectionInterface. + CommunicationException::handle(new ProtocolException( + $this->client->getConnection(), $message + )); + } +}