first commit
This commit is contained in:
194
vendor/yiisoft/yii2-redis/CHANGELOG.md
vendored
Normal file
194
vendor/yiisoft/yii2-redis/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
Yii Framework 2 redis extension Change Log
|
||||
==========================================
|
||||
|
||||
2.0.1 December 25, 2025
|
||||
-----------------------
|
||||
|
||||
- Fix #278: Prevent `null` parameter on `SocketException` to avoid PHP 8.4 implicity nullable types deprecation (HenryVolkmer)
|
||||
- New #276: Added support for predis (antonshevelev)
|
||||
- New #276: Changed default value of `yii\redis\Cache::$forceClusterMode` to `false` (antonshevelev)
|
||||
- New #276: Implemented `yii\redis\ConnectionInterface` in `yii\redis\Connection` (antonshevelev)
|
||||
|
||||
|
||||
2.0.20 June 05, 2025
|
||||
--------------------
|
||||
|
||||
- Bug CVE-2025-48493: Prevent logging `AUTH` parameters when `YII_DEBUG` is off (samdark)
|
||||
- Bug #270: Prevent null parameter on `mb_strlen` to avoid PHP 8.4 implicity nullable types deprecation (tehmaestro)
|
||||
|
||||
|
||||
2.0.19 February 13, 2025
|
||||
------------------------
|
||||
|
||||
- Enh #264: Improve performance of `mget()` for big list of keys (alx-xc, rob006)
|
||||
|
||||
|
||||
2.0.18 September 04, 2022
|
||||
-------------------------
|
||||
|
||||
- Enh #249 Added support to set the redis scheme to tls. Add to configuration: `'scheme' => 'tls'` (tychovbh)
|
||||
- Bug #247: `Cache::getValue()` now returns `false` in case of missing key (rhertogh)
|
||||
|
||||
|
||||
2.0.17 January 11, 2022
|
||||
-----------------------
|
||||
|
||||
- Enh #176: Fix reconnect logic bug, add `protected function sendRawCommand()` (ilyaplot)
|
||||
|
||||
|
||||
2.0.16 October 04, 2021
|
||||
-----------------------
|
||||
|
||||
- Enh #223: Add `Connection::$username` for using username for authentication (samdark, rvkulikov)
|
||||
|
||||
|
||||
2.0.15 May 05, 2021
|
||||
-------------------
|
||||
|
||||
- Enh #227: Added support for adjusting PHP context options and parameters. This allows e.g. supporting self-signed certificates (akselikap)
|
||||
|
||||
|
||||
2.0.14 November 10, 2020
|
||||
------------------------
|
||||
|
||||
- Bug #215: Fix `Connection::isActive()` returns `false` when the connection is active (cornernote)
|
||||
- Enh #212: Added support for the 'session.use_strict_mode' ini directive in `yii\web\Session` (rhertogh)
|
||||
|
||||
|
||||
2.0.13 May 02, 2020
|
||||
-------------------
|
||||
|
||||
- Enh #210: Add Redis 5.0 stream commands. Read more at [streams intro](https://redis.io/topics/streams-intro) (sartor)
|
||||
|
||||
|
||||
2.0.12 March 13, 2020
|
||||
---------------------
|
||||
|
||||
- Bug #182: Better handle `cache/flush-all` command when cache component is using shared database (rob006)
|
||||
- Bug #190: Accept null values (razonyang)
|
||||
- Bug #191: getIsActive() returns true when socket is not connected (mdx86)
|
||||
- Enh #174: Add ability to set up SSL connection (kulavvy)
|
||||
- Enh #195: Use `Instance::ensure()` to initialize `Session::$redis` (rob006)
|
||||
- Enh #199: Increase frequency of lock tries when `$timeout` is used in `Mutex::acquire()` (rob006)
|
||||
|
||||
|
||||
2.0.11 November 05, 2019
|
||||
------------------------
|
||||
|
||||
- Enh #66, #134, #135, #136, #142, #143, #147: Support Redis in cluster mode (hofrob)
|
||||
|
||||
|
||||
2.0.10 October 22, 2019
|
||||
-----------------------
|
||||
|
||||
- Enh #188: Added option to wait between connection retry (marty-macfly, rob006)
|
||||
|
||||
|
||||
2.0.9 September 23, 2018
|
||||
------------------------
|
||||
|
||||
- Bug #166: zrangebyscore without scores does not work (razonyang)
|
||||
- Enh #13: Added `>`, `<`, `>=` and `<=` conditions support in ActiveQuery (nailfor, zacksleo)
|
||||
|
||||
|
||||
2.0.8 March 20, 2018
|
||||
--------------------
|
||||
|
||||
- Bug #141: Calling ActiveQuery::indexBy() had no effect since Yii 2.0.14 (cebe)
|
||||
- Bug: (CVE-2018-8073): Fix possible remote code execution when improperly filtered user input is passed to `ActiveRecord::findOne()` and `::findAll()` (cebe)
|
||||
- Enh #66: Cache component can be configured to read / get from replicas (ryusoft)
|
||||
|
||||
|
||||
2.0.7 December 11, 2017
|
||||
-----------------------
|
||||
|
||||
- Bug #114: Fixed ActiveQuery `not between` and `not` conditions which where not working correctly (cebe, ak1987)
|
||||
- Bug #123: Fixed ActiveQuery to work with negative limit values, which are used in ActiveDataProvider for the count query (cebe)
|
||||
- Enh #9: Added orderBy support to redis ActiveQuery and LuaScriptBuilder (valinurovam)
|
||||
- Enh #91: Added option to retry connection after failing to communicate with redis server on stale socket (cebe)
|
||||
- Enh #106: Improved handling of connection errors and introduced `yii\redis\SocketException` for these (cebe)
|
||||
- Chg #127: Added PHP 7.2 compatibility (brandonkelly)
|
||||
|
||||
|
||||
2.0.6 April 05, 2017
|
||||
--------------------
|
||||
|
||||
- Bug #44: Remove quotes from numeric parts of composite key to avoid problem with different hashes for the same record (uniserpl)
|
||||
- Bug #67: Fixed regression from 2.0.5, reconnecting a closed connection fails (cebe)
|
||||
- Bug #82: Fixed session object destruction failure when key expires (juffin-halli, samdark)
|
||||
- Bug #93: Fixed `yii\redis\ActiveRecord::deleteAll()` with condition (samdark)
|
||||
- Bug #104: Fixed execution of two-word commands (cebe,branimir93)
|
||||
- Enh #53: Added `Mutex` that implements a Redis based mutex (turboezh, sergeymakinen)
|
||||
- Enh #81: Allow setting `Connection::$database` to `null` to avoid sending a `SELECT` command after connection (cebe)
|
||||
- Enh #89: Added support for `\yii\db\QueryInterface::emulateExecution()` (samdark)
|
||||
- Enh #103: Added missing commands and `@method` documentation for redis commands (cebe)
|
||||
- Enh: Optimized find by PK for relational queries and IN condition (cebe, andruha)
|
||||
|
||||
|
||||
2.0.5 March 17, 2016
|
||||
--------------------
|
||||
|
||||
- Bug #22: Fixed string escaping issue in LuaScriptBuilder (vistart)
|
||||
- Bug #37: Fixed detection of open socket (mirocow)
|
||||
- Bug #46: Fixed bug to execute session_regenerate_id in PHP 7.0 (githubjeka)
|
||||
- Enh #31: Added `Connection::$socketClientFlags` property for connection flags to be passed to `stream_socket_client()` (hugh-lee)
|
||||
- Chg #14: Added missing `BLPOP` command to `$redisCommands` (samdark)
|
||||
- Chg #61: Added missing `GEO*` commands to `$redisCommands` (leadermt)
|
||||
|
||||
|
||||
2.0.4 May 10, 2015
|
||||
------------------
|
||||
|
||||
- Enh #8: Auto increment value was not updated when a primary key was explicitly set (cebe, andruha)
|
||||
|
||||
|
||||
2.0.3 March 01, 2015
|
||||
--------------------
|
||||
|
||||
- no changes in this release.
|
||||
|
||||
|
||||
2.0.2 January 11, 2015
|
||||
----------------------
|
||||
|
||||
- Bug #6547: Fixed redis connection to deal with large data in combination with `mget()` (pyurin)
|
||||
|
||||
|
||||
2.0.1 December 07, 2014
|
||||
-----------------------
|
||||
|
||||
- Bug #4745: value of simple string returns was ignored by redis client and `true` is returned instead, now only `OK` will result in a `true` while all other values are returned as is (cebe)
|
||||
- Enh #3714: Added support for connecting to redis server using a unix socket (savvot, robregonm)
|
||||
|
||||
|
||||
2.0.0 October 12, 2014
|
||||
----------------------
|
||||
|
||||
- no changes in this release.
|
||||
|
||||
|
||||
2.0.0-rc September 27, 2014
|
||||
---------------------------
|
||||
|
||||
- Bug #1311: Fixed storage and finding of `null` and boolean values (samdark, cebe)
|
||||
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
|
||||
- Enh #4048: Added `init` event to `ActiveQuery` classes (qiangxue)
|
||||
- Enh #4086: changedAttributes of afterSave Event now contain old values (dizews)
|
||||
|
||||
|
||||
2.0.0-beta April 13, 2014
|
||||
-------------------------
|
||||
|
||||
- Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
|
||||
- Enh #1773: keyPrefix property of Session and Cache is not restricted to alnum characters anymore (cebe)
|
||||
- Enh #2002: Added filterWhere() method to yii\redis\ActiveQuery to allow easy addition of search filter conditions by ignoring empty search fields (samdark, cebe)
|
||||
- Enh #2892: ActiveRecord dirty attributes are now reset after call to `afterSave()` so information about changed attributes is available in `afterSave`-event (cebe)
|
||||
- Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)
|
||||
- Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`.
|
||||
All relational queries are now directly served by `ActiveQuery` allowing to use
|
||||
custom scopes in relations (cebe)
|
||||
|
||||
2.0.0-alpha, December 1, 2013
|
||||
-----------------------------
|
||||
|
||||
- Initial release.
|
||||
29
vendor/yiisoft/yii2-redis/LICENSE.md
vendored
Normal file
29
vendor/yiisoft/yii2-redis/LICENSE.md
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Yii Software LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
49
vendor/yiisoft/yii2-redis/Makefile
vendored
Normal file
49
vendor/yiisoft/yii2-redis/Makefile
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
# default versions to test against
|
||||
# these can be overridden by setting the environment variables in the shell
|
||||
REDIS_VERSION=3.0.0
|
||||
PHP_VERSION=php-5.6.8
|
||||
YII_VERSION=dev-master
|
||||
|
||||
# ensure all the configuration variables above are in environment of the shell commands below
|
||||
export
|
||||
|
||||
help:
|
||||
@echo "make test - run phpunit tests using a docker environment"
|
||||
@echo "make clean - stop docker and remove container"
|
||||
|
||||
test: docker adjust-config
|
||||
#composer require "yiisoft/yii2:${YII_VERSION}" --prefer-dist
|
||||
#composer install --prefer-dist
|
||||
docker run --rm=true -v $(shell pwd):/opt/test --link $(shell cat tests/dockerids/redis):redis yiitest/php:${PHP_VERSION} phpunit --verbose --color
|
||||
|
||||
adjust-config:
|
||||
echo "<?php \$$config['databases']['redis']['port'] = 6379; \$$config['databases']['redis']['hostname'] = 'redis';" > tests/data/config.local.php
|
||||
|
||||
docker: build-docker
|
||||
docker run -d -P yiitest/redis:${REDIS_VERSION} > tests/dockerids/redis
|
||||
|
||||
build-docker:
|
||||
test -d tests/docker || git clone https://github.com/cebe/jenkins-test-docker tests/docker
|
||||
cd tests/docker && git checkout -- . && git pull
|
||||
cd tests/docker/php && sh build.sh
|
||||
cd tests/docker/redis && sh build.sh
|
||||
mkdir -p tests/dockerids
|
||||
|
||||
clean:
|
||||
docker stop $(shell cat tests/dockerids/redis)
|
||||
docker rm $(shell cat tests/dockerids/redis)
|
||||
rm tests/dockerids/redis
|
||||
|
||||
test-sentinel:
|
||||
make build
|
||||
PHP_VERSION=$(filter-out $@,$(MAKECMDGOALS)) docker compose -f tests/docker/docker-compose.yml build --pull yii2-redis-php
|
||||
PHP_VERSION=$(filter-out $@,$(MAKECMDGOALS)) docker compose -f tests/docker/docker-compose.yml up -d
|
||||
PHP_VERSION=$(filter-out $@,$(MAKECMDGOALS)) docker compose -f tests/docker/docker-compose.yml exec yii2-redis-php sh -c "composer update && vendor/bin/phpunit --coverage-clover=coverage.clover"
|
||||
|
||||
build: ## Build an image from a docker-compose file. Params: {{ v=8.1 }}. Default latest PHP 8.1
|
||||
PHP_VERSION=$(filter-out $@,$(v)) docker compose -f tests/docker/docker-compose.yml up -d --build
|
||||
|
||||
down: ## Stop and remove containers, networks
|
||||
docker compose -f tests/docker/docker-compose.yml down
|
||||
|
||||
107
vendor/yiisoft/yii2-redis/README.md
vendored
Normal file
107
vendor/yiisoft/yii2-redis/README.md
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
<p align="center">
|
||||
<a href="https://redis.io/" target="_blank" rel="external">
|
||||
<img src="https://download.redis.io/redis.png" height="100px">
|
||||
</a>
|
||||
<h1 align="center">Redis Cache, Session and ActiveRecord for Yii 2</h1>
|
||||
<br>
|
||||
</p>
|
||||
|
||||
This extension provides the [redis](https://redis.io/) key-value store support for the [Yii framework 2.0](https://www.yiiframework.com).
|
||||
It includes a `Cache` and `Session` storage handler and implements the `ActiveRecord` pattern that allows
|
||||
you to store active records in redis.
|
||||
|
||||
For license information check the [LICENSE](LICENSE.md)-file.
|
||||
|
||||
Documentation is at [docs/guide/README.md](docs/guide/README.md).
|
||||
|
||||
[](https://packagist.org/packages/yiisoft/yii2-redis)
|
||||
[](https://packagist.org/packages/yiisoft/yii2-redis)
|
||||
[](https://github.com/yiisoft/yii2-redis/actions?query=workflow%3Abuild)
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
At least redis version is required for all components to work properly.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The preferred way to install this extension is through [composer](https://getcomposer.org/download/).
|
||||
|
||||
Either run
|
||||
|
||||
```
|
||||
php composer.phar require --prefer-dist yiisoft/yii2-redis:"~2.0.0"
|
||||
```
|
||||
|
||||
or add
|
||||
|
||||
```json
|
||||
"yiisoft/yii2-redis": "~2.0.0"
|
||||
```
|
||||
|
||||
to the require section of your composer.json.
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
To use this extension, you have to configure the Connection class in your application configuration:
|
||||
|
||||
```php
|
||||
return [
|
||||
//....
|
||||
'components' => [
|
||||
'redis' => [
|
||||
'class' => 'yii\redis\Connection',
|
||||
'hostname' => 'localhost',
|
||||
'port' => 6379,
|
||||
'database' => 0,
|
||||
],
|
||||
]
|
||||
];
|
||||
```
|
||||
|
||||
**SSL configuration** example:
|
||||
```php
|
||||
return [
|
||||
//....
|
||||
'components' => [
|
||||
'redis' => [
|
||||
'class' => 'yii\redis\Connection',
|
||||
'hostname' => 'localhost',
|
||||
'port' => 6380,
|
||||
'database' => 0,
|
||||
'useSSL' => true,
|
||||
// Use contextOptions for more control over the connection (https://www.php.net/manual/en/context.php), not usually needed
|
||||
'contextOptions' => [
|
||||
'ssl' => [
|
||||
'local_cert' => '/path/to/local/certificate',
|
||||
'local_pk' => '/path/to/local/private_key',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
**Configuring The Connection Scheme**
|
||||
|
||||
By default, Redis will use the tcp scheme when connecting to your Redis server; however, you may use TLS / SSL encryption by specifying a scheme configuration option in your application configuration:
|
||||
```php
|
||||
return [
|
||||
//....
|
||||
'components' => [
|
||||
'redis' => [
|
||||
//....
|
||||
'scheme' => 'tls'
|
||||
]
|
||||
]
|
||||
];
|
||||
```
|
||||
|
||||
Additional topics
|
||||
-----------------
|
||||
|
||||
* [predis support](/docs/guide/predis.md)
|
||||
97
vendor/yiisoft/yii2-redis/UPGRADE.md
vendored
Normal file
97
vendor/yiisoft/yii2-redis/UPGRADE.md
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
# Upgrading Instructions for Yii 2.0 Redis Extension
|
||||
|
||||
This file contains the upgrade notes for Yii 2.0 Redis Extension. These notes highlight changes that
|
||||
could break your application when you upgrade extension from one version to another.
|
||||
|
||||
Upgrading in general is as simple as updating your dependency in your composer.json and
|
||||
running `composer update`. In a big application however there may be more things to consider,
|
||||
which are explained in the following.
|
||||
|
||||
> Note: The following upgrading instructions are cumulative. That is,
|
||||
if you want to upgrade from version A to version C and there is
|
||||
version B between A and C, you need to follow the instructions
|
||||
for both A and B.
|
||||
|
||||
Upgrade from 2.0.17
|
||||
------------------
|
||||
|
||||
* The `Cache::getValue()` method now adheres to the specifications and returns `false` in case of a missing
|
||||
(or expired) key. This might have impact in the following scenarios:
|
||||
* You specified a custom `Cache::$serializer`, depending on the serializer used `Cache::get()` might now
|
||||
return `false` instead of `null` in case of a missing (or expired) key.
|
||||
* You extended the `Cache` class, are using the protected method `Cache::getValue()` directly and expect
|
||||
a return value of `null` in case of a missing (or expired) key.
|
||||
|
||||
Upgrade from 2.0.13
|
||||
------------------
|
||||
|
||||
* `yii\redis\Session` now respects the 'session.use_strict_mode' ini directive.
|
||||
In case you use a custom `Session` class and have overwritten the `Session::openSession()` and/or
|
||||
`Session::writeSession()` functions changes might be required:
|
||||
* When in strict mode the `openSession()` function should check if the requested session id exists
|
||||
(and mark it for forced regeneration if not).
|
||||
For example, the `yii\redis\Session` does this at the beginning of the function as follows:
|
||||
```php
|
||||
if ($this->getUseStrictMode()) {
|
||||
$id = $this->getId();
|
||||
if (!$this->redis->exists($this->calculateKey($id))) {
|
||||
//This session id does not exist, mark it for forced regeneration
|
||||
$this->_forceRegenerateId = $id;
|
||||
}
|
||||
}
|
||||
// ... normal function continues ...
|
||||
```
|
||||
* When in strict mode the `writeSession()` function should ignore writing the session under the old id.
|
||||
For example, the `yii\redis\Session` does this at the beginning of the function as follows:
|
||||
```php
|
||||
if ($this->getUseStrictMode() && $id === $this->_forceRegenerateId) {
|
||||
//Ignore write when forceRegenerate is active for this id
|
||||
return true;
|
||||
}
|
||||
// ... normal function continues ...
|
||||
```
|
||||
> Note: In case your custom functions call their `parent` functions, there are probably no changes needed to your
|
||||
code if those parents implement the `useStrictMode` checks.
|
||||
|
||||
> Warning: in case `openSession()` and/or `writeSession()` functions do not implement the `useStrictMode` code
|
||||
the session could be stored under the forced id without warning even if `useStrictMode` is enabled.
|
||||
|
||||
|
||||
Upgrade from 2.0.10
|
||||
------------------
|
||||
|
||||
* yii2-redis now provides a better support for Redis in cluster mode.
|
||||
|
||||
If you're using redis in cluster mode and want to use `MGET` and `MSET` effectively, you will need to supply a
|
||||
[hash tag](https://redis.io/topics/cluster-spec#keys-hash-tags) to allocate cache keys to the same hash slot.
|
||||
```
|
||||
\Yii::$app->cache->multiSet([
|
||||
'posts{user1}' => 123,
|
||||
'settings{user1}' => [
|
||||
'showNickname' => false,
|
||||
'sortBy' => 'created_at',
|
||||
],
|
||||
'unreadMessages{user1}' => 5,
|
||||
]);
|
||||
```
|
||||
|
||||
You might also want to force cluster mode for every request by setting `'forceClusterMode' => true` in your Redis component config. Otherwise an implicit check that executes Redis command `'CLUSTER INFO'` will be made on each request.
|
||||
|
||||
If you do not intend on using Redis in cluster mode, it is advisable to set `'forceClusterMode' => false` to avoid additional overhead and possible troubles when extending `yii\redis\Connection::executeCommand`, because executing `'CLUSTER INFO'` will return an error on a single-node Redis.
|
||||
|
||||
|
||||
Upgrade from 2.0.11
|
||||
-------------------
|
||||
|
||||
* `zrangebyscore` was changed to:
|
||||
|
||||
```php
|
||||
zrangebyscore($key, $min, $max, ...$options)
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
- `zrangebyscore($key, $min, $max)`
|
||||
- `zrangebyscore($key, $min, $max, 'WITHSCORES')`
|
||||
- `zrangebyscore($key, $min, $max, 'LIMIT', $offset, $count)`
|
||||
- `zrangebyscore($key, $min, $max, 'WITHSCORES', 'LIMIT', $offset, $count)`
|
||||
59
vendor/yiisoft/yii2-redis/composer.json
vendored
Normal file
59
vendor/yiisoft/yii2-redis/composer.json
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "yiisoft/yii2-redis",
|
||||
"description": "Redis Cache, Session and ActiveRecord for the Yii framework",
|
||||
"keywords": [
|
||||
"yii2",
|
||||
"redis",
|
||||
"active-record",
|
||||
"cache",
|
||||
"session"
|
||||
],
|
||||
"keywords": ["yii2", "redis", "active-record", "cache", "session"],
|
||||
"type": "yii2-extension",
|
||||
"license": "BSD-3-Clause",
|
||||
"support": {
|
||||
"issues": "https://github.com/yiisoft/yii2-redis/issues",
|
||||
"forum": "https://www.yiiframework.com/forum/",
|
||||
"wiki": "https://www.yiiframework.com/wiki/",
|
||||
"irc": "ircs://irc.libera.chat:6697/yii",
|
||||
"source": "https://github.com/yiisoft/yii2-redis"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Carsten Brandt",
|
||||
"email": "mail@cebe.cc"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"yiisoft/yii2": "~2.0.39",
|
||||
"ext-openssl": "*",
|
||||
"predis/predis": "^v2.3.0|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "9.*",
|
||||
"yiisoft/yii2-dev": "~2.0.39"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "yii\\redis\\": "src" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "yiiunit\\extensions\\redis\\": "tests/"}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://asset-packagist.org"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"yiisoft/yii2-composer": true
|
||||
}
|
||||
}
|
||||
}
|
||||
579
vendor/yiisoft/yii2-redis/src/ActiveQuery.php
vendored
Normal file
579
vendor/yiisoft/yii2-redis/src/ActiveQuery.php
vendored
Normal file
@ -0,0 +1,579 @@
|
||||
<?php
|
||||
/**
|
||||
* @link https://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license https://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\redis;
|
||||
|
||||
use yii\base\Component;
|
||||
use yii\base\InvalidParamException;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\ActiveQueryInterface;
|
||||
use yii\db\ActiveQueryTrait;
|
||||
use yii\db\ActiveRelationTrait;
|
||||
use yii\db\QueryTrait;
|
||||
|
||||
/**
|
||||
* ActiveQuery represents a query associated with an Active Record class.
|
||||
*
|
||||
* An ActiveQuery can be a normal query or be used in a relational context.
|
||||
*
|
||||
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
|
||||
* Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
|
||||
*
|
||||
* Normal Query
|
||||
* ------------
|
||||
*
|
||||
* ActiveQuery mainly provides the following methods to retrieve the query results:
|
||||
*
|
||||
* - [[one()]]: returns a single record populated with the first row of data.
|
||||
* - [[all()]]: returns all records based on the query results.
|
||||
* - [[count()]]: returns the number of records.
|
||||
* - [[sum()]]: returns the sum over the specified column.
|
||||
* - [[average()]]: returns the average over the specified column.
|
||||
* - [[min()]]: returns the min over the specified column.
|
||||
* - [[max()]]: returns the max over the specified column.
|
||||
* - [[scalar()]]: returns the value of the first column in the first row of the query result.
|
||||
* - [[exists()]]: returns a value indicating whether the query result has data or not.
|
||||
*
|
||||
* You can use query methods, such as [[where()]], [[limit()]] and [[orderBy()]] to customize the query options.
|
||||
*
|
||||
* ActiveQuery also provides the following additional query options:
|
||||
*
|
||||
* - [[with()]]: list of relations that this query should be performed with.
|
||||
* - [[indexBy()]]: the name of the column by which the query result should be indexed.
|
||||
* - [[asArray()]]: whether to return each record as an array.
|
||||
*
|
||||
* These options can be configured using methods of the same name. For example:
|
||||
*
|
||||
* ```php
|
||||
* $customers = Customer::find()->with('orders')->asArray()->all();
|
||||
* ```
|
||||
*
|
||||
* Relational query
|
||||
* ----------------
|
||||
*
|
||||
* In relational context ActiveQuery represents a relation between two Active Record classes.
|
||||
*
|
||||
* Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
|
||||
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
|
||||
* a getter method which calls one of the above methods and returns the created ActiveQuery object.
|
||||
*
|
||||
* A relation is specified by [[link]] which represents the association between columns
|
||||
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
|
||||
*
|
||||
* If a relation involves a junction table, it may be specified by [[via()]].
|
||||
* This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
|
||||
* marks a relation as inverse of another relation.
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ActiveQuery extends Component implements ActiveQueryInterface
|
||||
{
|
||||
use QueryTrait;
|
||||
use ActiveQueryTrait;
|
||||
use ActiveRelationTrait;
|
||||
|
||||
/**
|
||||
* @event Event an event that is triggered when the query is initialized via [[init()]].
|
||||
*/
|
||||
const EVENT_INIT = 'init';
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $modelClass the model class associated with this query
|
||||
* @param array $config configurations to be applied to the newly created query object
|
||||
*/
|
||||
public function __construct($modelClass, $config = [])
|
||||
{
|
||||
$this->modelClass = $modelClass;
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the object.
|
||||
* This method is called at the end of the constructor. The default implementation will trigger
|
||||
* an [[EVENT_INIT]] event. If you override this method, make sure you call the parent implementation at the end
|
||||
* to ensure triggering of the event.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->trigger(self::EVENT_INIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns all results as an array.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return array|ActiveRecord[] the query results. If the query results in nothing, an empty array will be returned.
|
||||
*/
|
||||
public function all($db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// TODO add support for orderBy
|
||||
$data = $this->executeScript($db, 'All');
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
$rows = [];
|
||||
foreach ($data as $dataRow) {
|
||||
$row = [];
|
||||
$c = count($dataRow);
|
||||
for ($i = 0; $i < $c;) {
|
||||
$row[$dataRow[$i++]] = $dataRow[$i++];
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
if (empty($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$models = $this->createModels($rows);
|
||||
if (!empty($this->with)) {
|
||||
$this->findWith($this->with, $models);
|
||||
}
|
||||
if ($this->indexBy !== null) {
|
||||
$indexedModels = [];
|
||||
if (is_string($this->indexBy)) {
|
||||
foreach ($models as $model) {
|
||||
$key = $model[$this->indexBy];
|
||||
$indexedModels[$key] = $model;
|
||||
}
|
||||
} else {
|
||||
foreach ($models as $model) {
|
||||
$key = call_user_func($this->indexBy, $model);
|
||||
$indexedModels[$key] = $model;
|
||||
}
|
||||
}
|
||||
$models = $indexedModels;
|
||||
}
|
||||
if (!$this->asArray) {
|
||||
foreach ($models as $model) {
|
||||
$model->afterFind();
|
||||
}
|
||||
}
|
||||
|
||||
return $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns a single row of result.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
|
||||
* the query result may be either an array or an ActiveRecord object. Null will be returned
|
||||
* if the query results in nothing.
|
||||
*/
|
||||
public function one($db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO add support for orderBy
|
||||
$data = $this->executeScript($db, 'One');
|
||||
if (empty($data)) {
|
||||
return null;
|
||||
}
|
||||
$row = [];
|
||||
$c = count($data);
|
||||
for ($i = 0; $i < $c;) {
|
||||
$row[$data[$i++]] = $data[$i++];
|
||||
}
|
||||
if ($this->asArray) {
|
||||
$model = $row;
|
||||
} else {
|
||||
/* @var $class ActiveRecord */
|
||||
$class = $this->modelClass;
|
||||
$model = $class::instantiate($row);
|
||||
$class = get_class($model);
|
||||
$class::populateRecord($model, $row);
|
||||
}
|
||||
if (!empty($this->with)) {
|
||||
$models = [$model];
|
||||
$this->findWith($this->with, $models);
|
||||
$model = $models[0];
|
||||
}
|
||||
if (!$this->asArray) {
|
||||
$model->afterFind();
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of records.
|
||||
* @param string $q the COUNT expression. This parameter is ignored by this implementation.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return int number of records
|
||||
*/
|
||||
public function count($q = '*', $db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($this->where === null) {
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
if ($db === null) {
|
||||
$db = $modelClass::getDb();
|
||||
}
|
||||
|
||||
return $db->executeCommand('LLEN', [$modelClass::keyPrefix()]);
|
||||
}
|
||||
|
||||
return $this->executeScript($db, 'Count');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the query result contains any row of data.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return bool whether the query result contains any row of data.
|
||||
*/
|
||||
public function exists($db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return false;
|
||||
}
|
||||
return $this->one($db) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and returns the first column of the result.
|
||||
* @param string $column name of the column to select
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return array the first column of the query result. An empty array is returned if the query results in nothing.
|
||||
*/
|
||||
public function column($column, $db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// TODO add support for orderBy
|
||||
return $this->executeScript($db, 'Column', $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of records.
|
||||
* @param string $column the column to sum up
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return int number of records
|
||||
*/
|
||||
public function sum($column, $db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->executeScript($db, 'Sum', $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the average of the specified column values.
|
||||
* @param string $column the column name or expression.
|
||||
* Make sure you properly quote column names in the expression.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return int the average of the specified column values.
|
||||
*/
|
||||
public function average($column, $db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return 0;
|
||||
}
|
||||
return $this->executeScript($db, 'Average', $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum of the specified column values.
|
||||
* @param string $column the column name or expression.
|
||||
* Make sure you properly quote column names in the expression.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return int the minimum of the specified column values.
|
||||
*/
|
||||
public function min($column, $db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return null;
|
||||
}
|
||||
return $this->executeScript($db, 'Min', $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum of the specified column values.
|
||||
* @param string $column the column name or expression.
|
||||
* Make sure you properly quote column names in the expression.
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return int the maximum of the specified column values.
|
||||
*/
|
||||
public function max($column, $db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return null;
|
||||
}
|
||||
return $this->executeScript($db, 'Max', $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query result as a scalar value.
|
||||
* The value returned will be the specified attribute in the first record of the query results.
|
||||
* @param string $attribute name of the attribute to select
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @return string the value of the specified attribute in the first record of the query result.
|
||||
* Null is returned if the query result is empty.
|
||||
*/
|
||||
public function scalar($attribute, $db = null)
|
||||
{
|
||||
if ($this->emulateExecution) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$record = $this->one($db);
|
||||
if ($record !== null) {
|
||||
return $record->hasAttribute($attribute) ? $record->$attribute : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a script created by [[LuaScriptBuilder]]
|
||||
* @param Connection|null $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @param string $type the type of the script to generate
|
||||
* @param string $columnName
|
||||
* @throws NotSupportedException
|
||||
* @return array|bool|null|string
|
||||
*/
|
||||
protected function executeScript($db, $type, $columnName = null)
|
||||
{
|
||||
if ($this->primaryModel !== null) {
|
||||
// lazy loading
|
||||
if ($this->via instanceof self) {
|
||||
// via junction table
|
||||
$viaModels = $this->via->findJunctionRows([$this->primaryModel]);
|
||||
$this->filterByModels($viaModels);
|
||||
} elseif (is_array($this->via)) {
|
||||
// via relation
|
||||
/* @var $viaQuery ActiveQuery */
|
||||
list($viaName, $viaQuery) = $this->via;
|
||||
if ($viaQuery->multiple) {
|
||||
$viaModels = $viaQuery->all();
|
||||
$this->primaryModel->populateRelation($viaName, $viaModels);
|
||||
} else {
|
||||
$model = $viaQuery->one();
|
||||
$this->primaryModel->populateRelation($viaName, $model);
|
||||
$viaModels = $model === null ? [] : [$model];
|
||||
}
|
||||
$this->filterByModels($viaModels);
|
||||
} else {
|
||||
$this->filterByModels([$this->primaryModel]);
|
||||
}
|
||||
}
|
||||
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
|
||||
if ($db === null) {
|
||||
$db = $modelClass::getDb();
|
||||
}
|
||||
|
||||
// find by primary key if possible. This is much faster than scanning all records
|
||||
if (
|
||||
is_array($this->where)
|
||||
&& (
|
||||
(!isset($this->where[0]) && $modelClass::isPrimaryKey(array_keys($this->where)))
|
||||
|| (isset($this->where[0]) && $this->where[0] === 'in' && $modelClass::isPrimaryKey((array) $this->where[1]))
|
||||
)
|
||||
) {
|
||||
return $this->findByPk($db, $type, $columnName);
|
||||
}
|
||||
|
||||
$method = 'build' . $type;
|
||||
$script = $db->getLuaScriptBuilder()->$method($this, $columnName);
|
||||
|
||||
return $db->executeCommand('EVAL', [$script, 0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch by pk if possible as this is much faster
|
||||
* @param Connection $db the database connection used to execute the query.
|
||||
* If this parameter is not given, the `db` application component will be used.
|
||||
* @param string $type the type of the script to generate
|
||||
* @param string $columnName
|
||||
* @return array|bool|null|string
|
||||
* @throws \yii\base\InvalidParamException
|
||||
* @throws \yii\base\NotSupportedException
|
||||
*/
|
||||
private function findByPk($db, $type, $columnName = null)
|
||||
{
|
||||
$needSort = !empty($this->orderBy) && in_array($type, ['All', 'One', 'Column']);
|
||||
if ($needSort) {
|
||||
if (!is_array($this->orderBy) || count($this->orderBy) > 1) {
|
||||
throw new NotSupportedException(
|
||||
'orderBy by multiple columns is not currently supported by redis ActiveRecord.'
|
||||
);
|
||||
}
|
||||
|
||||
$k = key($this->orderBy);
|
||||
$v = $this->orderBy[$k];
|
||||
if (is_numeric($k)) {
|
||||
$orderColumn = $v;
|
||||
$orderType = SORT_ASC;
|
||||
} else {
|
||||
$orderColumn = $k;
|
||||
$orderType = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->where[0]) && $this->where[0] === 'in') {
|
||||
$pks = (array) $this->where[2];
|
||||
} elseif (count($this->where) == 1) {
|
||||
$pks = (array) reset($this->where);
|
||||
} else {
|
||||
foreach ($this->where as $values) {
|
||||
if (is_array($values)) {
|
||||
// TODO support composite IN for composite PK
|
||||
throw new NotSupportedException('Find by composite PK is not supported by redis ActiveRecord.');
|
||||
}
|
||||
}
|
||||
$pks = [$this->where];
|
||||
}
|
||||
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $this->modelClass;
|
||||
|
||||
if ($type === 'Count') {
|
||||
$start = 0;
|
||||
$limit = null;
|
||||
} else {
|
||||
$start = ($this->offset === null || $this->offset < 0) ? 0 : $this->offset;
|
||||
$limit = ($this->limit < 0) ? null : $this->limit;
|
||||
}
|
||||
$i = 0;
|
||||
$data = [];
|
||||
$orderArray = [];
|
||||
foreach ($pks as $pk) {
|
||||
if (++$i > $start && ($limit === null || $i <= $start + $limit)) {
|
||||
$key = $modelClass::keyPrefix() . ':a:' . $modelClass::buildKey($pk);
|
||||
$result = $db->executeCommand('HGETALL', [$key]);
|
||||
if (!empty($result)) {
|
||||
$data[] = $result;
|
||||
if ($needSort) {
|
||||
$orderArray[] = $db->executeCommand('HGET', [$key, $orderColumn]);
|
||||
}
|
||||
if ($type === 'One' && $this->orderBy === null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($needSort) {
|
||||
$resultData = [];
|
||||
if ($orderType === SORT_ASC) {
|
||||
asort($orderArray, SORT_NATURAL);
|
||||
} else {
|
||||
arsort($orderArray, SORT_NATURAL);
|
||||
}
|
||||
foreach ($orderArray as $orderKey => $orderItem) {
|
||||
$resultData[] = $data[$orderKey];
|
||||
}
|
||||
$data = $resultData;
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'All':
|
||||
return $data;
|
||||
case 'One':
|
||||
return reset($data);
|
||||
case 'Count':
|
||||
return count($data);
|
||||
case 'Column':
|
||||
$column = [];
|
||||
foreach ($data as $dataRow) {
|
||||
$row = [];
|
||||
$c = count($dataRow);
|
||||
for ($i = 0; $i < $c;) {
|
||||
$row[$dataRow[$i++]] = $dataRow[$i++];
|
||||
}
|
||||
$column[] = $row[$columnName];
|
||||
}
|
||||
|
||||
return $column;
|
||||
case 'Sum':
|
||||
$sum = 0;
|
||||
foreach ($data as $dataRow) {
|
||||
$c = count($dataRow);
|
||||
for ($i = 0; $i < $c;) {
|
||||
if ($dataRow[$i++] == $columnName) {
|
||||
$sum += $dataRow[$i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sum;
|
||||
case 'Average':
|
||||
$sum = 0;
|
||||
$count = 0;
|
||||
foreach ($data as $dataRow) {
|
||||
$count++;
|
||||
$c = count($dataRow);
|
||||
for ($i = 0; $i < $c;) {
|
||||
if ($dataRow[$i++] == $columnName) {
|
||||
$sum += $dataRow[$i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sum / $count;
|
||||
case 'Min':
|
||||
$min = null;
|
||||
foreach ($data as $dataRow) {
|
||||
$c = count($dataRow);
|
||||
for ($i = 0; $i < $c;) {
|
||||
if ($dataRow[$i++] == $columnName && ($min == null || $dataRow[$i] < $min)) {
|
||||
$min = $dataRow[$i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $min;
|
||||
case 'Max':
|
||||
$max = null;
|
||||
foreach ($data as $dataRow) {
|
||||
$c = count($dataRow);
|
||||
for ($i = 0; $i < $c;) {
|
||||
if ($dataRow[$i++] == $columnName && ($max == null || $dataRow[$i] > $max)) {
|
||||
$max = $dataRow[$i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $max;
|
||||
}
|
||||
throw new InvalidParamException('Unknown fetch type: ' . $type);
|
||||
}
|
||||
}
|
||||
343
vendor/yiisoft/yii2-redis/src/ActiveRecord.php
vendored
Normal file
343
vendor/yiisoft/yii2-redis/src/ActiveRecord.php
vendored
Normal file
@ -0,0 +1,343 @@
|
||||
<?php
|
||||
/**
|
||||
* @link https://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license https://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\redis;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\db\BaseActiveRecord;
|
||||
use yii\helpers\Inflector;
|
||||
use yii\helpers\StringHelper;
|
||||
|
||||
/**
|
||||
* ActiveRecord is the base class for classes representing relational data in terms of objects.
|
||||
*
|
||||
* This class implements the ActiveRecord pattern for the [redis](https://redis.io/) key-value store.
|
||||
*
|
||||
* For defining a record a subclass should at least implement the [[attributes()]] method to define
|
||||
* attributes. A primary key can be defined via [[primaryKey()]] which defaults to `id` if not specified.
|
||||
*
|
||||
* The following is an example model called `Customer`:
|
||||
*
|
||||
* ```php
|
||||
* class Customer extends \yii\redis\ActiveRecord
|
||||
* {
|
||||
* public function attributes()
|
||||
* {
|
||||
* return ['id', 'name', 'address', 'registration_date'];
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ActiveRecord extends BaseActiveRecord
|
||||
{
|
||||
/**
|
||||
* Returns the database connection used by this AR class.
|
||||
* By default, the "redis" application component is used as the database connection.
|
||||
* You may override this method if you want to use a different database connection.
|
||||
* @return Connection the database connection used by this AR class.
|
||||
*/
|
||||
public static function getDb()
|
||||
{
|
||||
return Yii::$app->get('redis');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @return ActiveQuery the newly created [[ActiveQuery]] instance.
|
||||
*/
|
||||
public static function find()
|
||||
{
|
||||
return Yii::createObject(ActiveQuery::className(), [get_called_class()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary key name(s) for this AR class.
|
||||
* This method should be overridden by child classes to define the primary key.
|
||||
*
|
||||
* Note that an array should be returned even when it is a single primary key.
|
||||
*
|
||||
* @return string[] the primary keys of this record.
|
||||
*/
|
||||
public static function primaryKey()
|
||||
{
|
||||
return ['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all attribute names of the model.
|
||||
* This method must be overridden by child classes to define available attributes.
|
||||
* @return array list of attribute names.
|
||||
*/
|
||||
public function attributes()
|
||||
{
|
||||
throw new InvalidConfigException('The attributes() method of redis ActiveRecord has to be implemented by child classes.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares prefix of the key that represents the keys that store this records in redis.
|
||||
* By default this method returns the class name as the table name by calling [[Inflector::camel2id()]].
|
||||
* For example, 'Customer' becomes 'customer', and 'OrderItem' becomes
|
||||
* 'order_item'. You may override this method if you want different key naming.
|
||||
* @return string the prefix to apply to all AR keys
|
||||
*/
|
||||
public static function keyPrefix()
|
||||
{
|
||||
return Inflector::camel2id(StringHelper::basename(get_called_class()), '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function insert($runValidation = true, $attributes = null)
|
||||
{
|
||||
if ($runValidation && !$this->validate($attributes)) {
|
||||
return false;
|
||||
}
|
||||
if (!$this->beforeSave(true)) {
|
||||
return false;
|
||||
}
|
||||
$db = static::getDb();
|
||||
$values = $this->getDirtyAttributes($attributes);
|
||||
$pk = [];
|
||||
foreach (static::primaryKey() as $key) {
|
||||
$pk[$key] = $values[$key] = $this->getAttribute($key);
|
||||
if ($pk[$key] === null) {
|
||||
// use auto increment if pk is null
|
||||
$pk[$key] = $values[$key] = $db->executeCommand('INCR', [static::keyPrefix() . ':s:' . $key]);
|
||||
$this->setAttribute($key, $values[$key]);
|
||||
} elseif (is_numeric($pk[$key])) {
|
||||
// if pk is numeric update auto increment value
|
||||
$currentPk = $db->executeCommand('GET', [static::keyPrefix() . ':s:' . $key]);
|
||||
if ($pk[$key] > $currentPk) {
|
||||
$db->executeCommand('SET', [static::keyPrefix() . ':s:' . $key, $pk[$key]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// save pk in a findall pool
|
||||
$pk = static::buildKey($pk);
|
||||
$db->executeCommand('RPUSH', [static::keyPrefix(), $pk]);
|
||||
|
||||
$key = static::keyPrefix() . ':a:' . $pk;
|
||||
// save attributes
|
||||
$setArgs = [$key];
|
||||
foreach ($values as $attribute => $value) {
|
||||
// only insert attributes that are not null
|
||||
if ($value !== null) {
|
||||
if (is_bool($value)) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
$setArgs[] = $attribute;
|
||||
$setArgs[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($setArgs) > 1) {
|
||||
$db->executeCommand('HMSET', $setArgs);
|
||||
}
|
||||
|
||||
$changedAttributes = array_fill_keys(array_keys($values), null);
|
||||
$this->setOldAttributes($values);
|
||||
$this->afterSave(true, $changedAttributes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the whole table using the provided attribute values and conditions.
|
||||
* For example, to change the status to be 1 for all customers whose status is 2:
|
||||
*
|
||||
* ~~~
|
||||
* Customer::updateAll(['status' => 1], ['id' => 2]);
|
||||
* ~~~
|
||||
*
|
||||
* @param array $attributes attribute values (name-value pairs) to be saved into the table
|
||||
* @param array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
|
||||
* Please refer to [[ActiveQuery::where()]] on how to specify this parameter.
|
||||
* @return int the number of rows updated
|
||||
*/
|
||||
public static function updateAll($attributes, $condition = null)
|
||||
{
|
||||
if (empty($attributes)) {
|
||||
return 0;
|
||||
}
|
||||
$db = static::getDb();
|
||||
$n = 0;
|
||||
foreach (self::fetchPks($condition) as $pk) {
|
||||
$newPk = $pk;
|
||||
$pk = static::buildKey($pk);
|
||||
$key = static::keyPrefix() . ':a:' . $pk;
|
||||
// save attributes
|
||||
$delArgs = [$key];
|
||||
$setArgs = [$key];
|
||||
foreach ($attributes as $attribute => $value) {
|
||||
if (isset($newPk[$attribute])) {
|
||||
$newPk[$attribute] = $value;
|
||||
}
|
||||
if ($value !== null) {
|
||||
if (is_bool($value)) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
$setArgs[] = $attribute;
|
||||
$setArgs[] = $value;
|
||||
} else {
|
||||
$delArgs[] = $attribute;
|
||||
}
|
||||
}
|
||||
$newPk = static::buildKey($newPk);
|
||||
$newKey = static::keyPrefix() . ':a:' . $newPk;
|
||||
// rename index if pk changed
|
||||
if ($newPk != $pk) {
|
||||
$db->executeCommand('MULTI');
|
||||
if (count($setArgs) > 1) {
|
||||
$db->executeCommand('HMSET', $setArgs);
|
||||
}
|
||||
if (count($delArgs) > 1) {
|
||||
$db->executeCommand('HDEL', $delArgs);
|
||||
}
|
||||
$db->executeCommand('LINSERT', [static::keyPrefix(), 'AFTER', $pk, $newPk]);
|
||||
$db->executeCommand('LREM', [static::keyPrefix(), 0, $pk]);
|
||||
$db->executeCommand('RENAME', [$key, $newKey]);
|
||||
$db->executeCommand('EXEC');
|
||||
} else {
|
||||
if (count($setArgs) > 1) {
|
||||
$db->executeCommand('HMSET', $setArgs);
|
||||
}
|
||||
if (count($delArgs) > 1) {
|
||||
$db->executeCommand('HDEL', $delArgs);
|
||||
}
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the whole table using the provided counter changes and conditions.
|
||||
* For example, to increment all customers' age by 1,
|
||||
*
|
||||
* ~~~
|
||||
* Customer::updateAllCounters(['age' => 1]);
|
||||
* ~~~
|
||||
*
|
||||
* @param array $counters the counters to be updated (attribute name => increment value).
|
||||
* Use negative values if you want to decrement the counters.
|
||||
* @param array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
|
||||
* Please refer to [[ActiveQuery::where()]] on how to specify this parameter.
|
||||
* @return int the number of rows updated
|
||||
*/
|
||||
public static function updateAllCounters($counters, $condition = null)
|
||||
{
|
||||
if (empty($counters)) {
|
||||
return 0;
|
||||
}
|
||||
$db = static::getDb();
|
||||
$n = 0;
|
||||
foreach (self::fetchPks($condition) as $pk) {
|
||||
$key = static::keyPrefix() . ':a:' . static::buildKey($pk);
|
||||
foreach ($counters as $attribute => $value) {
|
||||
$db->executeCommand('HINCRBY', [$key, $attribute, $value]);
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes rows in the table using the provided conditions.
|
||||
* WARNING: If you do not specify any condition, this method will delete ALL rows in the table.
|
||||
*
|
||||
* For example, to delete all customers whose status is 3:
|
||||
*
|
||||
* ~~~
|
||||
* Customer::deleteAll(['status' => 3]);
|
||||
* ~~~
|
||||
*
|
||||
* @param array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
|
||||
* Please refer to [[ActiveQuery::where()]] on how to specify this parameter.
|
||||
* @return int the number of rows deleted
|
||||
*/
|
||||
public static function deleteAll($condition = null)
|
||||
{
|
||||
$pks = self::fetchPks($condition);
|
||||
if (empty($pks)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$db = static::getDb();
|
||||
$attributeKeys = [];
|
||||
$db->executeCommand('MULTI');
|
||||
foreach ($pks as $pk) {
|
||||
$pk = static::buildKey($pk);
|
||||
$db->executeCommand('LREM', [static::keyPrefix(), 0, $pk]);
|
||||
$attributeKeys[] = static::keyPrefix() . ':a:' . $pk;
|
||||
}
|
||||
$db->executeCommand('DEL', $attributeKeys);
|
||||
$result = $db->executeCommand('EXEC');
|
||||
|
||||
return end($result);
|
||||
}
|
||||
|
||||
private static function fetchPks($condition)
|
||||
{
|
||||
$query = static::find();
|
||||
$query->where($condition);
|
||||
$records = $query->asArray()->all(); // TODO limit fetched columns to pk
|
||||
$primaryKey = static::primaryKey();
|
||||
|
||||
$pks = [];
|
||||
foreach ($records as $record) {
|
||||
$pk = [];
|
||||
foreach ($primaryKey as $key) {
|
||||
$pk[$key] = $record[$key];
|
||||
}
|
||||
$pks[] = $pk;
|
||||
}
|
||||
|
||||
return $pks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a normalized key from a given primary key value.
|
||||
*
|
||||
* @param mixed $key the key to be normalized
|
||||
* @return string the generated key
|
||||
*/
|
||||
public static function buildKey($key)
|
||||
{
|
||||
if (is_numeric($key)) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
if (is_string($key)) {
|
||||
return ctype_alnum($key) && StringHelper::byteLength($key) <= 32 ? $key : md5($key);
|
||||
}
|
||||
|
||||
if (is_array($key)) {
|
||||
if (count($key) == 1) {
|
||||
return self::buildKey(reset($key));
|
||||
}
|
||||
ksort($key); // ensure order is always the same
|
||||
$isNumeric = true;
|
||||
foreach ($key as $value) {
|
||||
if (!is_numeric($value)) {
|
||||
$isNumeric = false;
|
||||
}
|
||||
}
|
||||
if ($isNumeric) {
|
||||
return implode('-', $key);
|
||||
}
|
||||
}
|
||||
|
||||
return md5(json_encode($key, JSON_NUMERIC_CHECK));
|
||||
}
|
||||
}
|
||||
397
vendor/yiisoft/yii2-redis/src/Cache.php
vendored
Normal file
397
vendor/yiisoft/yii2-redis/src/Cache.php
vendored
Normal file
@ -0,0 +1,397 @@
|
||||
<?php
|
||||
/**
|
||||
* @link https://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license https://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\redis;
|
||||
|
||||
use yii\db\Exception;
|
||||
use yii\di\Instance;
|
||||
|
||||
/**
|
||||
* Redis Cache implements a cache application component based on [redis](https://redis.io/) key-value store.
|
||||
*
|
||||
* Redis Cache requires redis version 2.6.12 or higher to work properly.
|
||||
*
|
||||
* It needs to be configured with a redis [[Connection]]. By default it will use the `redis` application component.
|
||||
*
|
||||
* > Note: It is recommended to use separate [[Connection::$database|database]] for cache and do not share it with
|
||||
* > other components. If you need to share database, you should set [[$shareDatabase]] to `true` and make sure that
|
||||
* > [[$keyPrefix]] has unique value which will allow to distinguish between cache keys and other data in database.
|
||||
*
|
||||
* See [[yii\caching\Cache]] manual for common cache operations that redis Cache supports.
|
||||
*
|
||||
* Unlike the [[yii\caching\Cache]], redis Cache allows the expire parameter of [[set]], [[add]], [[mset]] and [[madd]] to
|
||||
* be a floating point number, so you may specify the time in milliseconds (e.g. 0.1 will be 100 milliseconds).
|
||||
*
|
||||
* To use redis Cache as the cache application component, configure the application as follows,
|
||||
*
|
||||
* ~~~
|
||||
* [
|
||||
* 'components' => [
|
||||
* 'cache' => [
|
||||
* 'class' => 'yii\redis\Cache',
|
||||
* 'redis' => [
|
||||
* 'hostname' => 'localhost',
|
||||
* 'port' => 6379,
|
||||
* 'database' => 0,
|
||||
* ]
|
||||
* ],
|
||||
* ],
|
||||
* ]
|
||||
* ~~~
|
||||
*
|
||||
* Or if you have configured the redis [[Connection]] as an application component, the following is sufficient:
|
||||
*
|
||||
* ~~~
|
||||
* [
|
||||
* 'components' => [
|
||||
* 'cache' => [
|
||||
* 'class' => 'yii\redis\Cache',
|
||||
* // 'redis' => 'redis' // id of the connection application component
|
||||
* ],
|
||||
* ],
|
||||
* ]
|
||||
* ~~~
|
||||
*
|
||||
* If you have multiple redis replicas (e.g. AWS ElasticCache Redis) you can configure the cache to
|
||||
* send read operations to the replicas. If no replicas are configured, all operations will be performed on the
|
||||
* master connection configured via the [[redis]] property.
|
||||
*
|
||||
* ~~~
|
||||
* [
|
||||
* 'components' => [
|
||||
* 'cache' => [
|
||||
* 'class' => 'yii\redis\Cache',
|
||||
* 'enableReplicas' => true,
|
||||
* 'replicas' => [
|
||||
* // config for replica redis connections, (default class will be yii\redis\Connection if not provided)
|
||||
* // you can optionally put in master as hostname as well, as all GET operation will use replicas
|
||||
* 'redis',//id of Redis [[Connection]] Component
|
||||
* ['hostname' => 'redis-slave-002.xyz.0001.apse1.cache.amazonaws.com'],
|
||||
* ['hostname' => 'redis-slave-003.xyz.0001.apse1.cache.amazonaws.com'],
|
||||
* ],
|
||||
* ],
|
||||
* ],
|
||||
* ]
|
||||
* ~~~
|
||||
*
|
||||
* If you're using redis in cluster mode and want to use `MGET` and `MSET` effectively, you will need to supply a
|
||||
* [hash tag](https://redis.io/topics/cluster-spec#keys-hash-tags) to allocate cache keys to the same hash slot.
|
||||
*
|
||||
* ~~~
|
||||
* \Yii::$app->cache->multiSet([
|
||||
* 'posts{user1}' => 123,
|
||||
* 'settings{user1}' => [
|
||||
* 'showNickname' => false,
|
||||
* 'sortBy' => 'created_at',
|
||||
* ],
|
||||
* 'unreadMessages{user1}' => 5,
|
||||
* ]);
|
||||
* ~~~
|
||||
*
|
||||
* @property-read bool $isCluster Whether redis is running in cluster mode or not.
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Cache extends \yii\caching\Cache
|
||||
{
|
||||
/**
|
||||
* @var ConnectionInterface|string|array the Redis [[Connection]] object or the application component ID of the Redis [[Connection]].
|
||||
* This can also be an array that is used to create a redis [[Connection]] instance in case you do not want do configure
|
||||
* redis connection as an application component.
|
||||
* After the Cache object is created, if you want to change this property, you should only assign it
|
||||
* with a Redis [[Connection]] object.
|
||||
*/
|
||||
public $redis = 'redis';
|
||||
/**
|
||||
* @var bool whether to enable read / get from redis replicas.
|
||||
* @since 2.0.8
|
||||
* @see $replicas
|
||||
*/
|
||||
public $enableReplicas = false;
|
||||
/**
|
||||
* @var array the Redis [[Connection]] configurations for redis replicas.
|
||||
* Each entry is a class configuration, which will be used to instantiate a replica connection.
|
||||
* The default class is [[Connection|yii\redis\Connection]]. You should at least provide a hostname.
|
||||
*
|
||||
* Configuration example:
|
||||
*
|
||||
* ```php
|
||||
* 'replicas' => [
|
||||
* 'redis',
|
||||
* ['hostname' => 'redis-slave-002.xyz.0001.apse1.cache.amazonaws.com'],
|
||||
* ['hostname' => 'redis-slave-003.xyz.0001.apse1.cache.amazonaws.com'],
|
||||
* ],
|
||||
* ```
|
||||
*
|
||||
* @since 2.0.8
|
||||
* @see $enableReplicas
|
||||
*/
|
||||
public $replicas = [];
|
||||
/**
|
||||
* @var bool|null force cluster mode, don't check on every request. If this is null, cluster mode will be checked
|
||||
* once per request whenever the cache is accessed. To disable the check, set to true if cluster mode
|
||||
* should be enabled, or false if it should be disabled.
|
||||
* @since 2.0.11
|
||||
*/
|
||||
public $forceClusterMode = false;
|
||||
/**
|
||||
* @var bool whether redis [[Connection::$database|database]] is shared and can contain other data than cache.
|
||||
* Setting this to `true` will change [[flush()]] behavior - instead of using [`FLUSHDB`](https://redis.io/commands/flushdb)
|
||||
* command, component will iterate through all keys in database and remove only these with matching [[$keyPrefix]].
|
||||
* Note that this will no longer be an atomic operation and it is much less efficient than `FLUSHDB` command. It is
|
||||
* recommended to use separate database for cache and leave this value as `false`.
|
||||
* @since 2.0.12
|
||||
*/
|
||||
public $shareDatabase = false;
|
||||
|
||||
/**
|
||||
* @var ConnectionInterface currently active connection.
|
||||
*/
|
||||
private $_replica;
|
||||
/**
|
||||
* @var bool remember if redis is in cluster mode for the whole request
|
||||
*/
|
||||
private $_isCluster;
|
||||
/**
|
||||
* @var bool if hash tags were supplied for a MGET/MSET operation
|
||||
*/
|
||||
private $_hashTagAvailable = false;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the redis Cache component.
|
||||
* This method will initialize the [[redis]] property to make sure it refers to a valid redis connection.
|
||||
* @throws \yii\base\InvalidConfigException if [[redis]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->redis = Instance::ensure($this->redis, ConnectionInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a specified key exists in the cache.
|
||||
* This can be faster than getting the value from the cache if the data is big.
|
||||
* Note that this method does not check whether the dependency associated
|
||||
* with the cached data, if there is any, has changed. So a call to [[get]]
|
||||
* may return false while exists returns true.
|
||||
* @param mixed $key a key identifying the cached value. This can be a simple string or
|
||||
* a complex data structure consisting of factors representing the key.
|
||||
* @return bool true if a value exists in cache, false if the value is not in the cache or expired.
|
||||
*/
|
||||
public function exists($key)
|
||||
{
|
||||
return (bool) $this->redis->executeCommand('EXISTS', [$this->buildKey($key)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getValue($key)
|
||||
{
|
||||
$value = $this->getReplica()->executeCommand('GET', [$key]);
|
||||
if ($value === null) {
|
||||
return false; // Key is not in the cache or expired
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getValues($keys)
|
||||
{
|
||||
if ($this->isCluster && !$this->_hashTagAvailable) {
|
||||
return parent::getValues($keys);
|
||||
}
|
||||
|
||||
$response = $this->getReplica()->executeCommand('MGET', $keys);
|
||||
$result = [];
|
||||
$i = 0;
|
||||
foreach ($keys as $key) {
|
||||
$result[$key] = $response[$i++];
|
||||
}
|
||||
|
||||
$this->_hashTagAvailable = false;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function buildKey($key)
|
||||
{
|
||||
if (
|
||||
is_string($key)
|
||||
&& $this->isCluster
|
||||
&& preg_match('/^(.*)({.+})(.*)$/', $key, $matches) === 1) {
|
||||
|
||||
$this->_hashTagAvailable = true;
|
||||
|
||||
return parent::buildKey($matches[1] . $matches[3]) . $matches[2];
|
||||
}
|
||||
|
||||
return parent::buildKey($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function setValue($key, $value, $expire)
|
||||
{
|
||||
if ($expire == 0) {
|
||||
return (bool) $this->redis->executeCommand('SET', [$key, $value]);
|
||||
}
|
||||
|
||||
$expire = (int) ($expire * 1000);
|
||||
|
||||
return (bool) $this->redis->executeCommand('SET', [$key, $value, 'PX', $expire]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function setValues($data, $expire)
|
||||
{
|
||||
if ($this->isCluster && !$this->_hashTagAvailable) {
|
||||
return parent::setValues($data, $expire);
|
||||
}
|
||||
|
||||
$args = [];
|
||||
foreach ($data as $key => $value) {
|
||||
$args[] = $key;
|
||||
$args[] = $value;
|
||||
}
|
||||
|
||||
$failedKeys = [];
|
||||
if ($expire == 0) {
|
||||
$this->redis->executeCommand('MSET', $args);
|
||||
} else {
|
||||
$expire = (int) ($expire * 1000);
|
||||
$this->redis->executeCommand('MULTI');
|
||||
$this->redis->executeCommand('MSET', $args);
|
||||
$index = [];
|
||||
foreach ($data as $key => $value) {
|
||||
$this->redis->executeCommand('PEXPIRE', [$key, $expire]);
|
||||
$index[] = $key;
|
||||
}
|
||||
$result = $this->redis->executeCommand('EXEC');
|
||||
array_shift($result);
|
||||
foreach ($result as $i => $r) {
|
||||
if ($r != 1) {
|
||||
$failedKeys[] = $index[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_hashTagAvailable = false;
|
||||
|
||||
return $failedKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the redis extension is forced to run in cluster mode through config or the redis command
|
||||
* `CLUSTER INFO` executes successfully, `false` otherwise.
|
||||
*
|
||||
* Setting [[forceClusterMode]] to either `true` or `false` is preferred.
|
||||
* @return bool whether redis is running in cluster mode or not
|
||||
* @since 2.0.11
|
||||
*/
|
||||
public function getIsCluster()
|
||||
{
|
||||
if ($this->forceClusterMode !== null) {
|
||||
return $this->forceClusterMode;
|
||||
}
|
||||
|
||||
if ($this->_isCluster === null) {
|
||||
$this->_isCluster = false;
|
||||
try {
|
||||
$this->redis->executeCommand('CLUSTER INFO');
|
||||
$this->_isCluster = true;
|
||||
} catch (Exception $exception) {
|
||||
// if redis is running without cluster support, this command results in:
|
||||
// `ERR This instance has cluster support disabled`
|
||||
// and [[Connection::executeCommand]] throws an exception
|
||||
// we want to ignore it
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_isCluster;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function addValue($key, $value, $expire)
|
||||
{
|
||||
if ($expire == 0) {
|
||||
return (bool) $this->redis->executeCommand('SET', [$key, $value, 'NX']);
|
||||
}
|
||||
|
||||
$expire = (int) ($expire * 1000);
|
||||
|
||||
return (bool) $this->redis->executeCommand('SET', [$key, $value, 'PX', $expire, 'NX']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function deleteValue($key)
|
||||
{
|
||||
return (bool) $this->redis->executeCommand('DEL', [$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function flushValues()
|
||||
{
|
||||
if ($this->shareDatabase) {
|
||||
$cursor = 0;
|
||||
do {
|
||||
list($cursor, $keys) = $this->redis->scan($cursor, 'MATCH', $this->keyPrefix . '*');
|
||||
$cursor = (int) $cursor;
|
||||
if (!empty($keys)) {
|
||||
$this->redis->executeCommand('DEL', $keys);
|
||||
}
|
||||
} while ($cursor !== 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->redis->executeCommand('FLUSHDB');
|
||||
}
|
||||
|
||||
/**
|
||||
* It will return the current Replica Redis [[Connection]], and fall back to default [[redis]] [[Connection]]
|
||||
* defined in this instance. Only used in getValue() and getValues().
|
||||
* @since 2.0.8
|
||||
* @return array|string|ConnectionInterface
|
||||
* @throws \yii\base\InvalidConfigException
|
||||
*/
|
||||
protected function getReplica()
|
||||
{
|
||||
// @NOTE Predis uses its own implementation of balancing
|
||||
if ($this->enableReplicas === false || $this->redis instanceof \yii\redis\Predis\PredisConnection) {
|
||||
return $this->redis;
|
||||
}
|
||||
|
||||
if ($this->_replica !== null) {
|
||||
return $this->_replica;
|
||||
}
|
||||
|
||||
if (empty($this->replicas)) {
|
||||
return $this->_replica = $this->redis;
|
||||
}
|
||||
|
||||
$replicas = $this->replicas;
|
||||
shuffle($replicas);
|
||||
$config = array_shift($replicas);
|
||||
$this->_replica = Instance::ensure($config, Connection::className());
|
||||
return $this->_replica;
|
||||
}
|
||||
}
|
||||
720
vendor/yiisoft/yii2-redis/src/Connection.php
vendored
Normal file
720
vendor/yiisoft/yii2-redis/src/Connection.php
vendored
Normal file
@ -0,0 +1,720 @@
|
||||
<?php
|
||||
/**
|
||||
* @link https://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license https://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\redis;
|
||||
|
||||
use yii\base\Component;
|
||||
use yii\db\Exception;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\helpers\Inflector;
|
||||
use yii\helpers\VarDumper;
|
||||
|
||||
/**
|
||||
* The redis connection class is used to establish a connection to a [redis](https://redis.io/) server.
|
||||
*
|
||||
* By default it assumes there is a redis server running on localhost at port 6379 and uses the database number 0.
|
||||
*
|
||||
* It is possible to connect to a redis server using [[hostname]] and [[port]] or using a [[unixSocket]].
|
||||
*
|
||||
* It also supports [the AUTH command](https://redis.io/commands/auth) of redis.
|
||||
* When the server needs authentication, you can set the [[password]] property to
|
||||
* authenticate with the server after connect.
|
||||
*
|
||||
* @property-read string $connectionString Socket connection string.
|
||||
* @property-read string $driverName Name of the DB driver.
|
||||
* @property-read bool $isActive Whether the DB connection is established.
|
||||
* @property-read LuaScriptBuilder $luaScriptBuilder
|
||||
* @property-read resource|false $socket
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Connection extends Component implements ConnectionInterface
|
||||
{
|
||||
/**
|
||||
* @event Event an event that is triggered after a DB connection is established
|
||||
*/
|
||||
const EVENT_AFTER_OPEN = 'afterOpen';
|
||||
|
||||
/**
|
||||
* @var string the hostname or ip address to use for connecting to the redis server. Defaults to 'localhost'.
|
||||
* If [[unixSocket]] is specified, hostname and [[port]] will be ignored.
|
||||
*/
|
||||
public $hostname = 'localhost';
|
||||
/**
|
||||
* @var string the connection scheme used for connecting to the redis server. Defaults to 'tcp'.
|
||||
* @since 2.0.18
|
||||
*/
|
||||
public $scheme = 'tcp';
|
||||
/**
|
||||
* @var string if the query gets redirected, use this as the temporary new hostname
|
||||
* @since 2.0.11
|
||||
*/
|
||||
public $redirectConnectionString;
|
||||
/**
|
||||
* @var integer the port to use for connecting to the redis server. Default port is 6379.
|
||||
* If [[unixSocket]] is specified, [[hostname]] and port will be ignored.
|
||||
*/
|
||||
public $port = 6379;
|
||||
/**
|
||||
* @var string the unix socket path (e.g. `/var/run/redis/redis.sock`) to use for connecting to the redis server.
|
||||
* This can be used instead of [[hostname]] and [[port]] to connect to the server using a unix socket.
|
||||
* If a unix socket path is specified, [[hostname]] and [[port]] will be ignored.
|
||||
* @since 2.0.1
|
||||
*/
|
||||
public $unixSocket;
|
||||
/**
|
||||
* @var string|null username for establishing DB connection. Defaults to `null` meaning AUTH command will be performed without username.
|
||||
* Username was introduced in Redis 6.
|
||||
* @link https://redis.io/commands/auth
|
||||
* @link https://redis.io/topics/acl
|
||||
* @since 2.0.16
|
||||
*/
|
||||
public $username;
|
||||
/**
|
||||
* @var string the password for establishing DB connection. Defaults to null meaning no AUTH command is sent.
|
||||
* See https://redis.io/commands/auth
|
||||
*/
|
||||
public $password;
|
||||
/**
|
||||
* @var integer the redis database to use. This is an integer value starting from 0. Defaults to 0.
|
||||
* Since version 2.0.6 you can disable the SELECT command sent after connection by setting this property to `null`.
|
||||
*/
|
||||
public $database = 0;
|
||||
/**
|
||||
* @var float timeout to use for connection to redis. If not set the timeout set in php.ini will be used: `ini_get("default_socket_timeout")`.
|
||||
*/
|
||||
public $connectionTimeout;
|
||||
/**
|
||||
* @var float timeout to use for redis socket when reading and writing data. If not set the php default value will be used.
|
||||
*/
|
||||
public $dataTimeout;
|
||||
/**
|
||||
* @var boolean Send sockets over SSL protocol. Default state is false.
|
||||
* @since 2.0.12
|
||||
*/
|
||||
public $useSSL = false;
|
||||
/**
|
||||
* @var array PHP context options which are used in the Redis connection stream.
|
||||
* @see https://www.php.net/manual/en/context.ssl.php
|
||||
* @since 2.0.15
|
||||
*/
|
||||
public $contextOptions = [];
|
||||
/**
|
||||
* @var integer Bitmask field which may be set to any combination of connection flags passed to [stream_socket_client()](https://www.php.net/manual/en/function.stream-socket-client.php).
|
||||
* Currently the select of connection flags is limited to `STREAM_CLIENT_CONNECT` (default), `STREAM_CLIENT_ASYNC_CONNECT` and `STREAM_CLIENT_PERSISTENT`.
|
||||
*
|
||||
* > Warning: `STREAM_CLIENT_PERSISTENT` will make PHP reuse connections to the same server. If you are using multiple
|
||||
* > connection objects to refer to different redis [[$database|databases]] on the same [[port]], redis commands may
|
||||
* > get executed on the wrong database. `STREAM_CLIENT_PERSISTENT` is only safe to use if you use only one database.
|
||||
* >
|
||||
* > You may still use persistent connections in this case when disambiguating ports as described
|
||||
* > in [a comment on the PHP manual](https://www.php.net/manual/en/function.stream-socket-client.php#105393)
|
||||
* > e.g. on the connection used for session storage, specify the port as:
|
||||
* >
|
||||
* > ```php
|
||||
* > 'port' => '6379/session'
|
||||
* > ```
|
||||
*
|
||||
* @see https://www.php.net/manual/en/function.stream-socket-client.php
|
||||
* @since 2.0.5
|
||||
*/
|
||||
public $socketClientFlags = STREAM_CLIENT_CONNECT;
|
||||
/**
|
||||
* @var integer The number of times a command execution should be retried when a connection failure occurs.
|
||||
* This is used in [[executeCommand()]] when a [[SocketException]] is thrown.
|
||||
* Defaults to 0 meaning no retries on failure.
|
||||
* @since 2.0.7
|
||||
*/
|
||||
public $retries = 0;
|
||||
/**
|
||||
* @var integer The retry interval in microseconds to wait between retry.
|
||||
* This is used in [[executeCommand()]] when a [[SocketException]] is thrown.
|
||||
* Defaults to 0 meaning no wait.
|
||||
* @since 2.0.10
|
||||
*/
|
||||
public $retryInterval = 0;
|
||||
/**
|
||||
* @var array List of available redis commands.
|
||||
* @see https://redis.io/commands
|
||||
*/
|
||||
public $redisCommands = [
|
||||
'APPEND', // Append a value to a key
|
||||
'AUTH', // Authenticate to the server
|
||||
'BGREWRITEAOF', // Asynchronously rewrite the append-only file
|
||||
'BGSAVE', // Asynchronously save the dataset to disk
|
||||
'BITCOUNT', // Count set bits in a string
|
||||
'BITFIELD', // Perform arbitrary bitfield integer operations on strings
|
||||
'BITOP', // Perform bitwise operations between strings
|
||||
'BITPOS', // Find first bit set or clear in a string
|
||||
'BLPOP', // Remove and get the first element in a list, or block until one is available
|
||||
'BRPOP', // Remove and get the last element in a list, or block until one is available
|
||||
'BRPOPLPUSH', // Pop a value from a list, push it to another list and return it; or block until one is available
|
||||
'CLIENT KILL', // Kill the connection of a client
|
||||
'CLIENT LIST', // Get the list of client connections
|
||||
'CLIENT GETNAME', // Get the current connection name
|
||||
'CLIENT PAUSE', // Stop processing commands from clients for some time
|
||||
'CLIENT REPLY', // Instruct the server whether to reply to commands
|
||||
'CLIENT SETNAME', // Set the current connection name
|
||||
'CLUSTER ADDSLOTS', // Assign new hash slots to receiving node
|
||||
'CLUSTER COUNTKEYSINSLOT', // Return the number of local keys in the specified hash slot
|
||||
'CLUSTER DELSLOTS', // Set hash slots as unbound in receiving node
|
||||
'CLUSTER FAILOVER', // Forces a slave to perform a manual failover of its master.
|
||||
'CLUSTER FORGET', // Remove a node from the nodes table
|
||||
'CLUSTER GETKEYSINSLOT', // Return local key names in the specified hash slot
|
||||
'CLUSTER INFO', // Provides info about Redis Cluster node state
|
||||
'CLUSTER KEYSLOT', // Returns the hash slot of the specified key
|
||||
'CLUSTER MEET', // Force a node cluster to handshake with another node
|
||||
'CLUSTER NODES', // Get Cluster config for the node
|
||||
'CLUSTER REPLICATE', // Reconfigure a node as a slave of the specified master node
|
||||
'CLUSTER RESET', // Reset a Redis Cluster node
|
||||
'CLUSTER SAVECONFIG', // Forces the node to save cluster state on disk
|
||||
'CLUSTER SETSLOT', // Bind a hash slot to a specific node
|
||||
'CLUSTER SLAVES', // List slave nodes of the specified master node
|
||||
'CLUSTER SLOTS', // Get array of Cluster slot to node mappings
|
||||
'COMMAND', // Get array of Redis command details
|
||||
'COMMAND COUNT', // Get total number of Redis commands
|
||||
'COMMAND GETKEYS', // Extract keys given a full Redis command
|
||||
'COMMAND INFO', // Get array of specific Redis command details
|
||||
'CONFIG GET', // Get the value of a configuration parameter
|
||||
'CONFIG REWRITE', // Rewrite the configuration file with the in memory configuration
|
||||
'CONFIG SET', // Set a configuration parameter to the given value
|
||||
'CONFIG RESETSTAT', // Reset the stats returned by INFO
|
||||
'DBSIZE', // Return the number of keys in the selected database
|
||||
'DEBUG OBJECT', // Get debugging information about a key
|
||||
'DEBUG SEGFAULT', // Make the server crash
|
||||
'DECR', // Decrement the integer value of a key by one
|
||||
'DECRBY', // Decrement the integer value of a key by the given number
|
||||
'DEL', // Delete a key
|
||||
'DISCARD', // Discard all commands issued after MULTI
|
||||
'DUMP', // Return a serialized version of the value stored at the specified key.
|
||||
'ECHO', // Echo the given string
|
||||
'EVAL', // Execute a Lua script server side
|
||||
'EVALSHA', // Execute a Lua script server side
|
||||
'EXEC', // Execute all commands issued after MULTI
|
||||
'EXISTS', // Determine if a key exists
|
||||
'EXPIRE', // Set a key's time to live in seconds
|
||||
'EXPIREAT', // Set the expiration for a key as a UNIX timestamp
|
||||
'FLUSHALL', // Remove all keys from all databases
|
||||
'FLUSHDB', // Remove all keys from the current database
|
||||
'GEOADD', // Add one or more geospatial items in the geospatial index represented using a sorted set
|
||||
'GEOHASH', // Returns members of a geospatial index as standard geohash strings
|
||||
'GEOPOS', // Returns longitude and latitude of members of a geospatial index
|
||||
'GEODIST', // Returns the distance between two members of a geospatial index
|
||||
'GEORADIUS', // Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point
|
||||
'GEORADIUSBYMEMBER', // Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member
|
||||
'GET', // Get the value of a key
|
||||
'GETBIT', // Returns the bit value at offset in the string value stored at key
|
||||
'GETRANGE', // Get a substring of the string stored at a key
|
||||
'GETSET', // Set the string value of a key and return its old value
|
||||
'HDEL', // Delete one or more hash fields
|
||||
'HEXISTS', // Determine if a hash field exists
|
||||
'HGET', // Get the value of a hash field
|
||||
'HGETALL', // Get all the fields and values in a hash
|
||||
'HINCRBY', // Increment the integer value of a hash field by the given number
|
||||
'HINCRBYFLOAT', // Increment the float value of a hash field by the given amount
|
||||
'HKEYS', // Get all the fields in a hash
|
||||
'HLEN', // Get the number of fields in a hash
|
||||
'HMGET', // Get the values of all the given hash fields
|
||||
'HMSET', // Set multiple hash fields to multiple values
|
||||
'HSET', // Set the string value of a hash field
|
||||
'HSETNX', // Set the value of a hash field, only if the field does not exist
|
||||
'HSTRLEN', // Get the length of the value of a hash field
|
||||
'HVALS', // Get all the values in a hash
|
||||
'INCR', // Increment the integer value of a key by one
|
||||
'INCRBY', // Increment the integer value of a key by the given amount
|
||||
'INCRBYFLOAT', // Increment the float value of a key by the given amount
|
||||
'INFO', // Get information and statistics about the server
|
||||
'KEYS', // Find all keys matching the given pattern
|
||||
'LASTSAVE', // Get the UNIX time stamp of the last successful save to disk
|
||||
'LINDEX', // Get an element from a list by its index
|
||||
'LINSERT', // Insert an element before or after another element in a list
|
||||
'LLEN', // Get the length of a list
|
||||
'LPOP', // Remove and get the first element in a list
|
||||
'LPUSH', // Prepend one or multiple values to a list
|
||||
'LPUSHX', // Prepend a value to a list, only if the list exists
|
||||
'LRANGE', // Get a range of elements from a list
|
||||
'LREM', // Remove elements from a list
|
||||
'LSET', // Set the value of an element in a list by its index
|
||||
'LTRIM', // Trim a list to the specified range
|
||||
'MGET', // Get the values of all the given keys
|
||||
'MIGRATE', // Atomically transfer a key from a Redis instance to another one.
|
||||
'MONITOR', // Listen for all requests received by the server in real time
|
||||
'MOVE', // Move a key to another database
|
||||
'MSET', // Set multiple keys to multiple values
|
||||
'MSETNX', // Set multiple keys to multiple values, only if none of the keys exist
|
||||
'MULTI', // Mark the start of a transaction block
|
||||
'OBJECT', // Inspect the internals of Redis objects
|
||||
'PERSIST', // Remove the expiration from a key
|
||||
'PEXPIRE', // Set a key's time to live in milliseconds
|
||||
'PEXPIREAT', // Set the expiration for a key as a UNIX timestamp specified in milliseconds
|
||||
'PFADD', // Adds the specified elements to the specified HyperLogLog.
|
||||
'PFCOUNT', // Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s).
|
||||
'PFMERGE', // Merge N different HyperLogLogs into a single one.
|
||||
'PING', // Ping the server
|
||||
'PSETEX', // Set the value and expiration in milliseconds of a key
|
||||
'PSUBSCRIBE', // Listen for messages published to channels matching the given patterns
|
||||
'PUBSUB', // Inspect the state of the Pub/Sub subsystem
|
||||
'PTTL', // Get the time to live for a key in milliseconds
|
||||
'PUBLISH', // Post a message to a channel
|
||||
'PUNSUBSCRIBE', // Stop listening for messages posted to channels matching the given patterns
|
||||
'QUIT', // Close the connection
|
||||
'RANDOMKEY', // Return a random key from the keyspace
|
||||
'READONLY', // Enables read queries for a connection to a cluster slave node
|
||||
'READWRITE', // Disables read queries for a connection to a cluster slave node
|
||||
'RENAME', // Rename a key
|
||||
'RENAMENX', // Rename a key, only if the new key does not exist
|
||||
'RESTORE', // Create a key using the provided serialized value, previously obtained using DUMP.
|
||||
'ROLE', // Return the role of the instance in the context of replication
|
||||
'RPOP', // Remove and get the last element in a list
|
||||
'RPOPLPUSH', // Remove the last element in a list, prepend it to another list and return it
|
||||
'RPUSH', // Append one or multiple values to a list
|
||||
'RPUSHX', // Append a value to a list, only if the list exists
|
||||
'SADD', // Add one or more members to a set
|
||||
'SAVE', // Synchronously save the dataset to disk
|
||||
'SCARD', // Get the number of members in a set
|
||||
'SCRIPT DEBUG', // Set the debug mode for executed scripts.
|
||||
'SCRIPT EXISTS', // Check existence of scripts in the script cache.
|
||||
'SCRIPT FLUSH', // Remove all the scripts from the script cache.
|
||||
'SCRIPT KILL', // Kill the script currently in execution.
|
||||
'SCRIPT LOAD', // Load the specified Lua script into the script cache.
|
||||
'SDIFF', // Subtract multiple sets
|
||||
'SDIFFSTORE', // Subtract multiple sets and store the resulting set in a key
|
||||
'SELECT', // Change the selected database for the current connection
|
||||
'SET', // Set the string value of a key
|
||||
'SETBIT', // Sets or clears the bit at offset in the string value stored at key
|
||||
'SETEX', // Set the value and expiration of a key
|
||||
'SETNX', // Set the value of a key, only if the key does not exist
|
||||
'SETRANGE', // Overwrite part of a string at key starting at the specified offset
|
||||
'SHUTDOWN', // Synchronously save the dataset to disk and then shut down the server
|
||||
'SINTER', // Intersect multiple sets
|
||||
'SINTERSTORE', // Intersect multiple sets and store the resulting set in a key
|
||||
'SISMEMBER', // Determine if a given value is a member of a set
|
||||
'SLAVEOF', // Make the server a slave of another instance, or promote it as master
|
||||
'SLOWLOG', // Manages the Redis slow queries log
|
||||
'SMEMBERS', // Get all the members in a set
|
||||
'SMOVE', // Move a member from one set to another
|
||||
'SORT', // Sort the elements in a list, set or sorted set
|
||||
'SPOP', // Remove and return one or multiple random members from a set
|
||||
'SRANDMEMBER', // Get one or multiple random members from a set
|
||||
'SREM', // Remove one or more members from a set
|
||||
'STRLEN', // Get the length of the value stored in a key
|
||||
'SUBSCRIBE', // Listen for messages published to the given channels
|
||||
'SUNION', // Add multiple sets
|
||||
'SUNIONSTORE', // Add multiple sets and store the resulting set in a key
|
||||
'SWAPDB', // Swaps two Redis databases
|
||||
'SYNC', // Internal command used for replication
|
||||
'TIME', // Return the current server time
|
||||
'TOUCH', // Alters the last access time of a key(s). Returns the number of existing keys specified.
|
||||
'TTL', // Get the time to live for a key
|
||||
'TYPE', // Determine the type stored at key
|
||||
'UNSUBSCRIBE', // Stop listening for messages posted to the given channels
|
||||
'UNLINK', // Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.
|
||||
'UNWATCH', // Forget about all watched keys
|
||||
'WAIT', // Wait for the synchronous replication of all the write commands sent in the context of the current connection
|
||||
'WATCH', // Watch the given keys to determine execution of the MULTI/EXEC block
|
||||
'XACK', // Removes one or multiple messages from the pending entries list (PEL) of a stream consumer group
|
||||
'XADD', // Appends the specified stream entry to the stream at the specified key
|
||||
'XCLAIM', // Changes the ownership of a pending message, so that the new owner is the consumer specified as the command argument
|
||||
'XDEL', // Removes the specified entries from a stream, and returns the number of entries deleted
|
||||
'XGROUP', // Manages the consumer groups associated with a stream data structure
|
||||
'XINFO', // Retrieves different information about the streams and associated consumer groups
|
||||
'XLEN', // Returns the number of entries inside a stream
|
||||
'XPENDING', // Fetching data from a stream via a consumer group, and not acknowledging such data, has the effect of creating pending entries
|
||||
'XRANGE', // Returns the stream entries matching a given range of IDs
|
||||
'XREAD', // Read data from one or multiple streams, only returning entries with an ID greater than the last received ID reported by the caller
|
||||
'XREADGROUP', // Special version of the XREAD command with support for consumer groups
|
||||
'XREVRANGE', // Exactly like XRANGE, but with the notable difference of returning the entries in reverse order, and also taking the start-end range in reverse order
|
||||
'XTRIM', // Trims the stream to a given number of items, evicting older items (items with lower IDs) if needed
|
||||
'ZADD', // Add one or more members to a sorted set, or update its score if it already exists
|
||||
'ZCARD', // Get the number of members in a sorted set
|
||||
'ZCOUNT', // Count the members in a sorted set with scores within the given values
|
||||
'ZINCRBY', // Increment the score of a member in a sorted set
|
||||
'ZINTERSTORE', // Intersect multiple sorted sets and store the resulting sorted set in a new key
|
||||
'ZLEXCOUNT', // Count the number of members in a sorted set between a given lexicographical range
|
||||
'ZRANGE', // Return a range of members in a sorted set, by index
|
||||
'ZRANGEBYLEX', // Return a range of members in a sorted set, by lexicographical range
|
||||
'ZREVRANGEBYLEX', // Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.
|
||||
'ZRANGEBYSCORE', // Return a range of members in a sorted set, by score
|
||||
'ZRANK', // Determine the index of a member in a sorted set
|
||||
'ZREM', // Remove one or more members from a sorted set
|
||||
'ZREMRANGEBYLEX', // Remove all members in a sorted set between the given lexicographical range
|
||||
'ZREMRANGEBYRANK', // Remove all members in a sorted set within the given indexes
|
||||
'ZREMRANGEBYSCORE', // Remove all members in a sorted set within the given scores
|
||||
'ZREVRANGE', // Return a range of members in a sorted set, by index, with scores ordered from high to low
|
||||
'ZREVRANGEBYSCORE', // Return a range of members in a sorted set, by score, with scores ordered from high to low
|
||||
'ZREVRANK', // Determine the index of a member in a sorted set, with scores ordered from high to low
|
||||
'ZSCORE', // Get the score associated with the given member in a sorted set
|
||||
'ZUNIONSTORE', // Add multiple sorted sets and store the resulting sorted set in a new key
|
||||
'SCAN', // Incrementally iterate the keys space
|
||||
'SSCAN', // Incrementally iterate Set elements
|
||||
'HSCAN', // Incrementally iterate hash fields and associated values
|
||||
'ZSCAN', // Incrementally iterate sorted sets elements and associated scores
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array redis redirect socket connection pool
|
||||
*/
|
||||
private $_pool = [];
|
||||
|
||||
|
||||
/**
|
||||
* Closes the connection when this component is being serialized.
|
||||
* @return array
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
$this->close();
|
||||
return array_keys(get_object_vars($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the connection string used to open a socket connection. During a redirect (cluster mode) this will be the
|
||||
* target of the redirect.
|
||||
* @return string socket connection string
|
||||
* @since 2.0.11
|
||||
*/
|
||||
public function getConnectionString()
|
||||
{
|
||||
if ($this->unixSocket) {
|
||||
return 'unix://' . $this->unixSocket;
|
||||
}
|
||||
|
||||
return $this->scheme . '://' . ($this->redirectConnectionString ?: "$this->hostname:$this->port");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the connection resource if a connection to the target has been established before, `false` otherwise.
|
||||
* @return resource|false
|
||||
*/
|
||||
public function getSocket()
|
||||
{
|
||||
return ArrayHelper::getValue($this->_pool, $this->connectionString, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the DB connection is established.
|
||||
* @return bool whether the DB connection is established
|
||||
*/
|
||||
public function getIsActive(): bool
|
||||
{
|
||||
return ArrayHelper::getValue($this->_pool, $this->connectionString, false) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a DB connection.
|
||||
* It does nothing if a DB connection has already been established.
|
||||
* @throws Exception if connection fails
|
||||
*/
|
||||
public function open(): void
|
||||
{
|
||||
if ($this->socket !== false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$connection = $this->connectionString . ', database=' . $this->database;
|
||||
\Yii::debug('Opening redis DB connection: ' . $connection, __METHOD__);
|
||||
$socket = @stream_socket_client(
|
||||
$this->connectionString,
|
||||
$errorNumber,
|
||||
$errorDescription,
|
||||
$this->connectionTimeout ?: ini_get('default_socket_timeout'),
|
||||
$this->socketClientFlags,
|
||||
stream_context_create($this->contextOptions)
|
||||
);
|
||||
|
||||
if ($socket) {
|
||||
$this->_pool[ $this->connectionString ] = $socket;
|
||||
|
||||
if ($this->dataTimeout !== null) {
|
||||
stream_set_timeout($socket, $timeout = (int) $this->dataTimeout, (int) (($this->dataTimeout - $timeout) * 1000000));
|
||||
}
|
||||
if ($this->useSSL) {
|
||||
stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
|
||||
}
|
||||
if ($this->password !== null) {
|
||||
$this->executeCommand('AUTH', array_filter([$this->username, $this->password]));
|
||||
}
|
||||
if ($this->database !== null) {
|
||||
$this->executeCommand('SELECT', [$this->database]);
|
||||
}
|
||||
$this->initConnection();
|
||||
} else {
|
||||
\Yii::error("Failed to open redis DB connection ($connection): $errorNumber - $errorDescription", __CLASS__);
|
||||
$message = YII_DEBUG ? "Failed to open redis DB connection ($connection): $errorNumber - $errorDescription" : 'Failed to open DB connection.';
|
||||
throw new Exception($message, $errorDescription, $errorNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the currently active DB connection.
|
||||
* It does nothing if the connection is already closed.
|
||||
*/
|
||||
public function close(): void
|
||||
{
|
||||
foreach ($this->_pool as $socket) {
|
||||
$connection = $this->connectionString . ', database=' . $this->database;
|
||||
\Yii::debug('Closing DB connection: ' . $connection, __METHOD__);
|
||||
try {
|
||||
$this->executeCommand('QUIT');
|
||||
} catch (SocketException $e) {
|
||||
// ignore errors when quitting a closed connection
|
||||
}
|
||||
fclose($socket);
|
||||
}
|
||||
|
||||
$this->_pool = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the DB connection.
|
||||
* This method is invoked right after the DB connection is established.
|
||||
* The default implementation triggers an [[EVENT_AFTER_OPEN]] event.
|
||||
*/
|
||||
protected function initConnection()
|
||||
{
|
||||
$this->trigger(self::EVENT_AFTER_OPEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the DB driver for the current [[dsn]].
|
||||
* @return string name of the DB driver
|
||||
*/
|
||||
public function getDriverName()
|
||||
{
|
||||
return 'redis';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LuaScriptBuilder
|
||||
*/
|
||||
public function getLuaScriptBuilder()
|
||||
{
|
||||
return new LuaScriptBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows issuing all supported commands via magic methods.
|
||||
*
|
||||
* ```php
|
||||
* $redis->hmset('test_collection', 'key1', 'val1', 'key2', 'val2')
|
||||
* ```
|
||||
*
|
||||
* @param string $name name of the missing method to execute
|
||||
* @param array $params method call arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($name, $params)
|
||||
{
|
||||
$redisCommand = strtoupper(Inflector::camel2words($name, false));
|
||||
if (in_array($redisCommand, $this->redisCommands)) {
|
||||
return $this->executeCommand($redisCommand, $params);
|
||||
}
|
||||
|
||||
return parent::__call($name, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a redis command.
|
||||
* For a list of available commands and their parameters see https://redis.io/commands.
|
||||
*
|
||||
* The params array should contain the params separated by white space, e.g. to execute
|
||||
* `SET mykey somevalue NX` call the following:
|
||||
*
|
||||
* ```php
|
||||
* $redis->executeCommand('SET', ['mykey', 'somevalue', 'NX']);
|
||||
* ```
|
||||
*
|
||||
* @param string $name the name of the command
|
||||
* @param array $params list of parameters for the command
|
||||
* @return array|bool|null|string Dependent on the executed command this method
|
||||
* will return different data types:
|
||||
*
|
||||
* - `true` for commands that return "status reply" with the message `'OK'` or `'PONG'`.
|
||||
* - `string` for commands that return "status reply" that does not have the message `OK` (since version 2.0.1).
|
||||
* - `string` for commands that return "integer reply"
|
||||
* as the value is in the range of a signed 64 bit integer.
|
||||
* - `string` or `null` for commands that return "bulk reply".
|
||||
* - `array` for commands that return "Multi-bulk replies".
|
||||
*
|
||||
* See [redis protocol description](https://redis.io/topics/protocol)
|
||||
* for details on the mentioned reply types.
|
||||
* @throws Exception for commands that return [error reply](https://redis.io/topics/protocol#error-reply).
|
||||
*/
|
||||
public function executeCommand($name, $params = [])
|
||||
{
|
||||
$this->open();
|
||||
|
||||
$params = array_merge(explode(' ', $name), $params);
|
||||
$command = '*' . count($params) . "\r\n";
|
||||
foreach ($params as $arg) {
|
||||
$command .= '$' . mb_strlen($arg ?? '', '8bit') . "\r\n" . $arg . "\r\n";
|
||||
}
|
||||
|
||||
\Yii::debug("Executing Redis Command: {$name}", __METHOD__);
|
||||
if ($this->retries > 0) {
|
||||
$tries = $this->retries;
|
||||
while ($tries-- > 0) {
|
||||
try {
|
||||
return $this->sendRawCommand($command, $params);
|
||||
} catch (SocketException $e) {
|
||||
\Yii::error($e, __METHOD__);
|
||||
// backup retries, fail on commands that fail inside here
|
||||
$retries = $this->retries;
|
||||
$this->retries = 0;
|
||||
$this->close();
|
||||
if ($this->retryInterval > 0) {
|
||||
usleep($this->retryInterval);
|
||||
}
|
||||
try {
|
||||
$this->open();
|
||||
} catch (SocketException $exception) {
|
||||
// Fail to run initial commands, skip current try
|
||||
\Yii::error($exception, __METHOD__);
|
||||
$this->close();
|
||||
} catch (Exception $exception) {
|
||||
$this->close();
|
||||
}
|
||||
|
||||
$this->retries = $retries;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->sendRawCommand($command, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends RAW command string to the server.
|
||||
*
|
||||
* @param string $command command string
|
||||
* @param array $params list of parameters for the command
|
||||
*
|
||||
* @return array|bool|null|string Dependent on the executed command this method
|
||||
* will return different data types:
|
||||
*
|
||||
* - `true` for commands that return "status reply" with the message `'OK'` or `'PONG'`.
|
||||
* - `string` for commands that return "status reply" that does not have the message `OK` (since version 2.0.1).
|
||||
* - `string` for commands that return "integer reply"
|
||||
* as the value is in the range of a signed 64 bit integer.
|
||||
* - `string` or `null` for commands that return "bulk reply".
|
||||
* - `array` for commands that return "Multi-bulk replies".
|
||||
*
|
||||
* See [redis protocol description](https://redis.io/topics/protocol)
|
||||
* for details on the mentioned reply types.
|
||||
* @throws Exception for commands that return [error reply](https://redis.io/topics/protocol#error-reply).
|
||||
* @throws SocketException on connection error.
|
||||
*/
|
||||
protected function sendRawCommand($command, $params)
|
||||
{
|
||||
$written = @fwrite($this->socket, $command);
|
||||
if ($written === false) {
|
||||
throw new SocketException("Failed to write to socket.\nRedis command was: " . $command);
|
||||
}
|
||||
if ($written !== ($len = mb_strlen($command, '8bit'))) {
|
||||
throw new SocketException("Failed to write to socket. $written of $len bytes written.\nRedis command was: " . $command);
|
||||
}
|
||||
|
||||
return $this->parseResponse($params, $command);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @param string|null $command
|
||||
* @return mixed
|
||||
* @throws Exception on error
|
||||
* @throws SocketException
|
||||
*/
|
||||
private function parseResponse($params, $command = null)
|
||||
{
|
||||
if (($line = fgets($this->socket)) === false) {
|
||||
throw new SocketException("Failed to read from socket.\nRedis command was: " . implode(' ', $params));
|
||||
}
|
||||
$type = $line[0];
|
||||
$line = mb_substr($line, 1, -2, '8bit');
|
||||
switch ($type) {
|
||||
case '+': // Status reply
|
||||
if ($line === 'OK' || $line === 'PONG') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $line;
|
||||
case '-': // Error reply
|
||||
|
||||
if ($this->isRedirect($line)) {
|
||||
return $this->redirect($line, $command, $params);
|
||||
}
|
||||
|
||||
throw new Exception("Redis error: " . $line . "\nRedis command was: " . implode(' ', $params));
|
||||
case ':': // Integer reply
|
||||
// no cast to int as it is in the range of a signed 64 bit integer
|
||||
return $line;
|
||||
case '$': // Bulk replies
|
||||
if ($line == '-1') {
|
||||
return null;
|
||||
}
|
||||
$length = (int)$line + 2;
|
||||
$data = '';
|
||||
while ($length > 0) {
|
||||
if (($block = fread($this->socket, $length)) === false) {
|
||||
throw new SocketException("Failed to read from socket.\nRedis command was: " . implode(' ', $params));
|
||||
}
|
||||
$data .= $block;
|
||||
$length -= mb_strlen($block, '8bit');
|
||||
}
|
||||
|
||||
return mb_substr($data, 0, -2, '8bit');
|
||||
case '*': // Multi-bulk replies
|
||||
$count = (int) $line;
|
||||
$data = [];
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$data[] = $this->parseResponse($params);
|
||||
}
|
||||
|
||||
return $data;
|
||||
default:
|
||||
throw new Exception('Received illegal data from redis: ' . $line . "\nRedis command was: " . implode(' ', $params));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $line
|
||||
* @return bool
|
||||
*/
|
||||
private function isRedirect($line)
|
||||
{
|
||||
return is_string($line) && mb_strpos($line, 'MOVED') === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $redirect
|
||||
* @param string $command
|
||||
* @param array $params
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
* @throws SocketException
|
||||
*/
|
||||
private function redirect($redirect, $command, $params)
|
||||
{
|
||||
$responseParts = preg_split('/\s+/', $redirect);
|
||||
|
||||
$this->redirectConnectionString = ArrayHelper::getValue($responseParts, 2);
|
||||
|
||||
if ($this->redirectConnectionString) {
|
||||
\Yii::info('Redirecting to ' . $this->connectionString, __METHOD__);
|
||||
|
||||
$this->open();
|
||||
|
||||
$response = $this->sendRawCommand($command, $params);
|
||||
|
||||
$this->redirectConnectionString = null;
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
throw new Exception('No hostname found in redis redirect (MOVED): ' . VarDumper::dumpAsString($redirect));
|
||||
}
|
||||
}
|
||||
234
vendor/yiisoft/yii2-redis/src/ConnectionInterface.php
vendored
Normal file
234
vendor/yiisoft/yii2-redis/src/ConnectionInterface.php
vendored
Normal file
@ -0,0 +1,234 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace yii\redis;
|
||||
|
||||
/**
|
||||
* The execution of [redis commands](https://redis.io/commands) is possible with via [[executeCommand()]].
|
||||
* @method mixed append($key, $value) Append a value to a key. <https://redis.io/commands/append>
|
||||
* @method mixed auth($password) Authenticate to the server. <https://redis.io/commands/auth>
|
||||
* @method mixed bgrewriteaof() Asynchronously rewrite the append-only file. <https://redis.io/commands/bgrewriteaof>
|
||||
* @method mixed bgsave() Asynchronously save the dataset to disk. <https://redis.io/commands/bgsave>
|
||||
* @method mixed bitcount($key, $start = null, $end = null) Count set bits in a string. <https://redis.io/commands/bitcount>
|
||||
* @method mixed bitfield($key, ...$operations) Perform arbitrary bitfield integer operations on strings. <https://redis.io/commands/bitfield>
|
||||
* @method mixed bitop($operation, $destkey, ...$keys) Perform bitwise operations between strings. <https://redis.io/commands/bitop>
|
||||
* @method mixed bitpos($key, $bit, $start = null, $end = null) Find first bit set or clear in a string. <https://redis.io/commands/bitpos>
|
||||
* @method mixed blpop(...$keys, $timeout) Remove and get the first element in a list, or block until one is available. <https://redis.io/commands/blpop>
|
||||
* @method mixed brpop(...$keys, $timeout) Remove and get the last element in a list, or block until one is available. <https://redis.io/commands/brpop>
|
||||
* @method mixed brpoplpush($source, $destination, $timeout) Pop a value from a list, push it to another list and return it; or block until one is available. <https://redis.io/commands/brpoplpush>
|
||||
* @method mixed clientKill(...$filters) Kill the connection of a client. <https://redis.io/commands/client-kill>
|
||||
* @method mixed clientList() Get the list of client connections. <https://redis.io/commands/client-list>
|
||||
* @method mixed clientGetname() Get the current connection name. <https://redis.io/commands/client-getname>
|
||||
* @method mixed clientPause($timeout) Stop processing commands from clients for some time. <https://redis.io/commands/client-pause>
|
||||
* @method mixed clientReply($option) Instruct the server whether to reply to commands. <https://redis.io/commands/client-reply>
|
||||
* @method mixed clientSetname($connectionName) Set the current connection name. <https://redis.io/commands/client-setname>
|
||||
* @method mixed clusterAddslots(...$slots) Assign new hash slots to receiving node. <https://redis.io/commands/cluster-addslots>
|
||||
* @method mixed clusterCountkeysinslot($slot) Return the number of local keys in the specified hash slot. <https://redis.io/commands/cluster-countkeysinslot>
|
||||
* @method mixed clusterDelslots(...$slots) Set hash slots as unbound in receiving node. <https://redis.io/commands/cluster-delslots>
|
||||
* @method mixed clusterFailover($option = null) Forces a slave to perform a manual failover of its master.. <https://redis.io/commands/cluster-failover>
|
||||
* @method mixed clusterForget($nodeId) Remove a node from the nodes table. <https://redis.io/commands/cluster-forget>
|
||||
* @method mixed clusterGetkeysinslot($slot, $count) Return local key names in the specified hash slot. <https://redis.io/commands/cluster-getkeysinslot>
|
||||
* @method mixed clusterInfo() Provides info about Redis Cluster node state. <https://redis.io/commands/cluster-info>
|
||||
* @method mixed clusterKeyslot($key) Returns the hash slot of the specified key. <https://redis.io/commands/cluster-keyslot>
|
||||
* @method mixed clusterMeet($ip, $port) Force a node cluster to handshake with another node. <https://redis.io/commands/cluster-meet>
|
||||
* @method mixed clusterNodes() Get Cluster config for the node. <https://redis.io/commands/cluster-nodes>
|
||||
* @method mixed clusterReplicate($nodeId) Reconfigure a node as a slave of the specified master node. <https://redis.io/commands/cluster-replicate>
|
||||
* @method mixed clusterReset($resetType = "SOFT") Reset a Redis Cluster node. <https://redis.io/commands/cluster-reset>
|
||||
* @method mixed clusterSaveconfig() Forces the node to save cluster state on disk. <https://redis.io/commands/cluster-saveconfig>
|
||||
* @method mixed clusterSetslot($slot, $type, $nodeid = null) Bind a hash slot to a specific node. <https://redis.io/commands/cluster-setslot>
|
||||
* @method mixed clusterSlaves($nodeId) List slave nodes of the specified master node. <https://redis.io/commands/cluster-slaves>
|
||||
* @method mixed clusterSlots() Get array of Cluster slot to node mappings. <https://redis.io/commands/cluster-slots>
|
||||
* @method mixed command() Get array of Redis command details. <https://redis.io/commands/command>
|
||||
* @method mixed commandCount() Get total number of Redis commands. <https://redis.io/commands/command-count>
|
||||
* @method mixed commandGetkeys() Extract keys given a full Redis command. <https://redis.io/commands/command-getkeys>
|
||||
* @method mixed commandInfo(...$commandNames) Get array of specific Redis command details. <https://redis.io/commands/command-info>
|
||||
* @method mixed configGet($parameter) Get the value of a configuration parameter. <https://redis.io/commands/config-get>
|
||||
* @method mixed configRewrite() Rewrite the configuration file with the in memory configuration. <https://redis.io/commands/config-rewrite>
|
||||
* @method mixed configSet($parameter, $value) Set a configuration parameter to the given value. <https://redis.io/commands/config-set>
|
||||
* @method mixed configResetstat() Reset the stats returned by INFO. <https://redis.io/commands/config-resetstat>
|
||||
* @method mixed dbsize() Return the number of keys in the selected database. <https://redis.io/commands/dbsize>
|
||||
* @method mixed debugObject($key) Get debugging information about a key. <https://redis.io/commands/debug-object>
|
||||
* @method mixed debugSegfault() Make the server crash. <https://redis.io/commands/debug-segfault>
|
||||
* @method mixed decr($key) Decrement the integer value of a key by one. <https://redis.io/commands/decr>
|
||||
* @method mixed decrby($key, $decrement) Decrement the integer value of a key by the given number. <https://redis.io/commands/decrby>
|
||||
* @method mixed del(...$keys) Delete a key. <https://redis.io/commands/del>
|
||||
* @method mixed discard() Discard all commands issued after MULTI. <https://redis.io/commands/discard>
|
||||
* @method mixed dump($key) Return a serialized version of the value stored at the specified key.. <https://redis.io/commands/dump>
|
||||
* @method mixed echo ($message) Echo the given string. <https://redis.io/commands/echo>
|
||||
* @method mixed eval($script, $numkeys, ...$keys, ...$args) Execute a Lua script server side. <https://redis.io/commands/eval>
|
||||
* @method mixed evalsha($sha1, $numkeys, ...$keys, ...$args) Execute a Lua script server side. <https://redis.io/commands/evalsha>
|
||||
* @method mixed exec() Execute all commands issued after MULTI. <https://redis.io/commands/exec>
|
||||
* @method mixed exists(...$keys) Determine if a key exists. <https://redis.io/commands/exists>
|
||||
* @method mixed expire($key, $seconds) Set a key's time to live in seconds. <https://redis.io/commands/expire>
|
||||
* @method mixed expireat($key, $timestamp) Set the expiration for a key as a UNIX timestamp. <https://redis.io/commands/expireat>
|
||||
* @method mixed flushall($ASYNC = null) Remove all keys from all databases. <https://redis.io/commands/flushall>
|
||||
* @method mixed flushdb($ASYNC = null) Remove all keys from the current database. <https://redis.io/commands/flushdb>
|
||||
* @method mixed geoadd($key, $longitude, $latitude, $member, ...$more) Add one or more geospatial items in the geospatial index represented using a sorted set. <https://redis.io/commands/geoadd>
|
||||
* @method mixed geohash($key, ...$members) Returns members of a geospatial index as standard geohash strings. <https://redis.io/commands/geohash>
|
||||
* @method mixed geopos($key, ...$members) Returns longitude and latitude of members of a geospatial index. <https://redis.io/commands/geopos>
|
||||
* @method mixed geodist($key, $member1, $member2, $unit = null) Returns the distance between two members of a geospatial index. <https://redis.io/commands/geodist>
|
||||
* @method mixed georadius($key, $longitude, $latitude, $radius, $metric, ...$options) Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point. <https://redis.io/commands/georadius>
|
||||
* @method mixed georadiusbymember($key, $member, $radius, $metric, ...$options) Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member. <https://redis.io/commands/georadiusbymember>
|
||||
* @method mixed get($key) Get the value of a key. <https://redis.io/commands/get>
|
||||
* @method mixed getbit($key, $offset) Returns the bit value at offset in the string value stored at key. <https://redis.io/commands/getbit>
|
||||
* @method mixed getrange($key, $start, $end) Get a substring of the string stored at a key. <https://redis.io/commands/getrange>
|
||||
* @method mixed getset($key, $value) Set the string value of a key and return its old value. <https://redis.io/commands/getset>
|
||||
* @method mixed hdel($key, ...$fields) Delete one or more hash fields. <https://redis.io/commands/hdel>
|
||||
* @method mixed hexists($key, $field) Determine if a hash field exists. <https://redis.io/commands/hexists>
|
||||
* @method mixed hget($key, $field) Get the value of a hash field. <https://redis.io/commands/hget>
|
||||
* @method mixed hgetall($key) Get all the fields and values in a hash. <https://redis.io/commands/hgetall>
|
||||
* @method mixed hincrby($key, $field, $increment) Increment the integer value of a hash field by the given number. <https://redis.io/commands/hincrby>
|
||||
* @method mixed hincrbyfloat($key, $field, $increment) Increment the float value of a hash field by the given amount. <https://redis.io/commands/hincrbyfloat>
|
||||
* @method mixed hkeys($key) Get all the fields in a hash. <https://redis.io/commands/hkeys>
|
||||
* @method mixed hlen($key) Get the number of fields in a hash. <https://redis.io/commands/hlen>
|
||||
* @method mixed hmget($key, ...$fields) Get the values of all the given hash fields. <https://redis.io/commands/hmget>
|
||||
* @method mixed hmset($key, $field, $value, ...$more) Set multiple hash fields to multiple values. <https://redis.io/commands/hmset>
|
||||
* @method mixed hset($key, $field, $value) Set the string value of a hash field. <https://redis.io/commands/hset>
|
||||
* @method mixed hsetnx($key, $field, $value) Set the value of a hash field, only if the field does not exist. <https://redis.io/commands/hsetnx>
|
||||
* @method mixed hstrlen($key, $field) Get the length of the value of a hash field. <https://redis.io/commands/hstrlen>
|
||||
* @method mixed hvals($key) Get all the values in a hash. <https://redis.io/commands/hvals>
|
||||
* @method mixed incr($key) Increment the integer value of a key by one. <https://redis.io/commands/incr>
|
||||
* @method mixed incrby($key, $increment) Increment the integer value of a key by the given amount. <https://redis.io/commands/incrby>
|
||||
* @method mixed incrbyfloat($key, $increment) Increment the float value of a key by the given amount. <https://redis.io/commands/incrbyfloat>
|
||||
* @method mixed info($section = null) Get information and statistics about the server. <https://redis.io/commands/info>
|
||||
* @method mixed keys($pattern) Find all keys matching the given pattern. <https://redis.io/commands/keys>
|
||||
* @method mixed lastsave() Get the UNIX time stamp of the last successful save to disk. <https://redis.io/commands/lastsave>
|
||||
* @method mixed lindex($key, $index) Get an element from a list by its index. <https://redis.io/commands/lindex>
|
||||
* @method mixed linsert($key, $where, $pivot, $value) Insert an element before or after another element in a list. <https://redis.io/commands/linsert>
|
||||
* @method mixed llen($key) Get the length of a list. <https://redis.io/commands/llen>
|
||||
* @method mixed lpop($key) Remove and get the first element in a list. <https://redis.io/commands/lpop>
|
||||
* @method mixed lpush($key, ...$values) Prepend one or multiple values to a list. <https://redis.io/commands/lpush>
|
||||
* @method mixed lpushx($key, $value) Prepend a value to a list, only if the list exists. <https://redis.io/commands/lpushx>
|
||||
* @method mixed lrange($key, $start, $stop) Get a range of elements from a list. <https://redis.io/commands/lrange>
|
||||
* @method mixed lrem($key, $count, $value) Remove elements from a list. <https://redis.io/commands/lrem>
|
||||
* @method mixed lset($key, $index, $value) Set the value of an element in a list by its index. <https://redis.io/commands/lset>
|
||||
* @method mixed ltrim($key, $start, $stop) Trim a list to the specified range. <https://redis.io/commands/ltrim>
|
||||
* @method mixed mget(...$keys) Get the values of all the given keys. <https://redis.io/commands/mget>
|
||||
* @method mixed migrate($host, $port, $key, $destinationDb, $timeout, ...$options) Atomically transfer a key from a Redis instance to another one.. <https://redis.io/commands/migrate>
|
||||
* @method mixed monitor() Listen for all requests received by the server in real time. <https://redis.io/commands/monitor>
|
||||
* @method mixed move($key, $db) Move a key to another database. <https://redis.io/commands/move>
|
||||
* @method mixed mset(...$keyValuePairs) Set multiple keys to multiple values. <https://redis.io/commands/mset>
|
||||
* @method mixed msetnx(...$keyValuePairs) Set multiple keys to multiple values, only if none of the keys exist. <https://redis.io/commands/msetnx>
|
||||
* @method mixed multi() Mark the start of a transaction block. <https://redis.io/commands/multi>
|
||||
* @method mixed object($subcommand, ...$argumentss) Inspect the internals of Redis objects. <https://redis.io/commands/object>
|
||||
* @method mixed persist($key) Remove the expiration from a key. <https://redis.io/commands/persist>
|
||||
* @method mixed pexpire($key, $milliseconds) Set a key's time to live in milliseconds. <https://redis.io/commands/pexpire>
|
||||
* @method mixed pexpireat($key, $millisecondsTimestamp) Set the expiration for a key as a UNIX timestamp specified in milliseconds. <https://redis.io/commands/pexpireat>
|
||||
* @method mixed pfadd($key, ...$elements) Adds the specified elements to the specified HyperLogLog.. <https://redis.io/commands/pfadd>
|
||||
* @method mixed pfcount(...$keys) Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s).. <https://redis.io/commands/pfcount>
|
||||
* @method mixed pfmerge($destkey, ...$sourcekeys) Merge N different HyperLogLogs into a single one.. <https://redis.io/commands/pfmerge>
|
||||
* @method mixed ping($message = null) Ping the server. <https://redis.io/commands/ping>
|
||||
* @method mixed psetex($key, $milliseconds, $value) Set the value and expiration in milliseconds of a key. <https://redis.io/commands/psetex>
|
||||
* @method mixed psubscribe(...$patterns) Listen for messages published to channels matching the given patterns. <https://redis.io/commands/psubscribe>
|
||||
* @method mixed pubsub($subcommand, ...$arguments) Inspect the state of the Pub/Sub subsystem. <https://redis.io/commands/pubsub>
|
||||
* @method mixed pttl($key) Get the time to live for a key in milliseconds. <https://redis.io/commands/pttl>
|
||||
* @method mixed publish($channel, $message) Post a message to a channel. <https://redis.io/commands/publish>
|
||||
* @method mixed punsubscribe(...$patterns) Stop listening for messages posted to channels matching the given patterns. <https://redis.io/commands/punsubscribe>
|
||||
* @method mixed quit() Close the connection. <https://redis.io/commands/quit>
|
||||
* @method mixed randomkey() Return a random key from the keyspace. <https://redis.io/commands/randomkey>
|
||||
* @method mixed readonly() Enables read queries for a connection to a cluster slave node. <https://redis.io/commands/readonly>
|
||||
* @method mixed readwrite() Disables read queries for a connection to a cluster slave node. <https://redis.io/commands/readwrite>
|
||||
* @method mixed rename($key, $newkey) Rename a key. <https://redis.io/commands/rename>
|
||||
* @method mixed renamenx($key, $newkey) Rename a key, only if the new key does not exist. <https://redis.io/commands/renamenx>
|
||||
* @method mixed restore($key, $ttl, $serializedValue, $REPLACE = null) Create a key using the provided serialized value, previously obtained using DUMP.. <https://redis.io/commands/restore>
|
||||
* @method mixed role() Return the role of the instance in the context of replication. <https://redis.io/commands/role>
|
||||
* @method mixed rpop($key) Remove and get the last element in a list. <https://redis.io/commands/rpop>
|
||||
* @method mixed rpoplpush($source, $destination) Remove the last element in a list, prepend it to another list and return it. <https://redis.io/commands/rpoplpush>
|
||||
* @method mixed rpush($key, ...$values) Append one or multiple values to a list. <https://redis.io/commands/rpush>
|
||||
* @method mixed rpushx($key, $value) Append a value to a list, only if the list exists. <https://redis.io/commands/rpushx>
|
||||
* @method mixed sadd($key, ...$members) Add one or more members to a set. <https://redis.io/commands/sadd>
|
||||
* @method mixed save() Synchronously save the dataset to disk. <https://redis.io/commands/save>
|
||||
* @method mixed scard($key) Get the number of members in a set. <https://redis.io/commands/scard>
|
||||
* @method mixed scriptDebug($option) Set the debug mode for executed scripts.. <https://redis.io/commands/script-debug>
|
||||
* @method mixed scriptExists(...$sha1s) Check existence of scripts in the script cache.. <https://redis.io/commands/script-exists>
|
||||
* @method mixed scriptFlush() Remove all the scripts from the script cache.. <https://redis.io/commands/script-flush>
|
||||
* @method mixed scriptKill() Kill the script currently in execution.. <https://redis.io/commands/script-kill>
|
||||
* @method mixed scriptLoad($script) Load the specified Lua script into the script cache.. <https://redis.io/commands/script-load>
|
||||
* @method mixed sdiff(...$keys) Subtract multiple sets. <https://redis.io/commands/sdiff>
|
||||
* @method mixed sdiffstore($destination, ...$keys) Subtract multiple sets and store the resulting set in a key. <https://redis.io/commands/sdiffstore>
|
||||
* @method mixed select($index) Change the selected database for the current connection. <https://redis.io/commands/select>
|
||||
* @method mixed set($key, $value, ...$options) Set the string value of a key. <https://redis.io/commands/set>
|
||||
* @method mixed setbit($key, $offset, $value) Sets or clears the bit at offset in the string value stored at key. <https://redis.io/commands/setbit>
|
||||
* @method mixed setex($key, $seconds, $value) Set the value and expiration of a key. <https://redis.io/commands/setex>
|
||||
* @method mixed setnx($key, $value) Set the value of a key, only if the key does not exist. <https://redis.io/commands/setnx>
|
||||
* @method mixed setrange($key, $offset, $value) Overwrite part of a string at key starting at the specified offset. <https://redis.io/commands/setrange>
|
||||
* @method mixed shutdown($saveOption = null) Synchronously save the dataset to disk and then shut down the server. <https://redis.io/commands/shutdown>
|
||||
* @method mixed sinter(...$keys) Intersect multiple sets. <https://redis.io/commands/sinter>
|
||||
* @method mixed sinterstore($destination, ...$keys) Intersect multiple sets and store the resulting set in a key. <https://redis.io/commands/sinterstore>
|
||||
* @method mixed sismember($key, $member) Determine if a given value is a member of a set. <https://redis.io/commands/sismember>
|
||||
* @method mixed slaveof($host, $port) Make the server a slave of another instance, or promote it as master. <https://redis.io/commands/slaveof>
|
||||
* @method mixed slowlog($subcommand, $argument = null) Manages the Redis slow queries log. <https://redis.io/commands/slowlog>
|
||||
* @method mixed smembers($key) Get all the members in a set. <https://redis.io/commands/smembers>
|
||||
* @method mixed smove($source, $destination, $member) Move a member from one set to another. <https://redis.io/commands/smove>
|
||||
* @method mixed sort($key, ...$options) Sort the elements in a list, set or sorted set. <https://redis.io/commands/sort>
|
||||
* @method mixed spop($key, $count = null) Remove and return one or multiple random members from a set. <https://redis.io/commands/spop>
|
||||
* @method mixed srandmember($key, $count = null) Get one or multiple random members from a set. <https://redis.io/commands/srandmember>
|
||||
* @method mixed srem($key, ...$members) Remove one or more members from a set. <https://redis.io/commands/srem>
|
||||
* @method mixed strlen($key) Get the length of the value stored in a key. <https://redis.io/commands/strlen>
|
||||
* @method mixed subscribe(...$channels) Listen for messages published to the given channels. <https://redis.io/commands/subscribe>
|
||||
* @method mixed sunion(...$keys) Add multiple sets. <https://redis.io/commands/sunion>
|
||||
* @method mixed sunionstore($destination, ...$keys) Add multiple sets and store the resulting set in a key. <https://redis.io/commands/sunionstore>
|
||||
* @method mixed swapdb($index, $index) Swaps two Redis databases. <https://redis.io/commands/swapdb>
|
||||
* @method mixed sync() Internal command used for replication. <https://redis.io/commands/sync>
|
||||
* @method mixed time() Return the current server time. <https://redis.io/commands/time>
|
||||
* @method mixed touch(...$keys) Alters the last access time of a key(s). Returns the number of existing keys specified.. <https://redis.io/commands/touch>
|
||||
* @method mixed ttl($key) Get the time to live for a key. <https://redis.io/commands/ttl>
|
||||
* @method mixed type($key) Determine the type stored at key. <https://redis.io/commands/type>
|
||||
* @method mixed unsubscribe(...$channels) Stop listening for messages posted to the given channels. <https://redis.io/commands/unsubscribe>
|
||||
* @method mixed unlink(...$keys) Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.. <https://redis.io/commands/unlink>
|
||||
* @method mixed unwatch() Forget about all watched keys. <https://redis.io/commands/unwatch>
|
||||
* @method mixed wait($numslaves, $timeout) Wait for the synchronous replication of all the write commands sent in the context of the current connection. <https://redis.io/commands/wait>
|
||||
* @method mixed watch(...$keys) Watch the given keys to determine execution of the MULTI/EXEC block. <https://redis.io/commands/watch>
|
||||
* @method mixed xack($stream, $group, ...$ids) Removes one or multiple messages from the pending entries list (PEL) of a stream consumer group <https://redis.io/commands/xack>
|
||||
* @method mixed xadd($stream, $id, $field, $value, ...$fieldsValues) Appends the specified stream entry to the stream at the specified key <https://redis.io/commands/xadd>
|
||||
* @method mixed xclaim($stream, $group, $consumer, $minIdleTimeMs, $id, ...$options) Changes the ownership of a pending message, so that the new owner is the consumer specified as the command argument <https://redis.io/commands/xclaim>
|
||||
* @method mixed xdel($stream, ...$ids) Removes the specified entries from a stream, and returns the number of entries deleted <https://redis.io/commands/xdel>
|
||||
* @method mixed xgroup($subCommand, $stream, $group, ...$options) Manages the consumer groups associated with a stream data structure <https://redis.io/commands/xgroup>
|
||||
* @method mixed xinfo($subCommand, $stream, ...$options) Retrieves different information about the streams and associated consumer groups <https://redis.io/commands/xinfo>
|
||||
* @method mixed xlen($stream) Returns the number of entries inside a stream <https://redis.io/commands/xlen>
|
||||
* @method mixed xpending($stream, $group, ...$options) Fetching data from a stream via a consumer group, and not acknowledging such data, has the effect of creating pending entries <https://redis.io/commands/xpending>
|
||||
* @method mixed xrange($stream, $start, $end, ...$options) Returns the stream entries matching a given range of IDs <https://redis.io/commands/xrange>
|
||||
* @method mixed xread(...$options) Read data from one or multiple streams, only returning entries with an ID greater than the last received ID reported by the caller <https://redis.io/commands/xread>
|
||||
* @method mixed xreadgroup($subCommand, $group, $consumer, ...$options) Special version of the XREAD command with support for consumer groups <https://redis.io/commands/xreadgroup>
|
||||
* @method mixed xrevrange($stream, $end, $start, ...$options) Exactly like XRANGE, but with the notable difference of returning the entries in reverse order, and also taking the start-end range in reverse order <https://redis.io/commands/xrevrange>
|
||||
* @method mixed xtrim($stream, $strategy, ...$options) Trims the stream to a given number of items, evicting older items (items with lower IDs) if needed <https://redis.io/commands/xtrim>
|
||||
* @method mixed zadd($key, ...$options) Add one or more members to a sorted set, or update its score if it already exists. <https://redis.io/commands/zadd>
|
||||
* @method mixed zcard($key) Get the number of members in a sorted set. <https://redis.io/commands/zcard>
|
||||
* @method mixed zcount($key, $min, $max) Count the members in a sorted set with scores within the given values. <https://redis.io/commands/zcount>
|
||||
* @method mixed zincrby($key, $increment, $member) Increment the score of a member in a sorted set. <https://redis.io/commands/zincrby>
|
||||
* @method mixed zinterstore($destination, $numkeys, $key, ...$options) Intersect multiple sorted sets and store the resulting sorted set in a new key. <https://redis.io/commands/zinterstore>
|
||||
* @method mixed zlexcount($key, $min, $max) Count the number of members in a sorted set between a given lexicographical range. <https://redis.io/commands/zlexcount>
|
||||
* @method mixed zrange($key, $start, $stop, $WITHSCORES = null) Return a range of members in a sorted set, by index. <https://redis.io/commands/zrange>
|
||||
* @method mixed zrangebylex($key, $min, $max, $LIMIT = null, $offset = null, $count = null) Return a range of members in a sorted set, by lexicographical range. <https://redis.io/commands/zrangebylex>
|
||||
* @method mixed zrevrangebylex($key, $max, $min, $LIMIT = null, $offset = null, $count = null) Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.. <https://redis.io/commands/zrevrangebylex>
|
||||
* @method mixed zrangebyscore($key, $min, $max, ...$options) Return a range of members in a sorted set, by score. <https://redis.io/commands/zrangebyscore>
|
||||
* @method mixed zrank($key, $member) Determine the index of a member in a sorted set. <https://redis.io/commands/zrank>
|
||||
* @method mixed zrem($key, ...$members) Remove one or more members from a sorted set. <https://redis.io/commands/zrem>
|
||||
* @method mixed zremrangebylex($key, $min, $max) Remove all members in a sorted set between the given lexicographical range. <https://redis.io/commands/zremrangebylex>
|
||||
* @method mixed zremrangebyrank($key, $start, $stop) Remove all members in a sorted set within the given indexes. <https://redis.io/commands/zremrangebyrank>
|
||||
* @method mixed zremrangebyscore($key, $min, $max) Remove all members in a sorted set within the given scores. <https://redis.io/commands/zremrangebyscore>
|
||||
* @method mixed zrevrange($key, $start, $stop, $WITHSCORES = null) Return a range of members in a sorted set, by index, with scores ordered from high to low. <https://redis.io/commands/zrevrange>
|
||||
* @method mixed zrevrangebyscore($key, $max, $min, $WITHSCORES = null, $LIMIT = null, $offset = null, $count = null) Return a range of members in a sorted set, by score, with scores ordered from high to low. <https://redis.io/commands/zrevrangebyscore>
|
||||
* @method mixed zrevrank($key, $member) Determine the index of a member in a sorted set, with scores ordered from high to low. <https://redis.io/commands/zrevrank>
|
||||
* @method mixed zscore($key, $member) Get the score associated with the given member in a sorted set. <https://redis.io/commands/zscore>
|
||||
* @method mixed zunionstore($destination, $numkeys, $key, ...$options) Add multiple sorted sets and store the resulting sorted set in a new key. <https://redis.io/commands/zunionstore>
|
||||
* @method mixed scan($cursor, $MATCH = null, $pattern = null, $COUNT = null, $count = null) Incrementally iterate the keys space. <https://redis.io/commands/scan>
|
||||
* @method mixed sscan($key, $cursor, $MATCH = null, $pattern = null, $COUNT = null, $count = null) Incrementally iterate Set elements. <https://redis.io/commands/sscan>
|
||||
* @method mixed hscan($key, $cursor, $MATCH = null, $pattern = null, $COUNT = null, $count = null) Incrementally iterate hash fields and associated values. <https://redis.io/commands/hscan>
|
||||
* @method mixed zscan($key, $cursor, $MATCH = null, $pattern = null, $COUNT = null, $count = null) Incrementally iterate sorted sets elements and associated scores. <https://redis.io/commands/zscan>
|
||||
*/
|
||||
interface ConnectionInterface
|
||||
{
|
||||
public function open(): void;
|
||||
|
||||
public function close(): void;
|
||||
|
||||
public function getIsActive(): bool;
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $params
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeCommand($name, $params = []);
|
||||
}
|
||||
446
vendor/yiisoft/yii2-redis/src/LuaScriptBuilder.php
vendored
Normal file
446
vendor/yiisoft/yii2-redis/src/LuaScriptBuilder.php
vendored
Normal file
@ -0,0 +1,446 @@
|
||||
<?php
|
||||
/**
|
||||
* @link https://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license https://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\redis;
|
||||
|
||||
use yii\base\InvalidParamException;
|
||||
use yii\base\NotSupportedException;
|
||||
use yii\db\Exception;
|
||||
use yii\db\Expression;
|
||||
|
||||
/**
|
||||
* LuaScriptBuilder builds lua scripts used for retrieving data from redis.
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class LuaScriptBuilder extends \yii\base\BaseObject
|
||||
{
|
||||
/**
|
||||
* Builds a Lua script for finding a list of records
|
||||
* @param ActiveQuery $query the query used to build the script
|
||||
* @return string
|
||||
*/
|
||||
public function buildAll($query)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $query->modelClass;
|
||||
$key = $this->quoteValue($modelClass::keyPrefix() . ':a:');
|
||||
|
||||
return $this->build($query, "n=n+1 pks[n]=redis.call('HGETALL',$key .. pk)", 'pks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Lua script for finding one record
|
||||
* @param ActiveQuery $query the query used to build the script
|
||||
* @return string
|
||||
*/
|
||||
public function buildOne($query)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $query->modelClass;
|
||||
$key = $this->quoteValue($modelClass::keyPrefix() . ':a:');
|
||||
|
||||
return $this->build($query, "do return redis.call('HGETALL',$key .. pk) end", 'pks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Lua script for finding a column
|
||||
* @param ActiveQuery $query the query used to build the script
|
||||
* @param string $column name of the column
|
||||
* @return string
|
||||
*/
|
||||
public function buildColumn($query, $column)
|
||||
{
|
||||
// TODO add support for indexBy
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $query->modelClass;
|
||||
$key = $this->quoteValue($modelClass::keyPrefix() . ':a:');
|
||||
|
||||
return $this->build($query, "n=n+1 pks[n]=redis.call('HGET',$key .. pk," . $this->quoteValue($column) . ")", 'pks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Lua script for getting count of records
|
||||
* @param ActiveQuery $query the query used to build the script
|
||||
* @return string
|
||||
*/
|
||||
public function buildCount($query)
|
||||
{
|
||||
return $this->build($query, 'n=n+1', 'n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Lua script for finding the sum of a column
|
||||
* @param ActiveQuery $query the query used to build the script
|
||||
* @param string $column name of the column
|
||||
* @return string
|
||||
*/
|
||||
public function buildSum($query, $column)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $query->modelClass;
|
||||
$key = $this->quoteValue($modelClass::keyPrefix() . ':a:');
|
||||
|
||||
return $this->build($query, "n=n+redis.call('HGET',$key .. pk," . $this->quoteValue($column) . ")", 'n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Lua script for finding the average of a column
|
||||
* @param ActiveQuery $query the query used to build the script
|
||||
* @param string $column name of the column
|
||||
* @return string
|
||||
*/
|
||||
public function buildAverage($query, $column)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $query->modelClass;
|
||||
$key = $this->quoteValue($modelClass::keyPrefix() . ':a:');
|
||||
|
||||
return $this->build($query, "n=n+1 if v==nil then v=0 end v=v+redis.call('HGET',$key .. pk," . $this->quoteValue($column) . ")", 'v/n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Lua script for finding the min value of a column
|
||||
* @param ActiveQuery $query the query used to build the script
|
||||
* @param string $column name of the column
|
||||
* @return string
|
||||
*/
|
||||
public function buildMin($query, $column)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $query->modelClass;
|
||||
$key = $this->quoteValue($modelClass::keyPrefix() . ':a:');
|
||||
|
||||
return $this->build($query, "n=redis.call('HGET',$key .. pk," . $this->quoteValue($column) . ") if v==nil or n<v then v=n end", 'v');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Lua script for finding the max value of a column
|
||||
* @param ActiveQuery $query the query used to build the script
|
||||
* @param string $column name of the column
|
||||
* @return string
|
||||
*/
|
||||
public function buildMax($query, $column)
|
||||
{
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $query->modelClass;
|
||||
$key = $this->quoteValue($modelClass::keyPrefix() . ':a:');
|
||||
|
||||
return $this->build($query, "n=redis.call('HGET',$key .. pk," . $this->quoteValue($column) . ") if v==nil or n>v then v=n end", 'v');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActiveQuery $query the query used to build the script
|
||||
* @param string $buildResult the lua script for building the result
|
||||
* @param string $return the lua variable that should be returned
|
||||
* @throws NotSupportedException when query contains unsupported order by condition
|
||||
* @return string
|
||||
*/
|
||||
private function build($query, $buildResult, $return)
|
||||
{
|
||||
$columns = [];
|
||||
if ($query->where !== null) {
|
||||
$condition = $this->buildCondition($query->where, $columns);
|
||||
} else {
|
||||
$condition = 'true';
|
||||
}
|
||||
|
||||
$start = ($query->offset === null || $query->offset < 0) ? 0 : $query->offset;
|
||||
$limitCondition = 'i>' . $start . (($query->limit === null || $query->limit < 0) ? '' : ' and i<=' . ($start + $query->limit));
|
||||
|
||||
/* @var $modelClass ActiveRecord */
|
||||
$modelClass = $query->modelClass;
|
||||
$key = $this->quoteValue($modelClass::keyPrefix());
|
||||
$loadColumnValues = '';
|
||||
foreach ($columns as $column => $alias) {
|
||||
$loadColumnValues .= "local $alias=redis.call('HGET',$key .. ':a:' .. pk, " . $this->quoteValue($column) . ")\n";
|
||||
}
|
||||
|
||||
$getAllPks = <<<EOF
|
||||
local allpks=redis.call('LRANGE',$key,0,-1)
|
||||
EOF;
|
||||
if (!empty($query->orderBy)) {
|
||||
if (!is_array($query->orderBy) || count($query->orderBy) > 1) {
|
||||
throw new NotSupportedException(
|
||||
'orderBy by multiple columns is not currently supported by redis ActiveRecord.'
|
||||
);
|
||||
}
|
||||
|
||||
$k = key($query->orderBy);
|
||||
$v = $query->orderBy[$k];
|
||||
if (is_numeric($k)) {
|
||||
$orderColumn = $v;
|
||||
$orderType = 'ASC';
|
||||
} else {
|
||||
$orderColumn = $k;
|
||||
$orderType = $v === SORT_DESC ? 'DESC' : 'ASC';
|
||||
}
|
||||
|
||||
$getAllPks = <<<EOF
|
||||
local allpks=redis.pcall('SORT', $key, 'BY', $key .. ':a:*->' .. '$orderColumn', '$orderType')
|
||||
if allpks['err'] then
|
||||
allpks=redis.pcall('SORT', $key, 'BY', $key .. ':a:*->' .. '$orderColumn', '$orderType', 'ALPHA')
|
||||
end
|
||||
EOF;
|
||||
}
|
||||
|
||||
return <<<EOF
|
||||
$getAllPks
|
||||
local pks={}
|
||||
local n=0
|
||||
local v=nil
|
||||
local i=0
|
||||
local key=$key
|
||||
for k,pk in ipairs(allpks) do
|
||||
$loadColumnValues
|
||||
if $condition then
|
||||
i=i+1
|
||||
if $limitCondition then
|
||||
$buildResult
|
||||
end
|
||||
end
|
||||
end
|
||||
return $return
|
||||
EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a column to the list of columns to retrieve and creates an alias
|
||||
* @param string $column the column name to add
|
||||
* @param array $columns list of columns given by reference
|
||||
* @return string the alias generated for the column name
|
||||
*/
|
||||
private function addColumn($column, &$columns)
|
||||
{
|
||||
if (isset($columns[$column])) {
|
||||
return $columns[$column];
|
||||
}
|
||||
$name = 'c' . preg_replace("/[^a-z]+/i", "", $column) . count($columns);
|
||||
|
||||
return $columns[$column] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes a string value for use in a query.
|
||||
* Note that if the parameter is not a string or int, it will be returned without change.
|
||||
* @param string $str string to be quoted
|
||||
* @return string the properly quoted string
|
||||
*/
|
||||
private function quoteValue($str)
|
||||
{
|
||||
if (!is_string($str) && !is_int($str)) {
|
||||
return $str;
|
||||
}
|
||||
|
||||
return "'" . addcslashes($str, "\000\n\r\\\032\047") . "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the condition specification and generates the corresponding Lua expression.
|
||||
* @param string|array $condition the condition specification. Please refer to [[ActiveQuery::where()]]
|
||||
* on how to specify a condition.
|
||||
* @param array $columns the list of columns and aliases to be used
|
||||
* @return string the generated SQL expression
|
||||
* @throws \yii\db\Exception if the condition is in bad format
|
||||
* @throws \yii\base\NotSupportedException if the condition is not an array
|
||||
*/
|
||||
public function buildCondition($condition, &$columns)
|
||||
{
|
||||
static $builders = [
|
||||
'not' => 'buildNotCondition',
|
||||
'and' => 'buildAndCondition',
|
||||
'or' => 'buildAndCondition',
|
||||
'between' => 'buildBetweenCondition',
|
||||
'not between' => 'buildBetweenCondition',
|
||||
'in' => 'buildInCondition',
|
||||
'not in' => 'buildInCondition',
|
||||
'like' => 'buildLikeCondition',
|
||||
'not like' => 'buildLikeCondition',
|
||||
'or like' => 'buildLikeCondition',
|
||||
'or not like' => 'buildLikeCondition',
|
||||
'>' => 'buildCompareCondition',
|
||||
'>=' => 'buildCompareCondition',
|
||||
'<' => 'buildCompareCondition',
|
||||
'<=' => 'buildCompareCondition',
|
||||
];
|
||||
|
||||
if (!is_array($condition)) {
|
||||
throw new NotSupportedException('Where condition must be an array in redis ActiveRecord.');
|
||||
}
|
||||
if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
|
||||
$operator = strtolower($condition[0]);
|
||||
if (isset($builders[$operator])) {
|
||||
$method = $builders[$operator];
|
||||
array_shift($condition);
|
||||
|
||||
return $this->$method($operator, $condition, $columns);
|
||||
}
|
||||
|
||||
throw new Exception('Found unknown operator in query: ' . $operator);
|
||||
}
|
||||
|
||||
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
|
||||
return $this->buildHashCondition($condition, $columns);
|
||||
}
|
||||
|
||||
private function buildHashCondition($condition, &$columns)
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($condition as $column => $value) {
|
||||
if (is_array($value)) { // IN condition
|
||||
$parts[] = $this->buildInCondition('in', [$column, $value], $columns);
|
||||
} else {
|
||||
if (is_bool($value)) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
if ($value === null) {
|
||||
$parts[] = "redis.call('HEXISTS',key .. ':a:' .. pk, ".$this->quoteValue($column).")==0";
|
||||
} elseif ($value instanceof Expression) {
|
||||
$column = $this->addColumn($column, $columns);
|
||||
$parts[] = "$column==" . $value->expression;
|
||||
} else {
|
||||
$column = $this->addColumn($column, $columns);
|
||||
$value = $this->quoteValue($value);
|
||||
$parts[] = "$column==$value";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count($parts) === 1 ? $parts[0] : '(' . implode(') and (', $parts) . ')';
|
||||
}
|
||||
|
||||
private function buildNotCondition($operator, $operands, &$params)
|
||||
{
|
||||
if (count($operands) != 1) {
|
||||
throw new InvalidParamException("Operator '$operator' requires exactly one operand.");
|
||||
}
|
||||
|
||||
$operand = reset($operands);
|
||||
if (is_array($operand)) {
|
||||
$operand = $this->buildCondition($operand, $params);
|
||||
}
|
||||
|
||||
return "$operator ($operand)";
|
||||
}
|
||||
|
||||
private function buildAndCondition($operator, $operands, &$columns)
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($operands as $operand) {
|
||||
if (is_array($operand)) {
|
||||
$operand = $this->buildCondition($operand, $columns);
|
||||
}
|
||||
if ($operand !== '') {
|
||||
$parts[] = $operand;
|
||||
}
|
||||
}
|
||||
if (!empty($parts)) {
|
||||
return '(' . implode(") $operator (", $parts) . ')';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function buildBetweenCondition($operator, $operands, &$columns)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1], $operands[2])) {
|
||||
throw new Exception("Operator '$operator' requires three operands.");
|
||||
}
|
||||
|
||||
list($column, $value1, $value2) = $operands;
|
||||
|
||||
$value1 = $this->quoteValue($value1);
|
||||
$value2 = $this->quoteValue($value2);
|
||||
$column = $this->addColumn($column, $columns);
|
||||
|
||||
$condition = "$column >= $value1 and $column <= $value2";
|
||||
return $operator === 'not between' ? "not ($condition)" : $condition;
|
||||
}
|
||||
|
||||
private function buildInCondition($operator, $operands, &$columns)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1])) {
|
||||
throw new Exception("Operator '$operator' requires two operands.");
|
||||
}
|
||||
|
||||
list($column, $values) = $operands;
|
||||
|
||||
$values = (array) $values;
|
||||
|
||||
if (empty($values) || $column === []) {
|
||||
return $operator === 'in' ? 'false' : 'true';
|
||||
}
|
||||
|
||||
if (is_array($column) && count($column) > 1) {
|
||||
return $this->buildCompositeInCondition($operator, $column, $values, $columns);
|
||||
}
|
||||
|
||||
if (is_array($column)) {
|
||||
$column = reset($column);
|
||||
}
|
||||
$columnAlias = $this->addColumn($column, $columns);
|
||||
$parts = [];
|
||||
foreach ($values as $value) {
|
||||
if (is_array($value)) {
|
||||
$value = isset($value[$column]) ? $value[$column] : null;
|
||||
}
|
||||
if ($value === null) {
|
||||
$parts[] = "redis.call('HEXISTS',key .. ':a:' .. pk, ".$this->quoteValue($column).")==0";
|
||||
} elseif ($value instanceof Expression) {
|
||||
$parts[] = "$columnAlias==" . $value->expression;
|
||||
} else {
|
||||
$value = $this->quoteValue($value);
|
||||
$parts[] = "$columnAlias==$value";
|
||||
}
|
||||
}
|
||||
$operator = $operator === 'in' ? '' : 'not ';
|
||||
|
||||
return "$operator(" . implode(' or ', $parts) . ')';
|
||||
}
|
||||
|
||||
protected function buildCompositeInCondition($operator, $inColumns, $values, &$columns)
|
||||
{
|
||||
$vss = [];
|
||||
foreach ($values as $value) {
|
||||
$vs = [];
|
||||
foreach ($inColumns as $column) {
|
||||
if (isset($value[$column])) {
|
||||
$columnAlias = $this->addColumn($column, $columns);
|
||||
$vs[] = "$columnAlias==" . $this->quoteValue($value[$column]);
|
||||
} else {
|
||||
$vs[] = "redis.call('HEXISTS',key .. ':a:' .. pk, ".$this->quoteValue($column).")==0";
|
||||
}
|
||||
}
|
||||
$vss[] = '(' . implode(' and ', $vs) . ')';
|
||||
}
|
||||
$operator = $operator === 'in' ? '' : 'not ';
|
||||
|
||||
return "$operator(" . implode(' or ', $vss) . ')';
|
||||
}
|
||||
|
||||
protected function buildCompareCondition($operator, $operands, &$columns)
|
||||
{
|
||||
if (!isset($operands[0], $operands[1])) {
|
||||
throw new Exception("Operator '$operator' requires two operands.");
|
||||
}
|
||||
|
||||
list($column, $value) = $operands;
|
||||
|
||||
$column = $this->addColumn($column, $columns);
|
||||
if (is_numeric($value)){
|
||||
return "tonumber($column) $operator $value";
|
||||
}
|
||||
$value = $this->quoteValue($value);
|
||||
return "$column $operator $value";
|
||||
}
|
||||
|
||||
private function buildLikeCondition($operator, $operands, &$columns)
|
||||
{
|
||||
throw new NotSupportedException('LIKE conditions are not suppoerted by redis ActiveRecord.');
|
||||
}
|
||||
}
|
||||
165
vendor/yiisoft/yii2-redis/src/Mutex.php
vendored
Normal file
165
vendor/yiisoft/yii2-redis/src/Mutex.php
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
/**
|
||||
* @link https://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license https://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\redis;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\di\Instance;
|
||||
use yii\mutex\RetryAcquireTrait;
|
||||
|
||||
/**
|
||||
* Redis Mutex implements a mutex component using [redis](https://redis.io/) as the storage medium.
|
||||
*
|
||||
* Redis Mutex requires redis version 2.6.12 or higher to work properly.
|
||||
*
|
||||
* It needs to be configured with a redis [[Connection]] that is also configured as an application component.
|
||||
* By default it will use the `redis` application component.
|
||||
*
|
||||
* To use redis Mutex as the application component, configure the application as follows:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'components' => [
|
||||
* 'mutex' => [
|
||||
* 'class' => 'yii\redis\Mutex',
|
||||
* 'redis' => [
|
||||
* 'hostname' => 'localhost',
|
||||
* 'port' => 6379,
|
||||
* 'database' => 0,
|
||||
* ]
|
||||
* ],
|
||||
* ],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Or if you have configured the redis [[Connection]] as an application component, the following is sufficient:
|
||||
*
|
||||
* ```php
|
||||
* [
|
||||
* 'components' => [
|
||||
* 'mutex' => [
|
||||
* 'class' => 'yii\redis\Mutex',
|
||||
* // 'redis' => 'redis' // id of the connection application component
|
||||
* ],
|
||||
* ],
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @see \yii\mutex\Mutex
|
||||
* @see https://redis.io/topics/distlock
|
||||
*
|
||||
* @author Sergey Makinen <sergey@makinen.ru>
|
||||
* @author Alexander Zhuravlev <axelhex@gmail.com>
|
||||
* @since 2.0.6
|
||||
*/
|
||||
class Mutex extends \yii\mutex\Mutex
|
||||
{
|
||||
use RetryAcquireTrait;
|
||||
|
||||
/**
|
||||
* @var int the number of seconds in which the lock will be auto released.
|
||||
*/
|
||||
public $expire = 30;
|
||||
/**
|
||||
* @var string a string prefixed to every cache key so that it is unique. If not set,
|
||||
* it will use a prefix generated from [[Application::id]]. You may set this property to be an empty string
|
||||
* if you don't want to use key prefix. It is recommended that you explicitly set this property to some
|
||||
* static value if the cached data needs to be shared among multiple applications.
|
||||
*/
|
||||
public $keyPrefix;
|
||||
/**
|
||||
* @var ConnectionInterface|string|array the Redis [[Connection]] object or the application component ID of the Redis [[Connection]].
|
||||
* This can also be an array that is used to create a redis [[Connection]] instance in case you do not want do configure
|
||||
* redis connection as an application component.
|
||||
* After the Mutex object is created, if you want to change this property, you should only assign it
|
||||
* with a Redis [[Connection]] object.
|
||||
*/
|
||||
public $redis = 'redis';
|
||||
|
||||
/**
|
||||
* @var array Redis lock values. Used to be safe that only a lock owner can release it.
|
||||
*/
|
||||
private $_lockValues = [];
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the redis Mutex component.
|
||||
* This method will initialize the [[redis]] property to make sure it refers to a valid redis connection.
|
||||
* @throws InvalidConfigException if [[redis]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->redis = Instance::ensure($this->redis, ConnectionInterface::class);
|
||||
if ($this->keyPrefix === null) {
|
||||
$this->keyPrefix = substr(md5(Yii::$app->id), 0, 5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a lock by name.
|
||||
* @param string $name of the lock to be acquired. Must be unique.
|
||||
* @param int $timeout time (in seconds) to wait for lock to be released. Defaults to `0` meaning that method will return
|
||||
* false immediately in case lock was already acquired.
|
||||
* @return bool lock acquiring result.
|
||||
*/
|
||||
protected function acquireLock($name, $timeout = 0)
|
||||
{
|
||||
$key = $this->calculateKey($name);
|
||||
$value = Yii::$app->security->generateRandomString(20);
|
||||
|
||||
$result = $this->retryAcquire($timeout, function () use ($key, $value) {
|
||||
return $this->redis->executeCommand('SET', [$key, $value, 'NX', 'PX', (int) ($this->expire * 1000)]);
|
||||
});
|
||||
|
||||
if ($result) {
|
||||
$this->_lockValues[$name] = $value;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases acquired lock. This method will return `false` in case the lock was not found or Redis command failed.
|
||||
* @param string $name of the lock to be released. This lock must already exist.
|
||||
* @return bool lock release result: `false` in case named lock was not found or Redis command failed.
|
||||
*/
|
||||
protected function releaseLock($name)
|
||||
{
|
||||
static $releaseLuaScript = <<<LUA
|
||||
if redis.call("GET",KEYS[1])==ARGV[1] then
|
||||
return redis.call("DEL",KEYS[1])
|
||||
else
|
||||
return 0
|
||||
end
|
||||
LUA;
|
||||
if (
|
||||
!isset($this->_lockValues[$name])
|
||||
|| !$this->redis->executeCommand('EVAL', [
|
||||
$releaseLuaScript,
|
||||
1,
|
||||
$this->calculateKey($name),
|
||||
$this->_lockValues[$name],
|
||||
])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($this->_lockValues[$name]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique key used for storing the mutex in Redis.
|
||||
* @param string $name mutex name.
|
||||
* @return string a safe cache key associated with the mutex name.
|
||||
*/
|
||||
protected function calculateKey($name)
|
||||
{
|
||||
return $this->keyPrefix . md5(json_encode([__CLASS__, $name]));
|
||||
}
|
||||
}
|
||||
172
vendor/yiisoft/yii2-redis/src/Session.php
vendored
Normal file
172
vendor/yiisoft/yii2-redis/src/Session.php
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
<?php
|
||||
/**
|
||||
* @link https://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license https://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\redis;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\di\Instance;
|
||||
|
||||
/**
|
||||
* Redis Session implements a session component using [redis](https://redis.io/) as the storage medium.
|
||||
*
|
||||
* Redis Session requires redis version 2.6.12 or higher to work properly.
|
||||
*
|
||||
* It needs to be configured with a redis [[Connection]] that is also configured as an application component.
|
||||
* By default it will use the `redis` application component.
|
||||
*
|
||||
* To use redis Session as the session application component, configure the application as follows,
|
||||
*
|
||||
* ~~~
|
||||
* [
|
||||
* 'components' => [
|
||||
* 'session' => [
|
||||
* 'class' => 'yii\redis\Session',
|
||||
* 'redis' => [
|
||||
* 'hostname' => 'localhost',
|
||||
* 'port' => 6379,
|
||||
* 'database' => 0,
|
||||
* ]
|
||||
* ],
|
||||
* ],
|
||||
* ]
|
||||
* ~~~
|
||||
*
|
||||
* Or if you have configured the redis [[Connection]] as an application component, the following is sufficient:
|
||||
*
|
||||
* ~~~
|
||||
* [
|
||||
* 'components' => [
|
||||
* 'session' => [
|
||||
* 'class' => 'yii\redis\Session',
|
||||
* // 'redis' => 'redis' // id of the connection application component
|
||||
* ],
|
||||
* ],
|
||||
* ]
|
||||
* ~~~
|
||||
*
|
||||
* @property-read bool $useCustomStorage Whether to use custom storage.
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Session extends \yii\web\Session
|
||||
{
|
||||
/**
|
||||
* @var ConnectionInterface|string|array the Redis [[Connection]] object or the application component ID of the Redis [[Connection]].
|
||||
* This can also be an array that is used to create a redis [[Connection]] instance in case you do not want do configure
|
||||
* redis connection as an application component.
|
||||
* After the Session object is created, if you want to change this property, you should only assign it
|
||||
* with a Redis [[Connection]] object.
|
||||
*/
|
||||
public $redis = 'redis';
|
||||
/**
|
||||
* @var string a string prefixed to every cache key so that it is unique. If not set,
|
||||
* it will use a prefix generated from [[Application::id]]. You may set this property to be an empty string
|
||||
* if you don't want to use key prefix. It is recommended that you explicitly set this property to some
|
||||
* static value if the cached data needs to be shared among multiple applications.
|
||||
*/
|
||||
public $keyPrefix;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the redis Session component.
|
||||
* This method will initialize the [[redis]] property to make sure it refers to a valid redis connection.
|
||||
* @throws InvalidConfigException if [[redis]] is invalid.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->redis = Instance::ensure($this->redis, ConnectionInterface::class);
|
||||
if ($this->keyPrefix === null) {
|
||||
$this->keyPrefix = substr(md5(Yii::$app->id), 0, 5);
|
||||
}
|
||||
parent::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether to use custom session storage.
|
||||
* This method overrides the parent implementation and always returns true.
|
||||
* @return bool whether to use custom storage.
|
||||
*/
|
||||
public function getUseCustomStorage()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Session open handler.
|
||||
* @internal Do not call this method directly.
|
||||
* @param string $savePath session save path
|
||||
* @param string $sessionName session name
|
||||
* @return bool whether session is opened successfully
|
||||
*/
|
||||
public function openSession($savePath, $sessionName)
|
||||
{
|
||||
if ($this->getUseStrictMode()) {
|
||||
$id = $this->getId();
|
||||
if (!$this->redis->exists($this->calculateKey($id))) {
|
||||
//This session id does not exist, mark it for forced regeneration
|
||||
$this->_forceRegenerateId = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::openSession($savePath, $sessionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Session read handler.
|
||||
* Do not call this method directly.
|
||||
* @param string $id session ID
|
||||
* @return string the session data
|
||||
*/
|
||||
public function readSession($id)
|
||||
{
|
||||
$data = $this->redis->executeCommand('GET', [$this->calculateKey($id)]);
|
||||
|
||||
return $data === false || $data === null ? '' : $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Session write handler.
|
||||
* Do not call this method directly.
|
||||
* @param string $id session ID
|
||||
* @param string $data session data
|
||||
* @return bool whether session write is successful
|
||||
*/
|
||||
public function writeSession($id, $data)
|
||||
{
|
||||
if ($this->getUseStrictMode() && $id === $this->_forceRegenerateId) {
|
||||
//Ignore write when forceRegenerate is active for this id
|
||||
return true;
|
||||
}
|
||||
|
||||
return (bool) $this->redis->executeCommand('SET', [$this->calculateKey($id), $data, 'EX', $this->getTimeout()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Session destroy handler.
|
||||
* Do not call this method directly.
|
||||
* @param string $id session ID
|
||||
* @return bool whether session is destroyed successfully
|
||||
*/
|
||||
public function destroySession($id)
|
||||
{
|
||||
$this->redis->executeCommand('DEL', [$this->calculateKey($id)]);
|
||||
// @see https://github.com/yiisoft/yii2-redis/issues/82
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique key used for storing session data in cache.
|
||||
* @param string $id session variable name
|
||||
* @return string a safe cache key associated with the session variable name
|
||||
*/
|
||||
protected function calculateKey($id)
|
||||
{
|
||||
return $this->keyPrefix . md5(json_encode([__CLASS__, $id]));
|
||||
}
|
||||
}
|
||||
33
vendor/yiisoft/yii2-redis/src/SocketException.php
vendored
Normal file
33
vendor/yiisoft/yii2-redis/src/SocketException.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* @link https://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license https://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\redis;
|
||||
|
||||
use yii\db\Exception;
|
||||
|
||||
/**
|
||||
* SocketException indicates a socket connection failure in [[Connection]].
|
||||
* @since 2.0.7
|
||||
*/
|
||||
class SocketException extends Exception
|
||||
{
|
||||
public function __construct($message = null, $code = 0, ?\Exception $previous = null)
|
||||
{
|
||||
if (!YII_DEBUG) {
|
||||
$message = preg_replace('~AUTH \S+ \S+~', 'AUTH *** ***', $message);
|
||||
}
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the user-friendly name of this exception
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'Redis Socket Exception';
|
||||
}
|
||||
}
|
||||
48
vendor/yiisoft/yii2-redis/src/predis/Command/CommandDecorator.php
vendored
Normal file
48
vendor/yiisoft/yii2-redis/src/predis/Command/CommandDecorator.php
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace yii\redis\predis\Command;
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\Redis\COMMAND;
|
||||
|
||||
class CommandDecorator extends COMMAND
|
||||
{
|
||||
private $originalCommand;
|
||||
|
||||
public function __construct(CommandInterface $command)
|
||||
{
|
||||
$this->originalCommand = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Yii components expect response without changes
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Calling methods of the original class
|
||||
|
||||
public function __call($method, $args) { return call_user_func_array([$this->originalCommand, $method], $args); }
|
||||
|
||||
public function getId():string { return $this->originalCommand->getId(); }
|
||||
|
||||
public function setArguments(array $arguments): void { $this->originalCommand->setArguments($arguments); }
|
||||
|
||||
public function getArguments(): array { return $this->originalCommand->getArguments(); }
|
||||
|
||||
public function setSlot($slot): void { $this->originalCommand->setSlot($slot); }
|
||||
|
||||
public function getSlot(): ?int { return $this->originalCommand->getSlot(); }
|
||||
|
||||
public function setRawArguments(array $arguments): void { $this->originalCommand->setRawArguments($arguments); }
|
||||
|
||||
public function getArgument($index) { return $this->originalCommand->getArgument($index); }
|
||||
|
||||
public function parseResp3Response($data) { return $this->originalCommand->parseResp3Response($data); }
|
||||
|
||||
public function serializeCommand(): string { return $this->originalCommand->serializeCommand(); }
|
||||
}
|
||||
410
vendor/yiisoft/yii2-redis/src/predis/PredisConnection.php
vendored
Normal file
410
vendor/yiisoft/yii2-redis/src/predis/PredisConnection.php
vendored
Normal file
@ -0,0 +1,410 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace yii\redis\predis;
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Response\ErrorInterface;
|
||||
use Predis\Response\ResponseInterface;
|
||||
use Predis\Response\Status;
|
||||
use Yii;
|
||||
use yii\base\Component;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\helpers\Inflector;
|
||||
use yii\redis\ConnectionInterface;
|
||||
use yii\redis\LuaScriptBuilder;
|
||||
use yii\redis\predis\Command\CommandDecorator;
|
||||
|
||||
/**
|
||||
* Class PredisConnection
|
||||
*
|
||||
* @see https://github.com/predis/predis
|
||||
* ```php
|
||||
* // redis-sentinel
|
||||
* 'redis' = [
|
||||
* 'class' => PredisConnection::class,
|
||||
* 'parameters' => [
|
||||
* 'tcp://127.0.0.1:26379?timeout=0.100',
|
||||
* 'tcp://127.0.0.1:26380?timeout=0.100',
|
||||
* 'tcp://127.0.0.1:26381?timeout=0.100',
|
||||
* ],
|
||||
* 'options' => [
|
||||
* 'replication' => 'sentinel',
|
||||
* 'service' => 'mymaster',
|
||||
* 'parameters' => [
|
||||
* 'password' => 'password',
|
||||
* 'database' => 10,
|
||||
* // @see \Predis\Connection\StreamConnection
|
||||
* 'persistent' => true, // performs the connection asynchronously
|
||||
* 'async_connect' => true, //the connection asynchronously
|
||||
* 'read_write_timeout' => 0.1, // timeout of read / write operations
|
||||
* ],
|
||||
* ],
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* @property-read Client|null $client
|
||||
* @property-read bool $isActive Whether the DB connection is established.
|
||||
* @property-read LuaScriptBuilder $luaScriptBuilder
|
||||
*
|
||||
*/
|
||||
class PredisConnection extends Component implements ConnectionInterface
|
||||
{
|
||||
/**
|
||||
* @event Event an event that is triggered after a DB connection is established
|
||||
*/
|
||||
public const EVENT_AFTER_OPEN = 'afterOpen';
|
||||
|
||||
/**
|
||||
* @var array List of available redis commands.
|
||||
* @see https://redis.io/commands
|
||||
*/
|
||||
public $redisCommands = [
|
||||
'APPEND', // Append a value to a key
|
||||
'AUTH', // Authenticate to the server
|
||||
'BGREWRITEAOF', // Asynchronously rewrite the append-only file
|
||||
'BGSAVE', // Asynchronously save the dataset to disk
|
||||
'BITCOUNT', // Count set bits in a string
|
||||
'BITFIELD', // Perform arbitrary bitfield integer operations on strings
|
||||
'BITOP', // Perform bitwise operations between strings
|
||||
'BITPOS', // Find first bit set or clear in a string
|
||||
'BLPOP', // Remove and get the first element in a list, or block until one is available
|
||||
'BRPOP', // Remove and get the last element in a list, or block until one is available
|
||||
'BRPOPLPUSH', // Pop a value from a list, push it to another list and return it; or block until one is available
|
||||
'CLIENT KILL', // Kill the connection of a client
|
||||
'CLIENT LIST', // Get the list of client connections
|
||||
'CLIENT GETNAME', // Get the current connection name
|
||||
'CLIENT PAUSE', // Stop processing commands from clients for some time
|
||||
'CLIENT REPLY', // Instruct the server whether to reply to commands
|
||||
'CLIENT SETNAME', // Set the current connection name
|
||||
'CLUSTER ADDSLOTS', // Assign new hash slots to receiving node
|
||||
'CLUSTER COUNTKEYSINSLOT', // Return the number of local keys in the specified hash slot
|
||||
'CLUSTER DELSLOTS', // Set hash slots as unbound in receiving node
|
||||
'CLUSTER FAILOVER', // Forces a slave to perform a manual failover of its master.
|
||||
'CLUSTER FORGET', // Remove a node from the nodes table
|
||||
'CLUSTER GETKEYSINSLOT', // Return local key names in the specified hash slot
|
||||
'CLUSTER INFO', // Provides info about Redis Cluster node state
|
||||
'CLUSTER KEYSLOT', // Returns the hash slot of the specified key
|
||||
'CLUSTER MEET', // Force a node cluster to handshake with another node
|
||||
'CLUSTER NODES', // Get Cluster config for the node
|
||||
'CLUSTER REPLICATE', // Reconfigure a node as a slave of the specified master node
|
||||
'CLUSTER RESET', // Reset a Redis Cluster node
|
||||
'CLUSTER SAVECONFIG', // Forces the node to save cluster state on disk
|
||||
'CLUSTER SETSLOT', // Bind a hash slot to a specific node
|
||||
'CLUSTER SLAVES', // List slave nodes of the specified master node
|
||||
'CLUSTER SLOTS', // Get array of Cluster slot to node mappings
|
||||
'COMMAND', // Get array of Redis command details
|
||||
'COMMAND COUNT', // Get total number of Redis commands
|
||||
'COMMAND GETKEYS', // Extract keys given a full Redis command
|
||||
'COMMAND INFO', // Get array of specific Redis command details
|
||||
'CONFIG GET', // Get the value of a configuration parameter
|
||||
'CONFIG REWRITE', // Rewrite the configuration file with the in memory configuration
|
||||
'CONFIG SET', // Set a configuration parameter to the given value
|
||||
'CONFIG RESETSTAT', // Reset the stats returned by INFO
|
||||
'DBSIZE', // Return the number of keys in the selected database
|
||||
'DEBUG OBJECT', // Get debugging information about a key
|
||||
'DEBUG SEGFAULT', // Make the server crash
|
||||
'DECR', // Decrement the integer value of a key by one
|
||||
'DECRBY', // Decrement the integer value of a key by the given number
|
||||
'DEL', // Delete a key
|
||||
'DISCARD', // Discard all commands issued after MULTI
|
||||
'DUMP', // Return a serialized version of the value stored at the specified key.
|
||||
'ECHO', // Echo the given string
|
||||
'EVAL', // Execute a Lua script server side
|
||||
'EVALSHA', // Execute a Lua script server side
|
||||
'EXEC', // Execute all commands issued after MULTI
|
||||
'EXISTS', // Determine if a key exists
|
||||
'EXPIRE', // Set a key's time to live in seconds
|
||||
'EXPIREAT', // Set the expiration for a key as a UNIX timestamp
|
||||
'FLUSHALL', // Remove all keys from all databases
|
||||
'FLUSHDB', // Remove all keys from the current database
|
||||
'GEOADD', // Add one or more geospatial items in the geospatial index represented using a sorted set
|
||||
'GEOHASH', // Returns members of a geospatial index as standard geohash strings
|
||||
'GEOPOS', // Returns longitude and latitude of members of a geospatial index
|
||||
'GEODIST', // Returns the distance between two members of a geospatial index
|
||||
'GEORADIUS', // Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point
|
||||
'GEORADIUSBYMEMBER', // Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member
|
||||
'GET', // Get the value of a key
|
||||
'GETBIT', // Returns the bit value at offset in the string value stored at key
|
||||
'GETRANGE', // Get a substring of the string stored at a key
|
||||
'GETSET', // Set the string value of a key and return its old value
|
||||
'HDEL', // Delete one or more hash fields
|
||||
'HEXISTS', // Determine if a hash field exists
|
||||
'HGET', // Get the value of a hash field
|
||||
'HGETALL', // Get all the fields and values in a hash
|
||||
'HINCRBY', // Increment the integer value of a hash field by the given number
|
||||
'HINCRBYFLOAT', // Increment the float value of a hash field by the given amount
|
||||
'HKEYS', // Get all the fields in a hash
|
||||
'HLEN', // Get the number of fields in a hash
|
||||
'HMGET', // Get the values of all the given hash fields
|
||||
'HMSET', // Set multiple hash fields to multiple values
|
||||
'HSET', // Set the string value of a hash field
|
||||
'HSETNX', // Set the value of a hash field, only if the field does not exist
|
||||
'HSTRLEN', // Get the length of the value of a hash field
|
||||
'HVALS', // Get all the values in a hash
|
||||
'INCR', // Increment the integer value of a key by one
|
||||
'INCRBY', // Increment the integer value of a key by the given amount
|
||||
'INCRBYFLOAT', // Increment the float value of a key by the given amount
|
||||
'INFO', // Get information and statistics about the server
|
||||
'KEYS', // Find all keys matching the given pattern
|
||||
'LASTSAVE', // Get the UNIX time stamp of the last successful save to disk
|
||||
'LINDEX', // Get an element from a list by its index
|
||||
'LINSERT', // Insert an element before or after another element in a list
|
||||
'LLEN', // Get the length of a list
|
||||
'LPOP', // Remove and get the first element in a list
|
||||
'LPUSH', // Prepend one or multiple values to a list
|
||||
'LPUSHX', // Prepend a value to a list, only if the list exists
|
||||
'LRANGE', // Get a range of elements from a list
|
||||
'LREM', // Remove elements from a list
|
||||
'LSET', // Set the value of an element in a list by its index
|
||||
'LTRIM', // Trim a list to the specified range
|
||||
'MGET', // Get the values of all the given keys
|
||||
'MIGRATE', // Atomically transfer a key from a Redis instance to another one.
|
||||
'MONITOR', // Listen for all requests received by the server in real time
|
||||
'MOVE', // Move a key to another database
|
||||
'MSET', // Set multiple keys to multiple values
|
||||
'MSETNX', // Set multiple keys to multiple values, only if none of the keys exist
|
||||
'MULTI', // Mark the start of a transaction block
|
||||
'OBJECT', // Inspect the internals of Redis objects
|
||||
'PERSIST', // Remove the expiration from a key
|
||||
'PEXPIRE', // Set a key's time to live in milliseconds
|
||||
'PEXPIREAT', // Set the expiration for a key as a UNIX timestamp specified in milliseconds
|
||||
'PFADD', // Adds the specified elements to the specified HyperLogLog.
|
||||
'PFCOUNT', // Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s).
|
||||
'PFMERGE', // Merge N different HyperLogLogs into a single one.
|
||||
'PING', // Ping the server
|
||||
'PSETEX', // Set the value and expiration in milliseconds of a key
|
||||
'PSUBSCRIBE', // Listen for messages published to channels matching the given patterns
|
||||
'PUBSUB', // Inspect the state of the Pub/Sub subsystem
|
||||
'PTTL', // Get the time to live for a key in milliseconds
|
||||
'PUBLISH', // Post a message to a channel
|
||||
'PUNSUBSCRIBE', // Stop listening for messages posted to channels matching the given patterns
|
||||
'QUIT', // Close the connection
|
||||
'RANDOMKEY', // Return a random key from the keyspace
|
||||
'READONLY', // Enables read queries for a connection to a cluster slave node
|
||||
'READWRITE', // Disables read queries for a connection to a cluster slave node
|
||||
'RENAME', // Rename a key
|
||||
'RENAMENX', // Rename a key, only if the new key does not exist
|
||||
'RESTORE', // Create a key using the provided serialized value, previously obtained using DUMP.
|
||||
'ROLE', // Return the role of the instance in the context of replication
|
||||
'RPOP', // Remove and get the last element in a list
|
||||
'RPOPLPUSH', // Remove the last element in a list, prepend it to another list and return it
|
||||
'RPUSH', // Append one or multiple values to a list
|
||||
'RPUSHX', // Append a value to a list, only if the list exists
|
||||
'SADD', // Add one or more members to a set
|
||||
'SAVE', // Synchronously save the dataset to disk
|
||||
'SCARD', // Get the number of members in a set
|
||||
'SCRIPT DEBUG', // Set the debug mode for executed scripts.
|
||||
'SCRIPT EXISTS', // Check existence of scripts in the script cache.
|
||||
'SCRIPT FLUSH', // Remove all the scripts from the script cache.
|
||||
'SCRIPT KILL', // Kill the script currently in execution.
|
||||
'SCRIPT LOAD', // Load the specified Lua script into the script cache.
|
||||
'SDIFF', // Subtract multiple sets
|
||||
'SDIFFSTORE', // Subtract multiple sets and store the resulting set in a key
|
||||
'SELECT', // Change the selected database for the current connection
|
||||
'SET', // Set the string value of a key
|
||||
'SETBIT', // Sets or clears the bit at offset in the string value stored at key
|
||||
'SETEX', // Set the value and expiration of a key
|
||||
'SETNX', // Set the value of a key, only if the key does not exist
|
||||
'SETRANGE', // Overwrite part of a string at key starting at the specified offset
|
||||
'SHUTDOWN', // Synchronously save the dataset to disk and then shut down the server
|
||||
'SINTER', // Intersect multiple sets
|
||||
'SINTERSTORE', // Intersect multiple sets and store the resulting set in a key
|
||||
'SISMEMBER', // Determine if a given value is a member of a set
|
||||
'SLAVEOF', // Make the server a slave of another instance, or promote it as master
|
||||
'SLOWLOG', // Manages the Redis slow queries log
|
||||
'SMEMBERS', // Get all the members in a set
|
||||
'SMOVE', // Move a member from one set to another
|
||||
'SORT', // Sort the elements in a list, set or sorted set
|
||||
'SPOP', // Remove and return one or multiple random members from a set
|
||||
'SRANDMEMBER', // Get one or multiple random members from a set
|
||||
'SREM', // Remove one or more members from a set
|
||||
'STRLEN', // Get the length of the value stored in a key
|
||||
'SUBSCRIBE', // Listen for messages published to the given channels
|
||||
'SUNION', // Add multiple sets
|
||||
'SUNIONSTORE', // Add multiple sets and store the resulting set in a key
|
||||
'SWAPDB', // Swaps two Redis databases
|
||||
'SYNC', // Internal command used for replication
|
||||
'TIME', // Return the current server time
|
||||
'TOUCH', // Alters the last access time of a key(s). Returns the number of existing keys specified.
|
||||
'TTL', // Get the time to live for a key
|
||||
'TYPE', // Determine the type stored at key
|
||||
'UNSUBSCRIBE', // Stop listening for messages posted to the given channels
|
||||
'UNLINK', // Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.
|
||||
'UNWATCH', // Forget about all watched keys
|
||||
'WAIT', // Wait for the synchronous replication of all the write commands sent in the context of the current connection
|
||||
'WATCH', // Watch the given keys to determine execution of the MULTI/EXEC block
|
||||
'XACK', // Removes one or multiple messages from the pending entries list (PEL) of a stream consumer group
|
||||
'XADD', // Appends the specified stream entry to the stream at the specified key
|
||||
'XCLAIM', // Changes the ownership of a pending message, so that the new owner is the consumer specified as the command argument
|
||||
'XDEL', // Removes the specified entries from a stream, and returns the number of entries deleted
|
||||
'XGROUP', // Manages the consumer groups associated with a stream data structure
|
||||
'XINFO', // Retrieves different information about the streams and associated consumer groups
|
||||
'XLEN', // Returns the number of entries inside a stream
|
||||
'XPENDING', // Fetching data from a stream via a consumer group, and not acknowledging such data, has the effect of creating pending entries
|
||||
'XRANGE', // Returns the stream entries matching a given range of IDs
|
||||
'XREAD', // Read data from one or multiple streams, only returning entries with an ID greater than the last received ID reported by the caller
|
||||
'XREADGROUP', // Special version of the XREAD command with support for consumer groups
|
||||
'XREVRANGE', // Exactly like XRANGE, but with the notable difference of returning the entries in reverse order, and also taking the start-end range in reverse order
|
||||
'XTRIM', // Trims the stream to a given number of items, evicting older items (items with lower IDs) if needed
|
||||
'ZADD', // Add one or more members to a sorted set, or update its score if it already exists
|
||||
'ZCARD', // Get the number of members in a sorted set
|
||||
'ZCOUNT', // Count the members in a sorted set with scores within the given values
|
||||
'ZINCRBY', // Increment the score of a member in a sorted set
|
||||
'ZINTERSTORE', // Intersect multiple sorted sets and store the resulting sorted set in a new key
|
||||
'ZLEXCOUNT', // Count the number of members in a sorted set between a given lexicographical range
|
||||
'ZRANGE', // Return a range of members in a sorted set, by index
|
||||
'ZRANGEBYLEX', // Return a range of members in a sorted set, by lexicographical range
|
||||
'ZREVRANGEBYLEX', // Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.
|
||||
'ZRANGEBYSCORE', // Return a range of members in a sorted set, by score
|
||||
'ZRANK', // Determine the index of a member in a sorted set
|
||||
'ZREM', // Remove one or more members from a sorted set
|
||||
'ZREMRANGEBYLEX', // Remove all members in a sorted set between the given lexicographical range
|
||||
'ZREMRANGEBYRANK', // Remove all members in a sorted set within the given indexes
|
||||
'ZREMRANGEBYSCORE', // Remove all members in a sorted set within the given scores
|
||||
'ZREVRANGE', // Return a range of members in a sorted set, by index, with scores ordered from high to low
|
||||
'ZREVRANGEBYSCORE', // Return a range of members in a sorted set, by score, with scores ordered from high to low
|
||||
'ZREVRANK', // Determine the index of a member in a sorted set, with scores ordered from high to low
|
||||
'ZSCORE', // Get the score associated with the given member in a sorted set
|
||||
'ZUNIONSTORE', // Add multiple sorted sets and store the resulting sorted set in a new key
|
||||
'SCAN', // Incrementally iterate the keys space
|
||||
'SSCAN', // Incrementally iterate Set elements
|
||||
'HSCAN', // Incrementally iterate hash fields and associated values
|
||||
'ZSCAN', // Incrementally iterate sorted sets elements and associated scores
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* @return LuaScriptBuilder
|
||||
*/
|
||||
public function getLuaScriptBuilder(): LuaScriptBuilder
|
||||
{
|
||||
return new LuaScriptBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the DB connection.
|
||||
* This method is invoked right after the DB connection is established.
|
||||
* The default implementation triggers an [[EVENT_AFTER_OPEN]] event.
|
||||
*/
|
||||
protected function initConnection(): void
|
||||
{
|
||||
$this->trigger(self::EVENT_AFTER_OPEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var mixed Connection parameters for one or more servers.
|
||||
*/
|
||||
public $parameters;
|
||||
|
||||
/**
|
||||
* @var mixed Options to configure some behaviours of the client.
|
||||
*/
|
||||
public $options = [];
|
||||
|
||||
/**
|
||||
* @var Client|null redis connection
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Returns a value indicating whether the DB connection is established.
|
||||
*
|
||||
* @return bool whether the DB connection is established
|
||||
*/
|
||||
public function getIsActive(): bool
|
||||
{
|
||||
if ($this->client === null) {
|
||||
return false;
|
||||
}
|
||||
return $this->client->isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|ErrorInterface|ResponseInterface
|
||||
* @throws InvalidConfigException
|
||||
*/
|
||||
public function executeCommand($name, $params = [])
|
||||
{
|
||||
$this->open();
|
||||
|
||||
Yii::debug("Executing Redis Command: $name " . implode(' ', $params), __METHOD__);
|
||||
|
||||
$command = $this->client->createCommand($name, $params);
|
||||
$response = $this->client->executeCommand(new CommandDecorator($command));
|
||||
if ($response instanceof Status) {
|
||||
// ResponseStatus yii expect as bool
|
||||
return (string)$response === 'OK' || (string)$response === 'PONG';
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a DB connection.
|
||||
*
|
||||
* @return void
|
||||
* @throws InvalidConfigException
|
||||
*/
|
||||
public function open(): void
|
||||
{
|
||||
if (null !== $this->client) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->parameters)) {
|
||||
throw new InvalidConfigException('Connection::parameters cannot be empty');
|
||||
}
|
||||
|
||||
Yii::debug('Opening redis DB connection', __METHOD__);
|
||||
|
||||
$this->client = new Client($this->parameters, $this->options);
|
||||
$this->initConnection();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Closes the currently active DB connection.
|
||||
* It does nothing if the connection is already closed.
|
||||
*/
|
||||
public function close(): void
|
||||
{
|
||||
if ($this->client === null) {
|
||||
return;
|
||||
}
|
||||
$this->client->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get predis Client
|
||||
*
|
||||
* @return Client|null
|
||||
* @throws InvalidConfigException
|
||||
*/
|
||||
public function getClient(): ?Client
|
||||
{
|
||||
$this->open();
|
||||
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows issuing all supported commands via magic methods.
|
||||
* ```php
|
||||
* $redis->hmset('test_collection', 'key1', 'val1', 'key2', 'val2')
|
||||
* ```
|
||||
*
|
||||
* @param string $name name of the missing method to execute
|
||||
* @param array $params method call arguments
|
||||
* @return mixed
|
||||
* @throws InvalidConfigException
|
||||
*/
|
||||
public function __call($name, $params)
|
||||
{
|
||||
$redisCommand = strtoupper(Inflector::camel2words($name, false));
|
||||
if (in_array($redisCommand, $this->redisCommands, true)) {
|
||||
return $this->executeCommand($redisCommand, $params);
|
||||
}
|
||||
|
||||
return parent::__call($name, $params);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user