From 0b5dd3a3ac31860727ea4d658fab1283a77582b0 Mon Sep 17 00:00:00 2001 From: Andre Schuurman Date: Sat, 16 Jan 2016 23:57:37 +0100 Subject: [PATCH] First commit --- Counter.php | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE | 22 ++++++++ README.md | 59 ++++++++++++++++++++++ composer.json | 23 +++++++++ 4 files changed, 239 insertions(+) create mode 100644 Counter.php create mode 100644 LICENSE create mode 100644 README.md create mode 100644 composer.json diff --git a/Counter.php b/Counter.php new file mode 100644 index 0000000..7b9da13 --- /dev/null +++ b/Counter.php @@ -0,0 +1,135 @@ +redis)) { + $this->redis = Yii::$app->get($this->redis); + } elseif (is_array($this->redis)) { + if (!isset($this->redis['class'])) { + $this->redis['class'] = Connection::className(); + } + $this->redis = Yii::createObject($this->redis); + } + if (!$this->redis instanceof Connection) { + throw new InvalidConfigException("Counter::redis must be either a Redis connection instance or the application component ID of a Redis connection."); + } + } + + /** + * Checks whether a specified key exists in the counter store. + * This can be faster than getting the value from the counter store if the data is big. + * @param string $key a key identifying the counter. + * @return boolean true if a value exists in counter store, false if the value is not in the counter store or expired. + */ + public function exists($key) + { + return (bool) $this->redis->executeCommand('EXISTS', [$key]); + } + + /** + * Gets a counter identified by key + * + * @param string $key a key identifying the counter. + * @return boolean whether the value is successfully stored + */ + public function get($key) + { + return $this->redis->executeCommand('GET', [$key]); + } + + /** + * Sets a counter identified by key with amount + * + * @param string $key a key identifying the counter. + * @param integer $amount the value to incremented with + * @param integer $expire the number of seconds in which the stored value will expire. 0 means never expire. + * @return boolean whether the value is successfully stored + * @throws Exception + */ + public function set($key, $amount = 0, $expire = 0) + { + if (!is_int($amount)) { + throw new Exception('Counter amount can only be an integer value'); + } + if ($expire == 0) { + return (bool) $this->redis->executeCommand('SET', [$key, $amount, 'NX']); + } else { + $expire = (int) ($expire * 1000); + + return (bool) $this->redis->executeCommand('SET', [$key, $amount, 'PX', $expire, 'NX']); + } + } + + /** + * Deletes a value with the specified key from the counter store + * @param string $key a key identifying counter. + * @return boolean if no error happens during deletion + */ + public function delete($key) + { + return (bool) $this->redis->executeCommand('DEL', [$key]); + } + + /** + * Increment a counter identified by key with optional amount + * + * @param string $key a key identifying the counter. + * @param integer $amount the value to increment with + * @return boolean whether the counter is successfully stored + * @throws Exception + */ + public function incr($key, $amount = 1) + { + if (!is_int($amount)) { + throw new Exception('Counter amount can only be an integer value'); + } + return (bool) $this->redis->executeCommand('INCRBY', [$key, $amount]); + } + + /** + * Decrement a counter identified by key with optional amount + * + * @param string $key a key identifying the counter. + * @param integer $amount the value to decrement with + * @return boolean whether the counter is successfully stored + * @throws Exception + */ + public function decr($key, $amount = 1) + { + if (!is_int($amount)) { + throw new Exception('Counter amount can only be an integer value'); + } + return (bool) $this->redis->executeCommand('DECRBY', [$key, $amount]); + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1334ab0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 AFS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..b174b09 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +Yii2 Redis Counter +================== +Redis Counter implements fast atomic counters using Redis storage. + + +Installation +------------ + +The preferred way to install this extension is through [composer](http://getcomposer.org/download/). + +Either run + +``` +php composer.phar require --prefer-dist drsdre/yii2-redis-counter "*" +``` + +or add + +``` +"drsdre/yii2-redis-counter": "*" +``` + +to the require section of your `composer.json` file. + + +Usage +----- + +You need to setup the client as application component: + +```php +'components' => [ + 'redisCounter' => [ + 'class' => 'drsdre\redis\Counter', + ], + ... +] +``` + +Optional the parameter 'redis' can be added to specify a specific Redis connection. + +Usage +----- + +Once the extension is installed, use it in your code like : + +```php + // Create a hourly counter + $counter_key = 'hourly_statistics'; + + // Check if counter is setup + if (Yii::$app->redisCounter->exists($counter_key)) { + // Atomic increment counter with 1 + Yii::$app->redisCounter->incr($counter_key); + } else { + // Create counter set value to 1 and let it exist for 1 hour + Yii::$app->redisCounter->set($counter_key, 1, 60*60); + } +``` \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..6cfcaca --- /dev/null +++ b/composer.json @@ -0,0 +1,23 @@ +{ + "name": "drsdre/yii2-redis-counter", + "description": "Redis Counter implements fast atomic counters using Redis storage.", + "type": "yii2-extension", + "keywords": ["yii2","extension","redis","counter","atomic"], + "license": "MIT", + "authors": [ + { + "name": "A.F.Schuurman", + "email": "andre.schuurman+yii2-redis-counter@gmail.com" + } + ], + "require": { + "yiisoft/yii2": "*", + "yiisoft/yii2-bootstrap": "*", + "yiisoft/yii2-redis": "~2.0.0" + }, + "autoload": { + "psr-4": { + "drsdre\\redis": "" + } + } +}