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
+ ));
+ }
+}