mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-25 18:19:22 +03:00
Merge branch 'upstream/master' into dev/sync-nextcloud-addressbook
This commit is contained in:
commit
957654c746
1528 changed files with 85567 additions and 32765 deletions
|
|
@ -1,3 +1,5 @@
|
|||
Also see https://github.com/the-djmaze/snappymail/tree/master/plugins/example
|
||||
|
||||
PHP
|
||||
```php
|
||||
class Plugin extends \RainLoop\Plugins\AbstractPlugin
|
||||
|
|
@ -130,8 +132,9 @@ $Plugin->addHook('hook.name', 'functionName');
|
|||
### login.credentials
|
||||
params:
|
||||
string &$sEmail
|
||||
string &$sLogin
|
||||
string &$sImapUser
|
||||
string &$sPassword
|
||||
string &$sSmtpUser
|
||||
|
||||
### login.success
|
||||
params:
|
||||
|
|
@ -164,6 +167,12 @@ $Plugin->addHook('hook.name', 'functionName');
|
|||
bool $bSuccess
|
||||
\MailSo\Imap\Settings $oSettings
|
||||
|
||||
### imap.message-headers
|
||||
params:
|
||||
array &$aHeaders
|
||||
|
||||
Allows you to fetch more MIME headers for messages.
|
||||
|
||||
## Sieve
|
||||
|
||||
### sieve.before-connect
|
||||
|
|
@ -285,6 +294,13 @@ and called in JavaScript using rl.pluginRemoteRequest().
|
|||
params:
|
||||
array &$aPaths
|
||||
|
||||
### filter.language
|
||||
params:
|
||||
string &$sLanguage
|
||||
bool $bAdmin
|
||||
|
||||
Allows you to set a different language
|
||||
|
||||
### filter.message-html
|
||||
params:
|
||||
\RainLoop\Model\Account $oAccount
|
||||
|
|
@ -396,35 +412,54 @@ and called in JavaScript using rl.pluginRemoteRequest().
|
|||
string $sName
|
||||
mixed &$mResult
|
||||
|
||||
### service.app-delay-start-begin
|
||||
no params
|
||||
|
||||
### service.app-delay-start-end
|
||||
no params
|
||||
|
||||
# JavaScript Events
|
||||
|
||||
## mailbox
|
||||
### mailbox.inbox-unread-count
|
||||
### mailbox.message-list.selector.go-up
|
||||
### mailbox.message-list.selector.go-down
|
||||
|
||||
### mailbox.message.show
|
||||
Use to show a specific message.
|
||||
``` JavaScript
|
||||
dispatchEvent(
|
||||
new CustomEvent(
|
||||
'mailbox.message.show',
|
||||
{
|
||||
detail: {
|
||||
folder: 'INBOX',
|
||||
uid: 1
|
||||
},
|
||||
cancelable: false
|
||||
}
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## audio
|
||||
### audio.start
|
||||
### audio.stop
|
||||
### audio.api.stop
|
||||
## Misc
|
||||
### idle
|
||||
### rl-layout
|
||||
event.detail value is one of:
|
||||
0. NoPreview
|
||||
1. SidePreview
|
||||
2. BottomPreview
|
||||
|
||||
### rl-view-model.create
|
||||
event.detail = the ViewModel class
|
||||
Happens immediately after the ViewModel constructor
|
||||
Happens immediately after the ViewModel constructor.
|
||||
See accessible properties as https://github.com/the-djmaze/snappymail/blob/master/dev/Knoin/AbstractViews.js
|
||||
|
||||
### rl-view-model
|
||||
event.detail = the ViewModel class
|
||||
Happens after the full build (vm.onBuild()) and contains viewModelDom
|
||||
|
||||
### rl-vm-visible
|
||||
event.detail = the ViewModel class
|
||||
Happens after the model is made visible (vm.afterShow())
|
||||
|
||||
### sm-admin-login
|
||||
event.detail = FormData
|
||||
cancelable using preventDefault()
|
||||
|
|
|
|||
13
plugins/attachments-force-open/extension.js
Normal file
13
plugins/attachments-force-open/extension.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
(() => {
|
||||
|
||||
const dom = document.getElementById('MailMessageView').content;
|
||||
|
||||
dom.querySelector('.attachmentsControls').dataset.bind = '';
|
||||
|
||||
let ds = dom.querySelector('.attachmentsPlace').dataset;
|
||||
ds.bind = ds.bind.replace('showAttachmentControls', 'true');
|
||||
|
||||
ds = dom.querySelector('.controls-handle').dataset;
|
||||
ds.bind = ds.bind.replace('allowAttachmentControls', 'false');
|
||||
|
||||
})();
|
||||
20
plugins/attachments-force-open/index.php
Normal file
20
plugins/attachments-force-open/index.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
class AttachmentsForceOpenPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||
{
|
||||
const
|
||||
NAME = 'Attachments force open',
|
||||
AUTHOR = 'SnappyMail',
|
||||
URL = 'https://github.com/the-djmaze/snappymail/pull/1489',
|
||||
VERSION = '0.1',
|
||||
RELEASE = '2024-03-15',
|
||||
REQUIRED = '2.14.0',
|
||||
CATEGORY = 'General',
|
||||
LICENSE = 'MIT',
|
||||
DESCRIPTION = '';
|
||||
|
||||
public function Init() : void
|
||||
{
|
||||
$this->addJs('extension.js'); // add js file
|
||||
}
|
||||
}
|
||||
|
|
@ -39,11 +39,15 @@
|
|||
avatars = new Map,
|
||||
ncAvatars = new Map,
|
||||
templateId = 'MailMessageView',
|
||||
getAvatarUid = msg => {
|
||||
let from = msg.from[0],
|
||||
bimi = 'pass' == from.dkimStatus ? 1 : 0;
|
||||
return `${bimi}/${from.email.toLowerCase()}`;
|
||||
getBimiSelector = msg => {
|
||||
// Get 's' value out of 'v=BIMI1; s=foo;'
|
||||
let bimiSelector = msg.headers().valueByName('BIMI-Selector');
|
||||
bimiSelector = bimiSelector ? bimiSelector.match(/;.*s=([^\s;]+)/)[1] : '';
|
||||
return bimiSelector || '';
|
||||
},
|
||||
getBimiId = msg => ('pass' == msg.from[0].dkimStatus ? 1 : 0) + '-' + getBimiSelector(msg),
|
||||
getAvatarUrl = msg => `?Avatar/${getBimiId(msg)}/${msg.avatar}`,
|
||||
getAvatarUid = msg => `${getBimiId(msg)}/${msg.from[0].email.toLowerCase()}`,
|
||||
getAvatar = msg => ncAvatars.get(msg.from[0].email.toLowerCase()) || avatars.get(getAvatarUid(msg)),
|
||||
hash = async txt => {
|
||||
if (/^[0-9a-f]{15,}$/i.test(txt)) {
|
||||
|
|
@ -72,6 +76,8 @@
|
|||
if (rl.pluginSettingsGet('avatars', 'delay')) {
|
||||
queue.push([msg, fn]);
|
||||
runQueue();
|
||||
} else if (msg.avatar) {
|
||||
fn(getAvatarUrl(msg));
|
||||
}
|
||||
},
|
||||
runQueue = (() => {
|
||||
|
|
@ -84,6 +90,8 @@
|
|||
item[1](url);
|
||||
item = queue.shift();
|
||||
continue;
|
||||
} else if (item[0].avatar) {
|
||||
item[1](getAvatarUrl(item[0]));
|
||||
} else if (!avatars.has(uid)) {
|
||||
let from = item[0].from[0];
|
||||
rl.pluginRemoteRequest((iError, data) => {
|
||||
|
|
@ -97,9 +105,9 @@
|
|||
runQueue();
|
||||
}, 'Avatar', {
|
||||
bimi: 'pass' == from.dkimStatus ? 1 : 0,
|
||||
bimiSelector: getBimiSelector(item[0]),
|
||||
email: from.email
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
runQueue();
|
||||
|
|
@ -107,75 +115,81 @@
|
|||
}
|
||||
}).debounce(1000);
|
||||
|
||||
/**
|
||||
* Loads images from Nextcloud contacts
|
||||
*/
|
||||
addEventListener('DOMContentLoaded', () => {
|
||||
// addEventListener('DOMContentLoaded', () => {
|
||||
/**
|
||||
* Modify templates
|
||||
*/
|
||||
getEl('MailMessageList').content.querySelectorAll('.messageCheckbox')
|
||||
.forEach(el => el.append(Element.fromHTML(`<img class="fromPic" data-bind="fromPic:$data" loading="lazy">`)));
|
||||
const messageItemHeader = getEl(templateId).content.querySelector('.messageItemHeader');
|
||||
if (messageItemHeader) {
|
||||
messageItemHeader.prepend(Element.fromHTML(
|
||||
`<img class="fromPic" data-bind="visible: viewUserPicVisible, attr: {'src': viewUserPic() }" loading="lazy">`
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads images from Nextcloud contacts
|
||||
*/
|
||||
// rl.pluginSettingsGet('avatars', 'nextcloud');
|
||||
if (parent.OC) {
|
||||
const OC = () => parent.OC,
|
||||
if (parent.OC?.requestToken) {
|
||||
const OC = parent.OC,
|
||||
nsDAV = 'DAV:',
|
||||
nsNC = 'http://nextcloud.com/ns',
|
||||
nsCard = 'urn:ietf:params:xml:ns:carddav',
|
||||
getElementsByTagName = (parent, namespace, localName) => parent.getElementsByTagNameNS(namespace, localName),
|
||||
getElementValue = (parent, namespace, localName) =>
|
||||
getElementsByTagName(parent, namespace, localName)?.item(0)?.textContent,
|
||||
generateUrl = path => OC().webroot + '/remote.php' + path;
|
||||
if (OC().requestToken) {
|
||||
fetch(generateUrl(`/dav/addressbooks/users/${OC().currentUser}/contacts/`), {
|
||||
mode: 'same-origin',
|
||||
cache: 'no-cache',
|
||||
redirect: 'error',
|
||||
credentials: 'same-origin',
|
||||
method: 'REPORT',
|
||||
headers: {
|
||||
requesttoken: OC().requestToken,
|
||||
'Content-Type': 'application/xml; charset=utf-8',
|
||||
Depth: 1
|
||||
},
|
||||
body: '<x4:addressbook-query xmlns:x4="urn:ietf:params:xml:ns:carddav"><x0:prop xmlns:x0="DAV:"><x4:address-data><x4:prop name="EMAIL"/></x4:address-data><x3:has-photo xmlns:x3="http://nextcloud.com/ns"/></x0:prop></x4:addressbook-query>'
|
||||
})
|
||||
.then(response => (response.status < 400) ? response.text() : Promise.reject(new Error({ response })))
|
||||
.then(text => {
|
||||
const
|
||||
xmlParser = new DOMParser(),
|
||||
responseList = getElementsByTagName(
|
||||
xmlParser.parseFromString(text, 'application/xml').documentElement,
|
||||
nsDAV,
|
||||
'response');
|
||||
for (let i = 0; i < responseList.length; ++i) {
|
||||
const item = responseList.item(i);
|
||||
if (1 == getElementValue(item, nsNC, 'has-photo')) {
|
||||
[...getElementValue(item, nsCard, 'address-data').matchAll(/EMAIL.*?:([^@\r\n]+@[^@\r\n]+)/g)]
|
||||
.forEach(match => {
|
||||
ncAvatars.set(
|
||||
match[1].toLowerCase(),
|
||||
getElementValue(item, nsDAV, 'href') + '?photo'
|
||||
);
|
||||
});
|
||||
}
|
||||
getElementsByTagName(parent, namespace, localName)?.item(0)?.textContent;
|
||||
fetch(`${OC.webroot}/remote.php/dav/addressbooks/users/${OC.currentUser}/contacts/`, {
|
||||
mode: 'same-origin',
|
||||
cache: 'no-cache',
|
||||
redirect: 'error',
|
||||
credentials: 'same-origin',
|
||||
method: 'REPORT',
|
||||
headers: {
|
||||
requesttoken: OC.requestToken,
|
||||
'Content-Type': 'application/xml; charset=utf-8',
|
||||
Depth: 1
|
||||
},
|
||||
body: '<x4:addressbook-query xmlns:x4="urn:ietf:params:xml:ns:carddav"><x0:prop xmlns:x0="DAV:"><x4:address-data><x4:prop name="EMAIL"/></x4:address-data><x3:has-photo xmlns:x3="http://nextcloud.com/ns"/></x0:prop></x4:addressbook-query>'
|
||||
})
|
||||
.then(response => (response.status < 400) ? response.text() : Promise.reject(new Error({ response })))
|
||||
.then(text => {
|
||||
const
|
||||
xmlParser = new DOMParser(),
|
||||
responseList = getElementsByTagName(
|
||||
xmlParser.parseFromString(text, 'application/xml').documentElement,
|
||||
nsDAV,
|
||||
'response');
|
||||
for (let i = 0; i < responseList.length; ++i) {
|
||||
const item = responseList.item(i);
|
||||
if (1 == getElementValue(item, nsNC, 'has-photo')) {
|
||||
[...getElementValue(item, nsCard, 'address-data').matchAll(/EMAIL.*?:([^@\r\n]+@[^@\r\n]+)/g)]
|
||||
.forEach(match => {
|
||||
ncAvatars.set(
|
||||
match[1].toLowerCase(),
|
||||
getElementValue(item, nsDAV, 'href') + '?photo'
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// });
|
||||
|
||||
/**
|
||||
* Used by MailMessageList
|
||||
*/
|
||||
ko.bindingHandlers.fromPic = {
|
||||
init: (element, self, dummy, msg) => {
|
||||
try {
|
||||
if (msg?.from?.[0]) {
|
||||
let url = getAvatar(msg),
|
||||
from = msg.from[0],
|
||||
fn = url=>{element.src = url};
|
||||
if (url) {
|
||||
fn(url);
|
||||
} else if (msg.avatar) {
|
||||
if (msg.avatar?.startsWith('data:')) {
|
||||
fn(msg.avatar);
|
||||
} else {
|
||||
element.onerror = () => setIdenticon(from, fn);
|
||||
fn(`?Avatar/${'pass' == from.dkimStatus ? 1 : 0}/${msg.avatar}`);
|
||||
}
|
||||
} else if (msg.avatar?.startsWith('data:')) {
|
||||
fn(msg.avatar);
|
||||
} else {
|
||||
addQueue(msg, fn);
|
||||
}
|
||||
|
|
@ -188,17 +202,6 @@
|
|||
|
||||
addEventListener('rl-view-model.create', e => {
|
||||
if (templateId === e.detail.viewModelTemplateID) {
|
||||
|
||||
const
|
||||
template = getEl(templateId),
|
||||
messageItemHeader = template.content.querySelector('.messageItemHeader');
|
||||
|
||||
if (messageItemHeader) {
|
||||
messageItemHeader.prepend(Element.fromHTML(
|
||||
`<img class="fromPic" data-bind="visible: viewUserPicVisible, attr: {'src': viewUserPic() }" loading="lazy">`
|
||||
));
|
||||
}
|
||||
|
||||
let view = e.detail;
|
||||
view.viewUserPic = ko.observable('');
|
||||
view.viewUserPicVisible = ko.observable(false);
|
||||
|
|
@ -214,8 +217,7 @@
|
|||
if (url) {
|
||||
fn(url);
|
||||
} else if (msg.avatar) {
|
||||
fn(msg.avatar.startsWith('data:') ? msg.avatar
|
||||
: `?Avatar/${'pass' == msg.from[0].dkimStatus ? 1 : 0}/${msg.avatar}`);
|
||||
fn(msg.avatar.startsWith('data:') ? msg.avatar : getAvatarUrl(msg));
|
||||
} else {
|
||||
// let from = msg.from[0];
|
||||
// view.viewUserPic(`?Avatar/${'pass' == from.dkimStatus ? 1 : 0}/${encodeURIComponent(from.email)}`);
|
||||
|
|
@ -225,11 +227,6 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ('MailMessageList' === e.detail.viewModelTemplateID) {
|
||||
getEl('MailMessageList').content.querySelectorAll('.messageCheckbox')
|
||||
.forEach(el => el.append(Element.fromHTML(`<img class="fromPic" data-bind="fromPic:$data" loading="lazy">`)));
|
||||
}
|
||||
});
|
||||
|
||||
})(window.rl);
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
|
|
@ -10,12 +10,12 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
NAME = 'Avatars',
|
||||
AUTHOR = 'SnappyMail',
|
||||
URL = 'https://snappymail.eu/',
|
||||
VERSION = '1.11',
|
||||
RELEASE = '2023-02-23',
|
||||
REQUIRED = '2.25.0',
|
||||
VERSION = '1.19',
|
||||
RELEASE = '2024-07-08',
|
||||
REQUIRED = '2.33.0',
|
||||
CATEGORY = 'Contacts',
|
||||
LICENSE = 'MIT',
|
||||
DESCRIPTION = 'Show graphic of sender in message and messages list (supports BIMI, Gravatar and identicon, Contacts is still TODO)';
|
||||
DESCRIPTION = 'Show graphic of sender in message and messages list (supports BIMI, Gravatar, favicon and identicon, Contacts is still TODO)';
|
||||
|
||||
public function Init() : void
|
||||
{
|
||||
|
|
@ -28,16 +28,31 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$this->addJs("{$identicon}.js");
|
||||
}
|
||||
// https://github.com/the-djmaze/snappymail/issues/714
|
||||
if ($this->Config()->Get('plugin', 'service', true) || !$this->Config()->Get('plugin', 'delay', true)) {
|
||||
if ($this->Config()->Get('plugin', 'service', true)
|
||||
// || !$this->Config()->Get('plugin', 'delay', true)
|
||||
|| $this->Config()->Get('plugin', 'gravatar', false)
|
||||
|| $this->Config()->Get('plugin', 'bimi', false)
|
||||
|| $this->Config()->Get('plugin', 'favicon', false)
|
||||
) {
|
||||
$this->addHook('json.after-message', 'JsonMessage');
|
||||
$this->addHook('json.after-messagelist', 'JsonMessageList');
|
||||
}
|
||||
// https://www.ietf.org/archive/id/draft-brand-indicators-for-message-identification-04.html#bimi-selector
|
||||
if ($this->Config()->Get('plugin', 'bimi', false)) {
|
||||
$this->addHook('imap.message-headers', 'ImapMessageHeaders');
|
||||
}
|
||||
}
|
||||
|
||||
public function ImapMessageHeaders(array &$aHeaders)
|
||||
{
|
||||
// \MailSo\Mime\Enumerations\Header::BIMI_SELECTOR
|
||||
$aHeaders[] = 'BIMI-Selector';
|
||||
}
|
||||
|
||||
public function JsonMessage(array &$aResponse)
|
||||
{
|
||||
if ($icon = $this->JsonAvatar($aResponse['Result'])) {
|
||||
$aResponse['Result']['Avatar'] = $icon;
|
||||
$aResponse['Result']['avatar'] = $icon;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +61,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
if (!empty($aResponse['Result']['@Collection'])) {
|
||||
foreach ($aResponse['Result']['@Collection'] as &$message) {
|
||||
if ($icon = $this->JsonAvatar($message)) {
|
||||
$message['Avatar'] = $icon;
|
||||
$message['avatar'] = $icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,14 +74,10 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$mFrom = $mFrom->jsonSerialize();
|
||||
}
|
||||
if (\is_array($mFrom)) {
|
||||
if ('pass' == $mFrom['dkimStatus'] && $this->Config()->Get('plugin', 'service', true)) {
|
||||
// 'data:image/png;base64,[a-zA-Z0-9+/=]'
|
||||
return static::getServiceIcon($mFrom['email']);
|
||||
}
|
||||
if (!$this->Config()->Get('plugin', 'delay', true)
|
||||
&& ($this->Config()->Get('plugin', 'gravatar', false)
|
||||
if (/*!$this->Config()->Get('plugin', 'delay', true)
|
||||
&& */($this->Config()->Get('plugin', 'gravatar', false)
|
||||
|| ($this->Config()->Get('plugin', 'bimi', false) && 'pass' == $mFrom['dkimStatus'])
|
||||
|| !$this->Config()->Get('plugin', 'service', true)
|
||||
|| ($this->Config()->Get('plugin', 'favicon', false) && 'pass' == $mFrom['dkimStatus'])
|
||||
)
|
||||
) try {
|
||||
// Base64Url
|
||||
|
|
@ -74,6 +85,10 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
} catch (\Throwable $e) {
|
||||
\SnappyMail\Log::error('Crypt', $e->getMessage());
|
||||
}
|
||||
if ('pass' == $mFrom['dkimStatus'] && $this->Config()->Get('plugin', 'service', true)) {
|
||||
// 'data:image/png;base64,[a-zA-Z0-9+/=]'
|
||||
return static::getServiceIcon($mFrom['email']);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -84,8 +99,9 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
public function DoAvatar() : array
|
||||
{
|
||||
$bBimi = !empty($this->jsonParam('bimi'));
|
||||
$sBimiSelector = $this->jsonParam('bimiSelector') ?: '';
|
||||
$sEmail = $this->jsonParam('email');
|
||||
$aResult = $this->getAvatar($sEmail, !empty($bBimi));
|
||||
$aResult = $this->getAvatar($sEmail, $bBimi, $sBimiSelector);
|
||||
if ($aResult) {
|
||||
$aResult = [
|
||||
'type' => $aResult[0],
|
||||
|
|
@ -103,7 +119,9 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEmail)
|
||||
{
|
||||
$sEmail = \SnappyMail\Crypt::DecryptUrlSafe($sEmail);
|
||||
if ($sEmail && ($aResult = $this->getAvatar($sEmail, !empty($sBimi)))) {
|
||||
$aBimi = \explode('-', $sBimi, 2);
|
||||
$sBimiSelector = isset($aBimi[1]) ? $aBimi[1] : 'default';
|
||||
if ($sEmail && ($aResult = $this->getAvatar($sEmail, !empty($aBimi[0]), $sBimiSelector))) {
|
||||
\header('Content-Type: '.$aResult[0]);
|
||||
echo $aResult[1];
|
||||
} else {
|
||||
|
|
@ -122,12 +140,14 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
->SetDefaultValue(true),
|
||||
\RainLoop\Plugins\Property::NewInstance('bimi')->SetLabel('BIMI')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
// ->SetAllowedInJs(true)
|
||||
->SetDefaultValue(false)
|
||||
->SetDescription('https://bimigroup.org/ (DKIM header must be valid)'),
|
||||
\RainLoop\Plugins\Property::NewInstance('favicon')->SetLabel('Favicon')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
->SetDefaultValue(false)
|
||||
->SetDescription('Fetch favicon from domain (DKIM header must be valid)'),
|
||||
\RainLoop\Plugins\Property::NewInstance('gravatar')->SetLabel('Gravatar')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
// ->SetAllowedInJs(true)
|
||||
->SetDefaultValue(false)
|
||||
->SetDescription('https://wikipedia.org/wiki/Gravatar'),
|
||||
]);
|
||||
|
|
@ -135,7 +155,6 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
defined('RainLoop\\Enumerations\\PluginPropertyType::SELECT')
|
||||
? \RainLoop\Plugins\Property::NewInstance('identicon')->SetLabel('Identicon')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::SELECT)
|
||||
// ->SetAllowedInJs(true)
|
||||
->SetDefaultValue([
|
||||
['id' => '', 'name' => 'Name characters else silhouette'],
|
||||
['id' => 'identicon', 'name' => 'Name characters else squares'],
|
||||
|
|
@ -144,7 +163,6 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
->SetDescription('https://wikipedia.org/wiki/Identicon')
|
||||
: \RainLoop\Plugins\Property::NewInstance('identicon')->SetLabel('Identicon')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::SELECTION)
|
||||
// ->SetAllowedInJs(true)
|
||||
->SetDefaultValue(['','identicon','jdenticon'])
|
||||
->SetDescription('empty = default, identicon = squares, jdenticon = Triangles shape')
|
||||
,
|
||||
|
|
@ -190,13 +208,13 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
return null;
|
||||
}
|
||||
|
||||
private function getAvatar(string $sEmail, bool $bBimi) : ?array
|
||||
private function getAvatar(string $sEmail, bool $bBimi, string $sBimiSelector = '') : ?array
|
||||
{
|
||||
if (!\strpos($sEmail, '@')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$sAsciiEmail = \mb_strtolower(\MailSo\Base\Utils::IdnToAscii($sEmail, true));
|
||||
$sAsciiEmail = \mb_strtolower(\SnappyMail\IDN::emailToAscii($sEmail));
|
||||
$sEmailId = \sha1($sAsciiEmail);
|
||||
|
||||
\MailSo\Base\Http::setETag($sEmailId);
|
||||
|
|
@ -235,7 +253,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$aUrls = [];
|
||||
|
||||
if ($this->Config()->Get('plugin', 'bimi', false)) {
|
||||
$BIMI = $bBimi ? \SnappyMail\DNS::BIMI($sDomain) : null;
|
||||
$BIMI = $bBimi ? \SnappyMail\DNS::BIMI($sDomain, $sBimiSelector) : null;
|
||||
if ($BIMI) {
|
||||
$aUrls[] = $BIMI;
|
||||
// $aResult = ['text/uri-list', $BIMI];
|
||||
|
|
@ -246,7 +264,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
}
|
||||
|
||||
if ($this->Config()->Get('plugin', 'gravatar', false)) {
|
||||
$aUrls[] = 'http://gravatar.com/avatar/'.\md5(\strtolower($sAsciiEmail)).'?s=80&d=404';
|
||||
$aUrls[] = 'https://gravatar.com/avatar/'.\md5(\strtolower($sAsciiEmail)).'?s=80&d=404';
|
||||
}
|
||||
|
||||
foreach ($aUrls as $sUrl) {
|
||||
|
|
@ -277,11 +295,10 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (!$aResult) {
|
||||
$aResult = static::getFavicon($sEmail, $sDomain);
|
||||
|
||||
if (!$aResult && $this->Config()->Get('plugin', 'favicon', false)) {
|
||||
$aResult = static::getFavicon($sDomain);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
|
|
@ -292,16 +309,20 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$sDomain = \preg_replace('/^(.+\\.)?(paypal\\.[a-z][a-z])$/D', 'paypal.com', $sDomain);
|
||||
$sDomain = \preg_replace('/^facebookmail.com$/D', 'facebook.com', $sDomain);
|
||||
$sDomain = \preg_replace('/^dhlparcel.nl$/D', 'dhl.com', $sDomain);
|
||||
$sDomain = \preg_replace('/^amazon.nl$/D', 'amazon.com', $sDomain);
|
||||
$sDomain = \preg_replace('/^.+\\.([^.]+\\.[^.]+)$/D', '$1', $sDomain);
|
||||
return $sDomain;
|
||||
}
|
||||
|
||||
private static function cacheImage(string $sEmail, array $aResult) : void
|
||||
{
|
||||
$sEmailId = \sha1(\mb_strtolower(\MailSo\Base\Utils::IdnToAscii($sEmail, true)));
|
||||
if (!\is_dir(\APP_PRIVATE_DATA . 'avatars')) {
|
||||
\mkdir(\APP_PRIVATE_DATA . 'avatars', 0700);
|
||||
}
|
||||
$sEmailId = \mb_strtolower(\SnappyMail\IDN::emailToAscii($sEmail));
|
||||
if (\str_contains($sEmail, '@')) {
|
||||
$sEmailId = \sha1($sEmailId);
|
||||
}
|
||||
\file_put_contents(
|
||||
\APP_PRIVATE_DATA . 'avatars/' . $sEmailId . \SnappyMail\File\MimeType::toExtension($aResult[0]),
|
||||
$aResult[1]
|
||||
|
|
@ -311,35 +332,63 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
|
||||
private static function getCachedImage(string $sEmail) : ?array
|
||||
{
|
||||
$sEmail = \mb_strtolower(\MailSo\Base\Utils::IdnToAscii($sEmail, true));
|
||||
$sEmail = \mb_strtolower(\SnappyMail\IDN::emailToAscii($sEmail));
|
||||
$aFiles = \glob(\APP_PRIVATE_DATA . "avatars/{$sEmail}.*");
|
||||
if ($aFiles) {
|
||||
\MailSo\Base\Http::setLastModified(\filemtime($aFiles[0]));
|
||||
return [
|
||||
\mime_content_type($aFiles[0]),
|
||||
\file_get_contents($aFiles[0])
|
||||
];
|
||||
if (!$aFiles && \str_contains($sEmail, '@')) {
|
||||
$sEmailId = \sha1($sEmail);
|
||||
$aFiles = \glob(\APP_PRIVATE_DATA . "avatars/{$sEmailId}.*");
|
||||
if (!$aFiles) {
|
||||
$sDomain = \explode('@', $sEmail);
|
||||
$sDomain = \array_pop($sDomain);
|
||||
$aFiles = \glob(\APP_PRIVATE_DATA . "avatars/{$sDomain}.*");
|
||||
}
|
||||
}
|
||||
$sEmailId = \sha1($sEmail);
|
||||
$aFiles = \glob(\APP_PRIVATE_DATA . "avatars/{$sEmailId}.*");
|
||||
if ($aFiles) {
|
||||
\MailSo\Base\Http::setLastModified(\filemtime($aFiles[0]));
|
||||
return [
|
||||
\mime_content_type($aFiles[0]),
|
||||
\SnappyMail\File\MimeType::fromFile($aFiles[0]),
|
||||
\file_get_contents($aFiles[0])
|
||||
];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function getFavicon(string $sEmail, string $sDomain) : ?array
|
||||
private static function getFavicon(string $sDomain) : ?array
|
||||
{
|
||||
$aResult = static::getUrl('https://' . $sDomain . '/favicon.ico')
|
||||
?: static::getUrl('https://' . static::serviceDomain($sDomain) . '/favicon.ico')
|
||||
?: static::getUrl('https://www.' . static::serviceDomain($sDomain) . '/favicon.ico');
|
||||
// Also detect <link rel="shortcut icon" href="...">
|
||||
?: static::getUrl('https://www.' . static::serviceDomain($sDomain) . '/favicon.ico')
|
||||
?: static::getUrl("https://www.google.com/s2/favicons?sz=48&domain_url={$sDomain}")
|
||||
?: static::getUrl("https://api.faviconkit.com/{$sDomain}/48")
|
||||
// ?: static::getUrl("https://api.statvoo.com/favicon/{$sDomain}")
|
||||
;
|
||||
/*
|
||||
Also detect the following?
|
||||
|
||||
<link sizes="16x16" rel="shortcut icon" type="image/x-icon" href="/..." />
|
||||
<link sizes="16x16" rel="shortcut icon" type="image/png" href="/..." />
|
||||
<link sizes="32x32" rel="shortcut icon" type="image/png" href="/..." />
|
||||
<link sizes="96x96" rel="shortcut icon" type="image/png" href="/..." />
|
||||
|
||||
<link sizes="36x36" rel="icon" type="image/png" href="/..." />
|
||||
<link sizes="48x48" rel="icon" type="image/png" href="/..." />
|
||||
<link sizes="72x72" rel="icon" type="image/png" href="/..." />
|
||||
<link sizes="96x96" rel="icon" type="image/png" href="/..." />
|
||||
<link sizes="144x144" rel="icon" type="image/png" href="/..." />
|
||||
<link sizes="192x192" rel="icon" type="image/png" href="/..." />
|
||||
|
||||
<link sizes="57x57" rel="apple-touch-icon" type="image/png" href="/..." />
|
||||
<link sizes="60x60" rel="apple-touch-icon" type="image/png" href="/..." />
|
||||
<link sizes="72x72" rel="apple-touch-icon" type="image/png" href="/..." />
|
||||
<link sizes="76x76" rel="apple-touch-icon" type="image/png" href="/..." />
|
||||
<link sizes="114x114" rel="apple-touch-icon" type="image/png" href="/..." />
|
||||
<link sizes="120x120" rel="apple-touch-icon" type="image/png" href="/..." />
|
||||
<link sizes="144x144" rel="apple-touch-icon" type="image/png" href="/..." />
|
||||
<link sizes="152x152" rel="apple-touch-icon" type="image/png" href="/" />
|
||||
<link sizes="180x180" rel="apple-touch-icon" type="image/png" href="/..." />
|
||||
<link sizes="192x192" rel="apple-touch-icon" type="image/png" href="/..." />
|
||||
*/
|
||||
if ($aResult) {
|
||||
static::cacheImage($sEmail, $aResult);
|
||||
static::cacheImage($sDomain, $aResult);
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 RainLoop Team
|
||||
Copyright (c) 2016 RainLoop Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
95
plugins/backup/index.php
Normal file
95
plugins/backup/index.php
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
class BackupPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||
{
|
||||
const
|
||||
NAME = 'Backup',
|
||||
AUTHOR = 'SnappyMail',
|
||||
URL = 'https://snappymail.eu/',
|
||||
VERSION = '1.2',
|
||||
RELEASE = '2024-03-18',
|
||||
REQUIRED = '2.30.0',
|
||||
CATEGORY = 'General',
|
||||
LICENSE = 'MIT',
|
||||
DESCRIPTION = '';
|
||||
|
||||
public function Init() : void
|
||||
{
|
||||
// Admin Settings tab
|
||||
$this->addJs('js/BackupAdminSettings.js', true); // add js file
|
||||
$this->addJsonHook('JsonAdminBackupData');
|
||||
$this->addJsonHook('JsonAdminRestoreData');
|
||||
$this->addTemplate('templates/BackupAdminSettingsTab.html', true);
|
||||
}
|
||||
|
||||
public function JsonAdminBackupData()
|
||||
{
|
||||
if (!($this->Manager()->Actions() instanceof \RainLoop\ActionsAdmin)
|
||||
|| !$this->Manager()->Actions()->IsAdminLoggined()
|
||||
) {
|
||||
return $this->jsonResponse(__FUNCTION__, false);
|
||||
}
|
||||
|
||||
\file_put_contents(APP_PRIVATE_DATA.'cache/CACHEDIR.TAG', 'Signature: 8a477f597d28d172789f06886806bc55');
|
||||
|
||||
$sFileName = APP_PRIVATE_DATA . \MailSo\Base\Utils::Sha1Rand();
|
||||
|
||||
if (true) {
|
||||
$sType = 'application/zip';
|
||||
$sFileName .= '.zip';
|
||||
if (\class_exists('ZipArchive')) {
|
||||
// $oArchive = new \ZipArchive();
|
||||
// $oArchive->open($sFileName, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE);
|
||||
// $oArchive->setArchiveComment('SnappyMail/'.APP_VERSION);
|
||||
}
|
||||
$oArchive = new \SnappyMail\Stream\ZIP($sFileName);
|
||||
} else {
|
||||
$sType = 'application/x-gzip';
|
||||
$sFileName .= '.tgz';
|
||||
$oArchive = new \SnappyMail\Stream\TAR($sFileName);
|
||||
}
|
||||
|
||||
// $oArchive->addRecursive(APP_PRIVATE_DATA, '#/(cache.*)#');
|
||||
$oArchive->addRecursive(APP_PRIVATE_DATA.'configs', 'configs');
|
||||
$oArchive->addRecursive(APP_PRIVATE_DATA.'domains', 'domains');
|
||||
$oArchive->addRecursive(APP_PRIVATE_DATA.'plugins', 'plugins');
|
||||
$oArchive->addRecursive(APP_PRIVATE_DATA.'storage', 'storage');
|
||||
if (\is_readable(APP_PRIVATE_DATA.'AddressBook.sqlite')) {
|
||||
$oArchive->addFile(APP_PRIVATE_DATA.'AddressBook.sqlite');
|
||||
}
|
||||
// $oArchive->addFile(APP_DATA_FOLDER_PATH.'SALT.php');
|
||||
$oArchive->close();
|
||||
|
||||
$data = \base64_encode(\file_get_contents($sFileName));
|
||||
\unlink($sFileName);
|
||||
|
||||
return $this->jsonResponse(__FUNCTION__, array(
|
||||
'name' => \basename($sFileName),
|
||||
'data' => "data:{$sType};base64,{$data}"
|
||||
));
|
||||
}
|
||||
|
||||
public function JsonAdminRestoreData()
|
||||
{
|
||||
if (!($this->Manager()->Actions() instanceof \RainLoop\ActionsAdmin)
|
||||
|| empty($_FILES['backup'])
|
||||
|| 'application/zip' !== $_FILES['backup']['type']
|
||||
|| !\is_uploaded_file($_FILES['backup']['tmp_name'])
|
||||
) {
|
||||
return $this->jsonResponse(__FUNCTION__, false);
|
||||
}
|
||||
|
||||
$result = false;
|
||||
if (\class_exists('ZipArchive')) {
|
||||
$oArchive = new \ZipArchive();
|
||||
$oArchive->open($_FILES['backup']['tmp_name'], \ZIPARCHIVE::CREATE);
|
||||
$result = $oArchive->extractTo(APP_PRIVATE_DATA);
|
||||
} else if (\class_exists('PharData')) {
|
||||
$oArchive = new \PharData($sTmp, 0, null, \Phar::GZ);
|
||||
$result = $oArchive->extractTo(APP_PRIVATE_DATA);
|
||||
}
|
||||
|
||||
return $this->jsonResponse(__FUNCTION__, $result);
|
||||
}
|
||||
|
||||
}
|
||||
47
plugins/backup/js/BackupAdminSettings.js
Normal file
47
plugins/backup/js/BackupAdminSettings.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
(rl => { if (rl) {
|
||||
|
||||
class BackupAdminSettings
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this.loading = ko.observable(false);
|
||||
}
|
||||
|
||||
backup()
|
||||
{
|
||||
this.loading(true);
|
||||
rl.pluginRemoteRequest((iError, oData) => {
|
||||
|
||||
this.loading(false);
|
||||
|
||||
if (iError) {
|
||||
console.error({
|
||||
iError: iError,
|
||||
oData: oData
|
||||
});
|
||||
} else {
|
||||
var link = document.createElement("a");
|
||||
link.download = oData.Result.name;
|
||||
link.href = oData.Result.data;
|
||||
link.textContent = oData.Result.name;
|
||||
this.viewModelDom.append(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
}
|
||||
|
||||
}, 'JsonAdminBackupData');
|
||||
}
|
||||
|
||||
submitForm(form) {
|
||||
form.reportValidity()
|
||||
&& rl.pluginRemoteRequest((iError, oData) => {
|
||||
console.dir(oData);
|
||||
}, 'JsonAdminRestoreData', new FormData(form));
|
||||
}
|
||||
}
|
||||
|
||||
rl.addSettingsViewModelForAdmin(BackupAdminSettings, 'BackupAdminSettingsTab',
|
||||
'Backup and Restore', 'Backup');
|
||||
|
||||
}})(window.rl);
|
||||
|
|
@ -4,35 +4,31 @@ class BlackListPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
{
|
||||
const
|
||||
NAME = 'Blacklist',
|
||||
VERSION = '2.1',
|
||||
RELEASE = '2021-04-21',
|
||||
VERSION = '2.2',
|
||||
RELEASE = '2024-03-04',
|
||||
REQUIRED = '2.5.0',
|
||||
CATEGORY = 'Login',
|
||||
DESCRIPTION = 'Simple blacklist extension (with wildcard and exceptions functionality).';
|
||||
|
||||
public function Init() : void
|
||||
{
|
||||
$this->addHook('login.credentials', 'FilterLoginCredentials');
|
||||
$this->addHook('login.credentials.step-1', 'FilterLoginCredentials');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sEmail
|
||||
* @param string $sLogin
|
||||
* @param string $sPassword
|
||||
*
|
||||
* @throws \RainLoop\Exceptions\ClientException
|
||||
*/
|
||||
public function FilterLoginCredentials(&$sEmail, &$sLogin, &$sPassword)
|
||||
public function FilterLoginCredentials(string &$sEmail)
|
||||
{
|
||||
$sBlackList = \trim($this->Config()->Get('plugin', 'black_list', ''));
|
||||
if (0 < \strlen($sBlackList) && \RainLoop\Plugins\Helper::ValidateWildcardValues($sEmail, $sBlackList))
|
||||
{
|
||||
if (\strlen($sBlackList) && \RainLoop\Plugins\Helper::ValidateWildcardValues($sEmail, $sBlackList)) {
|
||||
$sExceptions = \trim($this->Config()->Get('plugin', 'exceptions', ''));
|
||||
if (0 === \strlen($sExceptions) || !\RainLoop\Plugins\Helper::ValidateWildcardValues($sEmail, $sExceptions))
|
||||
{
|
||||
if (!\strlen($sExceptions) || !\RainLoop\Plugins\Helper::ValidateWildcardValues($sEmail, $sExceptions)) {
|
||||
throw new \RainLoop\Exceptions\ClientException(
|
||||
$this->Config()->Get('plugin', 'auth_error', true) ?
|
||||
\RainLoop\Notifications::AuthError : \RainLoop\Notifications::AccountNotAllowed);
|
||||
$this->Config()->Get('plugin', 'auth_error', false)
|
||||
? \RainLoop\Notifications::AuthError
|
||||
: \RainLoop\Notifications::AccountNotAllowed
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +42,7 @@ class BlackListPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
\RainLoop\Plugins\Property::NewInstance('auth_error')->SetLabel('Auth Error')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
->SetDescription('Throw an authentication error instead of an access error.')
|
||||
->SetDefaultValue(true),
|
||||
->SetDefaultValue(false),
|
||||
\RainLoop\Plugins\Property::NewInstance('black_list')->SetLabel('Black List')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::STRING_TEXT)
|
||||
->SetDescription('Emails black list, space as delimiter, wildcard supported.')
|
||||
|
|
|
|||
61
plugins/cache-apcu/APCU.php
Normal file
61
plugins/cache-apcu/APCU.php
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of MailSo.
|
||||
*
|
||||
* (c) 2014 Usenko Timur
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MailSo\Cache\Drivers;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Cache
|
||||
* @subpackage Drivers
|
||||
*/
|
||||
class APCU implements \MailSo\Cache\DriverInterface
|
||||
{
|
||||
private string $sKeyPrefix;
|
||||
|
||||
public function setPrefix(string $sKeyPrefix) : void
|
||||
{
|
||||
$sKeyPrefix = \rtrim(\trim($sKeyPrefix), '\\/');
|
||||
$this->sKeyPrefix = empty($sKeyPrefix)
|
||||
? $sKeyPrefix
|
||||
: \preg_replace('/[^a-zA-Z0-9_]/', '_', $sKeyPrefix).'/';
|
||||
}
|
||||
|
||||
public function Set(string $sKey, string $sValue) : bool
|
||||
{
|
||||
return \apcu_store($this->generateCachedKey($sKey), (string) $sValue);
|
||||
}
|
||||
|
||||
public function Exists(string $sKey) : bool
|
||||
{
|
||||
return \apcu_exists($this->generateCachedKey($sKey));
|
||||
}
|
||||
|
||||
public function Get(string $sKey) : ?string
|
||||
{
|
||||
$sValue = \apcu_fetch($this->generateCachedKey($sKey));
|
||||
return \is_string($sValue) ? $sValue : null;
|
||||
}
|
||||
|
||||
public function Delete(string $sKey) : void
|
||||
{
|
||||
\apcu_delete($this->generateCachedKey($sKey));
|
||||
}
|
||||
|
||||
public function GC(int $iTimeToClearInHours = 24) : bool
|
||||
{
|
||||
return (0 === $iTimeToClearInHours) ? \apcu_clear_cache('user') : false;
|
||||
}
|
||||
|
||||
private function generateCachedKey(string $sKey) : string
|
||||
{
|
||||
return $this->sKeyPrefix.\sha1($sKey);
|
||||
}
|
||||
}
|
||||
34
plugins/cache-apcu/index.php
Normal file
34
plugins/cache-apcu/index.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
class CacheAPCuPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||
{
|
||||
const
|
||||
NAME = 'Cache APCu',
|
||||
VERSION = '2.36',
|
||||
RELEASE = '2024-03-22',
|
||||
REQUIRED = '2.36.0',
|
||||
CATEGORY = 'Cache',
|
||||
DESCRIPTION = 'Cache handler using PHP APCu';
|
||||
|
||||
public function Init() : void
|
||||
{
|
||||
if (\MailSo\Base\Utils::FunctionsCallable(array('apcu_store', 'apcu_fetch', 'apcu_delete', 'apcu_clear_cache'))) {
|
||||
$this->addHook('main.fabrica', 'MainFabrica');
|
||||
}
|
||||
}
|
||||
|
||||
public function Supported() : string
|
||||
{
|
||||
return \MailSo\Base\Utils::FunctionsCallable(array('apcu_store', 'apcu_fetch', 'apcu_delete', 'apcu_clear_cache'))
|
||||
? ''
|
||||
: 'PHP APCu not installed';
|
||||
}
|
||||
|
||||
public function MainFabrica($sName, &$mResult)
|
||||
{
|
||||
if ('cache' == $sName) {
|
||||
require_once __DIR__ . '/APCU.php';
|
||||
$mResult = new \MailSo\Cache\Drivers\APCU;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
plugins/cache-memcache/Memcache.php
Normal file
81
plugins/cache-memcache/Memcache.php
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of MailSo.
|
||||
*
|
||||
* (c) 2014 Usenko Timur
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace MailSo\Cache\Drivers;
|
||||
|
||||
/**
|
||||
* @category MailSo
|
||||
* @package Cache
|
||||
* @subpackage Drivers
|
||||
*/
|
||||
class Memcache implements \MailSo\Cache\DriverInterface
|
||||
{
|
||||
private int $iExpire;
|
||||
|
||||
/**
|
||||
* @var \Memcache|\Memcached|null
|
||||
*/
|
||||
private $oMem;
|
||||
|
||||
private string $sKeyPrefix;
|
||||
|
||||
function __construct(string $sHost = '127.0.0.1', int $iPort = 11211, int $iExpire = 43200)
|
||||
{
|
||||
$this->iExpire = 0 < $iExpire ? $iExpire : 43200;
|
||||
|
||||
$this->oMem = \class_exists('Memcache',false) ? new \Memcache : new \Memcached;
|
||||
if (!$this->oMem->addServer($sHost, \strpos($sHost, ':/') ? 0 : $iPort)) {
|
||||
$this->oMem = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function setPrefix(string $sKeyPrefix) : void
|
||||
{
|
||||
$sKeyPrefix = \rtrim(\trim($sKeyPrefix), '\\/');
|
||||
$this->sKeyPrefix = empty($sKeyPrefix)
|
||||
? $sKeyPrefix
|
||||
: \preg_replace('/[^a-zA-Z0-9_]/', '_', $sKeyPrefix).'/';
|
||||
}
|
||||
|
||||
public function Set(string $sKey, string $sValue) : bool
|
||||
{
|
||||
return $this->oMem ? $this->oMem->set($this->generateCachedKey($sKey), $sValue, 0, $this->iExpire) : false;
|
||||
}
|
||||
|
||||
public function Exists(string $sKey) : bool
|
||||
{
|
||||
return $this->oMem && false !== $this->oMem->get($this->generateCachedKey($sKey));
|
||||
}
|
||||
|
||||
public function Get(string $sKey) : ?string
|
||||
{
|
||||
$sValue = $this->oMem ? $this->oMem->get($this->generateCachedKey($sKey)) : null;
|
||||
return \is_string($sValue) ? $sValue : null;
|
||||
}
|
||||
|
||||
public function Delete(string $sKey) : void
|
||||
{
|
||||
$this->oMem && $this->oMem->delete($this->generateCachedKey($sKey));
|
||||
}
|
||||
|
||||
public function GC(int $iTimeToClearInHours = 24) : bool
|
||||
{
|
||||
if (0 === $iTimeToClearInHours && $this->oMem) {
|
||||
return $this->oMem->flush();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function generateCachedKey(string $sKey) : string
|
||||
{
|
||||
return $this->sKeyPrefix.\sha1($sKey);
|
||||
}
|
||||
}
|
||||
54
plugins/cache-memcache/index.php
Normal file
54
plugins/cache-memcache/index.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
class CacheMemcachePlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||
{
|
||||
const
|
||||
NAME = 'Cache Memcache',
|
||||
VERSION = '2.36',
|
||||
RELEASE = '2024-03-22',
|
||||
REQUIRED = '2.36.0',
|
||||
CATEGORY = 'Cache',
|
||||
DESCRIPTION = 'Cache handler using PHP Memcache or PHP Memcached';
|
||||
|
||||
public function Init() : void
|
||||
{
|
||||
if (\class_exists('Memcache',false) || \class_exists('Memcached',false)) {
|
||||
$this->addHook('main.fabrica', 'MainFabrica');
|
||||
}
|
||||
}
|
||||
|
||||
public function Supported() : string
|
||||
{
|
||||
return (\class_exists('Memcache',false) || \class_exists('Memcached',false))
|
||||
? ''
|
||||
: 'PHP Memcache/Memcached not installed';
|
||||
}
|
||||
|
||||
public function MainFabrica($sName, &$mResult)
|
||||
{
|
||||
if ('cache' == $sName) {
|
||||
require_once __DIR__ . '/Memcache.php';
|
||||
$mResult = new \MailSo\Cache\Drivers\Memcache(
|
||||
$this->Config()->Get('plugin', 'host', '127.0.0.1'),
|
||||
(int) $this->Config()->Get('plugin', 'port', 11211)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function configMapping() : array
|
||||
{
|
||||
return array(
|
||||
\RainLoop\Plugins\Property::NewInstance('host')->SetLabel('Host')
|
||||
->SetDescription('Hostname of the memcache server')
|
||||
->SetDefaultValue('127.0.0.1'),
|
||||
\RainLoop\Plugins\Property::NewInstance('port')->SetLabel('Port')
|
||||
->SetDescription('Port of the memcache server')
|
||||
->SetDefaultValue(11211)
|
||||
/*
|
||||
,\RainLoop\Plugins\Property::NewInstance('password')->SetLabel('Password')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::PASSWORD)
|
||||
->SetDefaultValue('')
|
||||
*/
|
||||
);
|
||||
}
|
||||
}
|
||||
22
plugins/cache-redis/LICENSE
Normal file
22
plugins/cache-redis/LICENSE
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2009-2020 Daniele Alessandri (original work)
|
||||
Copyright (c) 2021-2023 Till Krüss (modified work)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
64
plugins/cache-redis/Predis/Autoloader.php
Normal file
64
plugins/cache-redis/Predis/Autoloader.php
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
/**
|
||||
* Implements a lightweight PSR-0 compliant autoloader for Predis.
|
||||
*
|
||||
* @author Eric Naeseth <eric@thumbtack.com>
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Autoloader
|
||||
{
|
||||
private $directory;
|
||||
private $prefix;
|
||||
private $prefixLength;
|
||||
|
||||
/**
|
||||
* @param string $baseDirectory Base directory where the source files are located.
|
||||
*/
|
||||
public function __construct($baseDirectory = __DIR__)
|
||||
{
|
||||
$this->directory = $baseDirectory;
|
||||
$this->prefix = __NAMESPACE__ . '\\';
|
||||
$this->prefixLength = strlen($this->prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the autoloader class with the PHP SPL autoloader.
|
||||
*
|
||||
* @param bool $prepend Prepend the autoloader on the stack instead of appending it.
|
||||
*/
|
||||
public static function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register([new self(), 'autoload'], true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a class from a file using its fully qualified name.
|
||||
*
|
||||
* @param string $className Fully qualified name of a class.
|
||||
*/
|
||||
public function autoload($className)
|
||||
{
|
||||
if (0 === strpos($className, $this->prefix)) {
|
||||
$parts = explode('\\', substr($className, $this->prefixLength));
|
||||
$filepath = $this->directory . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $parts) . '.php';
|
||||
|
||||
if (is_file($filepath)) {
|
||||
require $filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
612
plugins/cache-redis/Predis/Client.php
Normal file
612
plugins/cache-redis/Predis/Client.php
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
use ArrayIterator;
|
||||
use InvalidArgumentException;
|
||||
use IteratorAggregate;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\RawCommand;
|
||||
use Predis\Command\Redis\Container\ContainerFactory;
|
||||
use Predis\Command\Redis\Container\ContainerInterface;
|
||||
use Predis\Command\ScriptCommand;
|
||||
use Predis\Configuration\Options;
|
||||
use Predis\Configuration\OptionsInterface;
|
||||
use Predis\Connection\ConnectionInterface;
|
||||
use Predis\Connection\Parameters;
|
||||
use Predis\Connection\ParametersInterface;
|
||||
use Predis\Connection\RelayConnection;
|
||||
use Predis\Monitor\Consumer as MonitorConsumer;
|
||||
use Predis\Pipeline\Atomic;
|
||||
use Predis\Pipeline\FireAndForget;
|
||||
use Predis\Pipeline\Pipeline;
|
||||
use Predis\Pipeline\RelayAtomic;
|
||||
use Predis\Pipeline\RelayPipeline;
|
||||
use Predis\PubSub\Consumer as PubSubConsumer;
|
||||
use Predis\PubSub\RelayConsumer as RelayPubSubConsumer;
|
||||
use Predis\Response\ErrorInterface as ErrorResponseInterface;
|
||||
use Predis\Response\ResponseInterface;
|
||||
use Predis\Response\ServerException;
|
||||
use Predis\Transaction\MultiExec as MultiExecTransaction;
|
||||
use ReturnTypeWillChange;
|
||||
use RuntimeException;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Client class used for connecting and executing commands on Redis.
|
||||
*
|
||||
* This is the main high-level abstraction of Predis upon which various other
|
||||
* abstractions are built. Internally it aggregates various other classes each
|
||||
* one with its own responsibility and scope.
|
||||
*
|
||||
* @template-implements \IteratorAggregate<string, static>
|
||||
*/
|
||||
class Client implements ClientInterface, IteratorAggregate
|
||||
{
|
||||
public const VERSION = '2.2.2';
|
||||
|
||||
/** @var OptionsInterface */
|
||||
private $options;
|
||||
|
||||
/** @var ConnectionInterface */
|
||||
private $connection;
|
||||
|
||||
/** @var Command\FactoryInterface */
|
||||
private $commands;
|
||||
|
||||
/**
|
||||
* @param mixed $parameters Connection parameters for one or more servers.
|
||||
* @param mixed $options Options to configure some behaviours of the client.
|
||||
*/
|
||||
public function __construct($parameters = null, $options = null)
|
||||
{
|
||||
$this->options = static::createOptions($options ?? new Options());
|
||||
$this->connection = static::createConnection($this->options, $parameters ?? new Parameters());
|
||||
$this->commands = $this->options->commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new set of client options for the client.
|
||||
*
|
||||
* @param array|OptionsInterface $options Set of client options
|
||||
*
|
||||
* @return OptionsInterface
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected static function createOptions($options)
|
||||
{
|
||||
if (is_array($options)) {
|
||||
return new Options($options);
|
||||
} elseif ($options instanceof OptionsInterface) {
|
||||
return $options;
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid type for client options');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates single or aggregate connections from supplied arguments.
|
||||
*
|
||||
* This method accepts the following types to create a connection instance:
|
||||
*
|
||||
* - Array (dictionary: single connection, indexed: aggregate connections)
|
||||
* - String (URI for a single connection)
|
||||
* - Callable (connection initializer callback)
|
||||
* - Instance of Predis\Connection\ParametersInterface (used as-is)
|
||||
* - Instance of Predis\Connection\ConnectionInterface (returned as-is)
|
||||
*
|
||||
* When a callable is passed, it receives the original set of client options
|
||||
* and must return an instance of Predis\Connection\ConnectionInterface.
|
||||
*
|
||||
* Connections are created using the connection factory (in case of single
|
||||
* connections) or a specialized aggregate connection initializer (in case
|
||||
* of cluster and replication) retrieved from the supplied client options.
|
||||
*
|
||||
* @param OptionsInterface $options Client options container
|
||||
* @param mixed $parameters Connection parameters
|
||||
*
|
||||
* @return ConnectionInterface
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected static function createConnection(OptionsInterface $options, $parameters)
|
||||
{
|
||||
if ($parameters instanceof ConnectionInterface) {
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
if ($parameters instanceof ParametersInterface || is_string($parameters)) {
|
||||
return $options->connections->create($parameters);
|
||||
}
|
||||
|
||||
if (is_array($parameters)) {
|
||||
if (!isset($parameters[0])) {
|
||||
return $options->connections->create($parameters);
|
||||
} elseif ($options->defined('cluster') && $initializer = $options->cluster) {
|
||||
return $initializer($parameters, true);
|
||||
} elseif ($options->defined('replication') && $initializer = $options->replication) {
|
||||
return $initializer($parameters, true);
|
||||
} elseif ($options->defined('aggregate') && $initializer = $options->aggregate) {
|
||||
return $initializer($parameters, false);
|
||||
} else {
|
||||
throw new InvalidArgumentException(
|
||||
'Array of connection parameters requires `cluster`, `replication` or `aggregate` client option'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_callable($parameters)) {
|
||||
$connection = call_user_func($parameters, $options);
|
||||
|
||||
if (!$connection instanceof ConnectionInterface) {
|
||||
throw new InvalidArgumentException('Callable parameters must return a valid connection');
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Invalid type for connection parameters');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCommandFactory()
|
||||
{
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new client using a specific underlying connection.
|
||||
*
|
||||
* This method allows to create a new client instance by picking a specific
|
||||
* connection out of an aggregate one, with the same options of the original
|
||||
* client instance.
|
||||
*
|
||||
* The specified selector defines which logic to use to look for a suitable
|
||||
* connection by the specified value. Supported selectors are:
|
||||
*
|
||||
* - `id`
|
||||
* - `key`
|
||||
* - `slot`
|
||||
* - `command`
|
||||
* - `alias`
|
||||
* - `role`
|
||||
*
|
||||
* Internally the client relies on duck-typing and follows this convention:
|
||||
*
|
||||
* $selector string => getConnectionBy$selector($value) method
|
||||
*
|
||||
* This means that support for specific selectors may vary depending on the
|
||||
* actual logic implemented by connection classes and there is no interface
|
||||
* binding a connection class to implement any of these.
|
||||
*
|
||||
* @param string $selector Type of selector.
|
||||
* @param mixed $value Value to be used by the selector.
|
||||
*
|
||||
* @return ClientInterface
|
||||
*/
|
||||
public function getClientBy($selector, $value)
|
||||
{
|
||||
$selector = strtolower($selector);
|
||||
|
||||
if (!in_array($selector, ['id', 'key', 'slot', 'role', 'alias', 'command'])) {
|
||||
throw new InvalidArgumentException("Invalid selector type: `$selector`");
|
||||
}
|
||||
|
||||
if (!method_exists($this->connection, $method = "getConnectionBy$selector")) {
|
||||
$class = get_class($this->connection);
|
||||
throw new InvalidArgumentException("Selecting connection by $selector is not supported by $class");
|
||||
}
|
||||
|
||||
if (!$connection = $this->connection->$method($value)) {
|
||||
throw new InvalidArgumentException("Cannot find a connection by $selector matching `$value`");
|
||||
}
|
||||
|
||||
return new static($connection, $this->getOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the underlying connection and connects to the server.
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
$this->connection->connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying connection and disconnects from the server.
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
$this->connection->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying connection and disconnects from the server.
|
||||
*
|
||||
* This is the same as `Client::disconnect()` as it does not actually send
|
||||
* the `QUIT` command to Redis, but simply closes the connection.
|
||||
*/
|
||||
public function quit()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of the underlying connection.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnected()
|
||||
{
|
||||
return $this->connection->isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the configured serializer and compression to given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function pack($value)
|
||||
{
|
||||
return $this->connection instanceof RelayConnection
|
||||
? $this->connection->pack($value)
|
||||
: $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes and decompresses to given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function unpack($value)
|
||||
{
|
||||
return $this->connection instanceof RelayConnection
|
||||
? $this->connection->unpack($value)
|
||||
: $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a command without filtering its arguments, parsing the response,
|
||||
* applying any prefix to keys or throwing exceptions on Redis errors even
|
||||
* regardless of client options.
|
||||
*
|
||||
* It is possible to identify Redis error responses from normal responses
|
||||
* using the second optional argument which is populated by reference.
|
||||
*
|
||||
* @param array $arguments Command arguments as defined by the command signature.
|
||||
* @param bool $error Set to TRUE when Redis returned an error response.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeRaw(array $arguments, &$error = null)
|
||||
{
|
||||
$error = false;
|
||||
$commandID = array_shift($arguments);
|
||||
|
||||
$response = $this->connection->executeCommand(
|
||||
new RawCommand($commandID, $arguments)
|
||||
);
|
||||
|
||||
if ($response instanceof ResponseInterface) {
|
||||
if ($response instanceof ErrorResponseInterface) {
|
||||
$error = true;
|
||||
}
|
||||
|
||||
return (string) $response;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __call($commandID, $arguments)
|
||||
{
|
||||
return $this->executeCommand(
|
||||
$this->createCommand($commandID, $arguments)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createCommand($commandID, $arguments = [])
|
||||
{
|
||||
return $this->commands->create($commandID, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return ContainerInterface
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
return ContainerFactory::create($this, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function __set(string $name, $value)
|
||||
{
|
||||
throw new RuntimeException('Not allowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __isset(string $name)
|
||||
{
|
||||
throw new RuntimeException('Not allowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function executeCommand(CommandInterface $command)
|
||||
{
|
||||
$response = $this->connection->executeCommand($command);
|
||||
|
||||
if ($response instanceof ResponseInterface) {
|
||||
if ($response instanceof ErrorResponseInterface) {
|
||||
$response = $this->onErrorResponse($command, $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $command->parseResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles -ERR responses returned by Redis.
|
||||
*
|
||||
* @param CommandInterface $command Redis command that generated the error.
|
||||
* @param ErrorResponseInterface $response Instance of the error response.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws ServerException
|
||||
*/
|
||||
protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
|
||||
{
|
||||
if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
|
||||
$response = $this->executeCommand($command->getEvalCommand());
|
||||
|
||||
if (!$response instanceof ResponseInterface) {
|
||||
$response = $command->parseResponse($response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($this->options->exceptions) {
|
||||
throw new ServerException($response->getMessage());
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the specified initializer method on `$this` by adjusting the
|
||||
* actual invocation depending on the arity (0, 1 or 2 arguments). This is
|
||||
* simply an utility method to create Redis contexts instances since they
|
||||
* follow a common initialization path.
|
||||
*
|
||||
* @param string $initializer Method name.
|
||||
* @param array $argv Arguments for the method.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function sharedContextFactory($initializer, $argv = null)
|
||||
{
|
||||
switch (count($argv)) {
|
||||
case 0:
|
||||
return $this->$initializer();
|
||||
|
||||
case 1:
|
||||
return is_array($argv[0])
|
||||
? $this->$initializer($argv[0])
|
||||
: $this->$initializer(null, $argv[0]);
|
||||
|
||||
case 2:
|
||||
[$arg0, $arg1] = $argv;
|
||||
|
||||
return $this->$initializer($arg0, $arg1);
|
||||
|
||||
default:
|
||||
return $this->$initializer($this, $argv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new pipeline context and returns it, or returns the results of
|
||||
* a pipeline executed inside the optionally provided callable object.
|
||||
*
|
||||
* @param mixed ...$arguments Array of options, a callable for execution, or both.
|
||||
*
|
||||
* @return Pipeline|array
|
||||
*/
|
||||
public function pipeline(...$arguments)
|
||||
{
|
||||
return $this->sharedContextFactory('createPipeline', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual pipeline context initializer method.
|
||||
*
|
||||
* @param array|null $options Options for the context.
|
||||
* @param mixed $callable Optional callable used to execute the context.
|
||||
*
|
||||
* @return Pipeline|array
|
||||
*/
|
||||
protected function createPipeline(array $options = null, $callable = null)
|
||||
{
|
||||
if (isset($options['atomic']) && $options['atomic']) {
|
||||
$class = Atomic::class;
|
||||
} elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
|
||||
$class = FireAndForget::class;
|
||||
} else {
|
||||
$class = Pipeline::class;
|
||||
}
|
||||
|
||||
if ($this->connection instanceof RelayConnection) {
|
||||
if (isset($options['atomic']) && $options['atomic']) {
|
||||
$class = RelayAtomic::class;
|
||||
} elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
|
||||
throw new NotSupportedException('The "relay" extension does not support fire-and-forget pipelines.');
|
||||
} else {
|
||||
$class = RelayPipeline::class;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @var ClientContextInterface
|
||||
*/
|
||||
$pipeline = new $class($this);
|
||||
|
||||
if (isset($callable)) {
|
||||
return $pipeline->execute($callable);
|
||||
}
|
||||
|
||||
return $pipeline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new transaction context and returns it, or returns the results
|
||||
* of a transaction executed inside the optionally provided callable object.
|
||||
*
|
||||
* @param mixed ...$arguments Array of options, a callable for execution, or both.
|
||||
*
|
||||
* @return MultiExecTransaction|array
|
||||
*/
|
||||
public function transaction(...$arguments)
|
||||
{
|
||||
return $this->sharedContextFactory('createTransaction', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual transaction context initializer method.
|
||||
*
|
||||
* @param array $options Options for the context.
|
||||
* @param mixed $callable Optional callable used to execute the context.
|
||||
*
|
||||
* @return MultiExecTransaction|array
|
||||
*/
|
||||
protected function createTransaction(array $options = null, $callable = null)
|
||||
{
|
||||
$transaction = new MultiExecTransaction($this, $options);
|
||||
|
||||
if (isset($callable)) {
|
||||
return $transaction->execute($callable);
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new publish/subscribe context and returns it, or starts its loop
|
||||
* inside the optionally provided callable object.
|
||||
*
|
||||
* @param mixed ...$arguments Array of options, a callable for execution, or both.
|
||||
*
|
||||
* @return PubSubConsumer|null
|
||||
*/
|
||||
public function pubSubLoop(...$arguments)
|
||||
{
|
||||
return $this->sharedContextFactory('createPubSub', func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual publish/subscribe context initializer method.
|
||||
*
|
||||
* @param array $options Options for the context.
|
||||
* @param mixed $callable Optional callable used to execute the context.
|
||||
*
|
||||
* @return PubSubConsumer|null
|
||||
*/
|
||||
protected function createPubSub(array $options = null, $callable = null)
|
||||
{
|
||||
if ($this->connection instanceof RelayConnection) {
|
||||
$pubsub = new RelayPubSubConsumer($this, $options);
|
||||
} else {
|
||||
$pubsub = new PubSubConsumer($this, $options);
|
||||
}
|
||||
|
||||
if (!isset($callable)) {
|
||||
return $pubsub;
|
||||
}
|
||||
|
||||
foreach ($pubsub as $message) {
|
||||
if (call_user_func($callable, $pubsub, $message) === false) {
|
||||
$pubsub->stop();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new monitor consumer and returns it.
|
||||
*
|
||||
* @return MonitorConsumer
|
||||
*/
|
||||
public function monitor()
|
||||
{
|
||||
return new MonitorConsumer($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Traversable<string, static>
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
$clients = [];
|
||||
$connection = $this->getConnection();
|
||||
|
||||
if (!$connection instanceof Traversable) {
|
||||
return new ArrayIterator([
|
||||
(string) $connection => new static($connection, $this->getOptions()),
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($connection as $node) {
|
||||
$clients[(string) $node] = new static($node, $this->getOptions());
|
||||
}
|
||||
|
||||
return new ArrayIterator($clients);
|
||||
}
|
||||
}
|
||||
42
plugins/cache-redis/Predis/ClientConfiguration.php
Normal file
42
plugins/cache-redis/Predis/ClientConfiguration.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
class ClientConfiguration
|
||||
{
|
||||
/**
|
||||
* @var array{modules: array}|string[][]
|
||||
*/
|
||||
private static $config = [
|
||||
'modules' => [
|
||||
['name' => 'Json', 'commandPrefix' => 'JSON'],
|
||||
['name' => 'BloomFilter', 'commandPrefix' => 'BF'],
|
||||
['name' => 'CuckooFilter', 'commandPrefix' => 'CF'],
|
||||
['name' => 'CountMinSketch', 'commandPrefix' => 'CMS'],
|
||||
['name' => 'TDigest', 'commandPrefix' => 'TDIGEST'],
|
||||
['name' => 'TopK', 'commandPrefix' => 'TOPK'],
|
||||
['name' => 'Search', 'commandPrefix' => 'FT'],
|
||||
['name' => 'TimeSeries', 'commandPrefix' => 'TS'],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns available modules with configuration.
|
||||
*
|
||||
* @return array|string[][]
|
||||
*/
|
||||
public static function getModules(): array
|
||||
{
|
||||
return self::$config['modules'];
|
||||
}
|
||||
}
|
||||
380
plugins/cache-redis/Predis/ClientContextInterface.php
Normal file
380
plugins/cache-redis/Predis/ClientContextInterface.php
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
use Predis\Command\Argument\Geospatial\ByInterface;
|
||||
use Predis\Command\Argument\Geospatial\FromInterface;
|
||||
use Predis\Command\Argument\Search\AggregateArguments;
|
||||
use Predis\Command\Argument\Search\AlterArguments;
|
||||
use Predis\Command\Argument\Search\CreateArguments;
|
||||
use Predis\Command\Argument\Search\DropArguments;
|
||||
use Predis\Command\Argument\Search\ExplainArguments;
|
||||
use Predis\Command\Argument\Search\ProfileArguments;
|
||||
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
|
||||
use Predis\Command\Argument\Search\SearchArguments;
|
||||
use Predis\Command\Argument\Search\SugAddArguments;
|
||||
use Predis\Command\Argument\Search\SugGetArguments;
|
||||
use Predis\Command\Argument\Search\SynUpdateArguments;
|
||||
use Predis\Command\Argument\Server\LimitOffsetCount;
|
||||
use Predis\Command\Argument\Server\To;
|
||||
use Predis\Command\Argument\TimeSeries\AddArguments;
|
||||
use Predis\Command\Argument\TimeSeries\AlterArguments as TSAlterArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments as TSCreateArguments;
|
||||
use Predis\Command\Argument\TimeSeries\DecrByArguments;
|
||||
use Predis\Command\Argument\TimeSeries\GetArguments;
|
||||
use Predis\Command\Argument\TimeSeries\IncrByArguments;
|
||||
use Predis\Command\Argument\TimeSeries\InfoArguments;
|
||||
use Predis\Command\Argument\TimeSeries\MGetArguments;
|
||||
use Predis\Command\Argument\TimeSeries\MRangeArguments;
|
||||
use Predis\Command\Argument\TimeSeries\RangeArguments;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\Redis\Container\ACL;
|
||||
use Predis\Command\Redis\Container\CLUSTER;
|
||||
use Predis\Command\Redis\Container\FunctionContainer;
|
||||
use Predis\Command\Redis\Container\Json\JSONDEBUG;
|
||||
use Predis\Command\Redis\Container\Search\FTCONFIG;
|
||||
use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
|
||||
/**
|
||||
* Interface defining a client-side context such as a pipeline or transaction.
|
||||
*
|
||||
* @method $this copy(string $source, string $destination, int $db = -1, bool $replace = false)
|
||||
* @method $this del(array|string $keys)
|
||||
* @method $this dump($key)
|
||||
* @method $this exists($key)
|
||||
* @method $this expire($key, $seconds, string $expireOption = '')
|
||||
* @method $this expireat($key, $timestamp, string $expireOption = '')
|
||||
* @method $this expiretime(string $key)
|
||||
* @method $this keys($pattern)
|
||||
* @method $this move($key, $db)
|
||||
* @method $this object($subcommand, $key)
|
||||
* @method $this persist($key)
|
||||
* @method $this pexpire($key, $milliseconds)
|
||||
* @method $this pexpireat($key, $timestamp)
|
||||
* @method $this pttl($key)
|
||||
* @method $this randomkey()
|
||||
* @method $this rename($key, $target)
|
||||
* @method $this renamenx($key, $target)
|
||||
* @method $this scan($cursor, array $options = null)
|
||||
* @method $this sort($key, array $options = null)
|
||||
* @method $this sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false)
|
||||
* @method $this ttl($key)
|
||||
* @method $this type($key)
|
||||
* @method $this append($key, $value)
|
||||
* @method $this bfadd(string $key, $item)
|
||||
* @method $this bfexists(string $key, $item)
|
||||
* @method $this bfinfo(string $key, string $modifier = '')
|
||||
* @method $this bfinsert(string $key, int $capacity = -1, float $error = -1, int $expansion = -1, bool $noCreate = false, bool $nonScaling = false, string ...$item)
|
||||
* @method $this bfloadchunk(string $key, int $iterator, $data)
|
||||
* @method $this bfmadd(string $key, ...$item)
|
||||
* @method $this bfmexists(string $key, ...$item)
|
||||
* @method $this bfreserve(string $key, float $errorRate, int $capacity, int $expansion = -1, bool $nonScaling = false)
|
||||
* @method $this bfscandump(string $key, int $iterator)
|
||||
* @method $this bitcount(string $key, $start = null, $end = null, string $index = 'byte')
|
||||
* @method $this bitop($operation, $destkey, $key)
|
||||
* @method $this bitfield($key, $subcommand, ...$subcommandArg)
|
||||
* @method $this bitpos($key, $bit, $start = null, $end = null, string $index = 'byte')
|
||||
* @method $this blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
|
||||
* @method $this bzpopmax(array $keys, int $timeout)
|
||||
* @method $this bzpopmin(array $keys, int $timeout)
|
||||
* @method $this bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
|
||||
* @method $this cfadd(string $key, $item)
|
||||
* @method $this cfaddnx(string $key, $item)
|
||||
* @method $this cfcount(string $key, $item)
|
||||
* @method $this cfdel(string $key, $item)
|
||||
* @method $this cfexists(string $key, $item)
|
||||
* @method $this cfloadchunk(string $key, int $iterator, $data)
|
||||
* @method $this cfmexists(string $key, ...$item)
|
||||
* @method $this cfinfo(string $key)
|
||||
* @method $this cfinsert(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
|
||||
* @method $this cfinsertnx(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
|
||||
* @method $this cfreserve(string $key, int $capacity, int $bucketSize = -1, int $maxIterations = -1, int $expansion = -1)
|
||||
* @method $this cfscandump(string $key, int $iterator)
|
||||
* @method $this cmsincrby(string $key, string|int...$itemIncrementDictionary)
|
||||
* @method $this cmsinfo(string $key)
|
||||
* @method $this cmsinitbydim(string $key, int $width, int $depth)
|
||||
* @method $this cmsinitbyprob(string $key, float $errorRate, float $probability)
|
||||
* @method $this cmsmerge(string $destination, array $sources, array $weights = [])
|
||||
* @method $this cmsquery(string $key, string ...$item)
|
||||
* @method $this decr($key)
|
||||
* @method $this decrby($key, $decrement)
|
||||
* @method $this failover(?To $to = null, bool $abort = false, int $timeout = -1)
|
||||
* @method $this fcall(string $function, array $keys, ...$args)
|
||||
* @method $this fcall_ro(string $function, array $keys, ...$args)
|
||||
* @method $this ftaggregate(string $index, string $query, ?AggregateArguments $arguments = null)
|
||||
* @method $this ftaliasadd(string $alias, string $index)
|
||||
* @method $this ftaliasdel(string $alias)
|
||||
* @method $this ftaliasupdate(string $alias, string $index)
|
||||
* @method $this ftalter(string $index, FieldInterface[] $schema, ?AlterArguments $arguments = null)
|
||||
* @method $this ftcreate(string $index, FieldInterface[] $schema, ?CreateArguments $arguments = null)
|
||||
* @method $this ftdictadd(string $dict, ...$term)
|
||||
* @method $this ftdictdel(string $dict, ...$term)
|
||||
* @method $this ftdictdump(string $dict)
|
||||
* @method $this ftdropindex(string $index, ?DropArguments $arguments = null)
|
||||
* @method $this ftexplain(string $index, string $query, ?ExplainArguments $arguments = null)
|
||||
* @method $this ftinfo(string $index)
|
||||
* @method $this ftprofile(string $index, ProfileArguments $arguments)
|
||||
* @method $this ftsearch(string $index, string $query, ?SearchArguments $arguments = null)
|
||||
* @method $this ftspellcheck(string $index, string $query, ?SearchArguments $arguments = null)
|
||||
* @method $this ftsugadd(string $key, string $string, float $score, ?SugAddArguments $arguments = null)
|
||||
* @method $this ftsugdel(string $key, string $string)
|
||||
* @method $this ftsugget(string $key, string $prefix, ?SugGetArguments $arguments = null)
|
||||
* @method $this ftsuglen(string $key)
|
||||
* @method $this ftsyndump(string $index)
|
||||
* @method $this ftsynupdate(string $index, string $synonymGroupId, ?SynUpdateArguments $arguments = null, string ...$terms)
|
||||
* @method $this fttagvals(string $index, string $fieldName)
|
||||
* @method $this get($key)
|
||||
* @method $this getbit($key, $offset)
|
||||
* @method $this getex(string $key, $modifier = '', $value = false)
|
||||
* @method $this getrange($key, $start, $end)
|
||||
* @method $this getdel(string $key)
|
||||
* @method $this getset($key, $value)
|
||||
* @method $this incr($key)
|
||||
* @method $this incrby($key, $increment)
|
||||
* @method $this incrbyfloat($key, $increment)
|
||||
* @method $this mget(array $keys)
|
||||
* @method $this mset(array $dictionary)
|
||||
* @method $this msetnx(array $dictionary)
|
||||
* @method $this psetex($key, $milliseconds, $value)
|
||||
* @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method $this setbit($key, $offset, $value)
|
||||
* @method $this setex($key, $seconds, $value)
|
||||
* @method $this setnx($key, $value)
|
||||
* @method $this setrange($key, $offset, $value)
|
||||
* @method $this strlen($key)
|
||||
* @method $this hdel($key, array $fields)
|
||||
* @method $this hexists($key, $field)
|
||||
* @method $this hget($key, $field)
|
||||
* @method $this hgetall($key)
|
||||
* @method $this hincrby($key, $field, $increment)
|
||||
* @method $this hincrbyfloat($key, $field, $increment)
|
||||
* @method $this hkeys($key)
|
||||
* @method $this hlen($key)
|
||||
* @method $this hmget($key, array $fields)
|
||||
* @method $this hmset($key, array $dictionary)
|
||||
* @method $this hrandfield(string $key, int $count = 1, bool $withValues = false)
|
||||
* @method $this hscan($key, $cursor, array $options = null)
|
||||
* @method $this hset($key, $field, $value)
|
||||
* @method $this hsetnx($key, $field, $value)
|
||||
* @method $this hvals($key)
|
||||
* @method $this hstrlen($key, $field)
|
||||
* @method $this jsonarrappend(string $key, string $path = '$', ...$value)
|
||||
* @method $this jsonarrindex(string $key, string $path, string $value, int $start = 0, int $stop = 0)
|
||||
* @method $this jsonarrinsert(string $key, string $path, int $index, string ...$value)
|
||||
* @method $this jsonarrlen(string $key, string $path = '$')
|
||||
* @method $this jsonarrpop(string $key, string $path = '$', int $index = -1)
|
||||
* @method $this jsonarrtrim(string $key, string $path, int $start, int $stop)
|
||||
* @method $this jsonclear(string $key, string $path = '$')
|
||||
* @method $this jsondel(string $key, string $path = '$')
|
||||
* @method $this jsonforget(string $key, string $path = '$')
|
||||
* @method $this jsonget(string $key, string $indent = '', string $newline = '', string $space = '', string ...$paths)
|
||||
* @method $this jsonnumincrby(string $key, string $path, int $value)
|
||||
* @method $this jsonmerge(string $key, string $path, string $value)
|
||||
* @method $this jsonmget(array $keys, string $path)
|
||||
* @method $this jsonmset(string ...$keyPathValue)
|
||||
* @method $this jsonobjkeys(string $key, string $path = '$')
|
||||
* @method $this jsonobjlen(string $key, string $path = '$')
|
||||
* @method $this jsonresp(string $key, string $path = '$')
|
||||
* @method $this jsonset(string $key, string $path, string $value, ?string $subcommand = null)
|
||||
* @method $this jsonstrappend(string $key, string $path, string $value)
|
||||
* @method $this jsonstrlen(string $key, string $path = '$')
|
||||
* @method $this jsontoggle(string $key, string $path)
|
||||
* @method $this jsontype(string $key, string $path = '$')
|
||||
* @method $this blmove(string $source, string $destination, string $where, string $to, int $timeout)
|
||||
* @method $this blpop(array|string $keys, $timeout)
|
||||
* @method $this brpop(array|string $keys, $timeout)
|
||||
* @method $this brpoplpush($source, $destination, $timeout)
|
||||
* @method $this lcs(string $key1, string $key2, bool $len = false, bool $idx = false, int $minMatchLen = 0, bool $withMatchLen = false)
|
||||
* @method $this lindex($key, $index)
|
||||
* @method $this linsert($key, $whence, $pivot, $value)
|
||||
* @method $this llen($key)
|
||||
* @method $this lmove(string $source, string $destination, string $where, string $to)
|
||||
* @method $this lmpop(array $keys, string $modifier = 'left', int $count = 1)
|
||||
* @method $this lpop($key)
|
||||
* @method $this lpush($key, array $values)
|
||||
* @method $this lpushx($key, array $values)
|
||||
* @method $this lrange($key, $start, $stop)
|
||||
* @method $this lrem($key, $count, $value)
|
||||
* @method $this lset($key, $index, $value)
|
||||
* @method $this ltrim($key, $start, $stop)
|
||||
* @method $this rpop($key)
|
||||
* @method $this rpoplpush($source, $destination)
|
||||
* @method $this rpush($key, array $values)
|
||||
* @method $this rpushx($key, array $values)
|
||||
* @method $this sadd($key, array $members)
|
||||
* @method $this scard($key)
|
||||
* @method $this sdiff(array|string $keys)
|
||||
* @method $this sdiffstore($destination, array|string $keys)
|
||||
* @method $this sinter(array|string $keys)
|
||||
* @method $this sintercard(array $keys, int $limit = 0)
|
||||
* @method $this sinterstore($destination, array|string $keys)
|
||||
* @method $this sismember($key, $member)
|
||||
* @method $this smembers($key)
|
||||
* @method $this smismember(string $key, string ...$members)
|
||||
* @method $this smove($source, $destination, $member)
|
||||
* @method $this spop($key, $count = null)
|
||||
* @method $this srandmember($key, $count = null)
|
||||
* @method $this srem($key, $member)
|
||||
* @method $this sscan($key, $cursor, array $options = null)
|
||||
* @method $this sunion(array|string $keys)
|
||||
* @method $this sunionstore($destination, array|string $keys)
|
||||
* @method $this tdigestadd(string $key, float ...$value)
|
||||
* @method $this tdigestbyrank(string $key, int ...$rank)
|
||||
* @method $this tdigestbyrevrank(string $key, int ...$reverseRank)
|
||||
* @method $this tdigestcdf(string $key, int ...$value)
|
||||
* @method $this tdigestcreate(string $key, int $compression = 0)
|
||||
* @method $this tdigestinfo(string $key)
|
||||
* @method $this tdigestmax(string $key)
|
||||
* @method $this tdigestmerge(string $destinationKey, array $sourceKeys, int $compression = 0, bool $override = false)
|
||||
* @method $this tdigestquantile(string $key, float ...$quantile)
|
||||
* @method $this tdigestmin(string $key)
|
||||
* @method $this tdigestrank(string $key, ...$value)
|
||||
* @method $this tdigestreset(string $key)
|
||||
* @method $this tdigestrevrank(string $key, float ...$value)
|
||||
* @method $this tdigesttrimmed_mean(string $key, float $lowCutQuantile, float $highCutQuantile)
|
||||
* @method $this topkadd(string $key, ...$items)
|
||||
* @method $this topkincrby(string $key, ...$itemIncrement)
|
||||
* @method $this topkinfo(string $key)
|
||||
* @method $this topklist(string $key, bool $withCount = false)
|
||||
* @method $this topkquery(string $key, ...$items)
|
||||
* @method $this topkreserve(string $key, int $topK, int $width = 8, int $depth = 7, float $decay = 0.9)
|
||||
* @method $this tsadd(string $key, int $timestamp, float $value, ?AddArguments $arguments = null)
|
||||
* @method $this tsalter(string $key, ?TSAlterArguments $arguments = null)
|
||||
* @method $this tscreate(string $key, ?TSCreateArguments $arguments = null)
|
||||
* @method $this tscreaterule(string $sourceKey, string $destKey, string $aggregator, int $bucketDuration, int $alignTimestamp = 0)
|
||||
* @method $this tsdecrby(string $key, float $value, ?DecrByArguments $arguments = null)
|
||||
* @method $this tsdel(string $key, int $fromTimestamp, int $toTimestamp)
|
||||
* @method $this tsdeleterule(string $sourceKey, string $destKey)
|
||||
* @method $this tsget(string $key, GetArguments $arguments = null)
|
||||
* @method $this tsincrby(string $key, float $value, ?IncrByArguments $arguments = null)
|
||||
* @method $this tsinfo(string $key, ?InfoArguments $arguments = null)
|
||||
* @method $this tsmadd(mixed ...$keyTimestampValue)
|
||||
* @method $this tsmget(MGetArguments $arguments, string ...$filterExpression)
|
||||
* @method $this tsmrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
|
||||
* @method $this tsmrevrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
|
||||
* @method $this tsqueryindex(string ...$filterExpression)
|
||||
* @method $this tsrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
|
||||
* @method $this tsrevrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
|
||||
* @method $this zadd($key, array $membersAndScoresDictionary)
|
||||
* @method $this zcard($key)
|
||||
* @method $this zcount($key, $min, $max)
|
||||
* @method $this zdiff(array $keys, bool $withScores = false)
|
||||
* @method $this zdiffstore(string $destination, array $keys)
|
||||
* @method $this zincrby($key, $increment, $member)
|
||||
* @method $this zintercard(array $keys, int $limit = 0)
|
||||
* @method $this zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
|
||||
* @method $this zinter(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
|
||||
* @method $this zmpop(array $keys, string $modifier = 'min', int $count = 1)
|
||||
* @method $this zmscore(string $key, string ...$member)
|
||||
* @method $this zrandmember(string $key, int $count = 1, bool $withScores = false)
|
||||
* @method $this zrange($key, $start, $stop, array $options = null)
|
||||
* @method $this zrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method $this zrangestore(string $destination, string $source, int|string $min, string|int $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0)
|
||||
* @method $this zrank($key, $member)
|
||||
* @method $this zrem($key, $member)
|
||||
* @method $this zremrangebyrank($key, $start, $stop)
|
||||
* @method $this zremrangebyscore($key, $min, $max)
|
||||
* @method $this zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
|
||||
* @method $this zrevrank($key, $member)
|
||||
* @method $this zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
|
||||
* @method $this zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
|
||||
* @method $this zscore($key, $member)
|
||||
* @method $this zscan($key, $cursor, array $options = null)
|
||||
* @method $this zrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method $this zrevrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method $this zremrangebylex($key, $min, $max)
|
||||
* @method $this zlexcount($key, $min, $max)
|
||||
* @method $this pexpiretime(string $key)
|
||||
* @method $this pfadd($key, array $elements)
|
||||
* @method $this pfmerge($destinationKey, array|string $sourceKeys)
|
||||
* @method $this pfcount(array|string $keys)
|
||||
* @method $this pubsub($subcommand, $argument)
|
||||
* @method $this publish($channel, $message)
|
||||
* @method $this discard()
|
||||
* @method $this exec()
|
||||
* @method $this multi()
|
||||
* @method $this unwatch()
|
||||
* @method $this waitaof(int $numLocal, int $numReplicas, int $timeout)
|
||||
* @method $this watch($key)
|
||||
* @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method $this eval_ro(string $script, array $keys, ...$argument)
|
||||
* @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method $this evalsha_ro(string $sha1, array $keys, ...$argument)
|
||||
* @method $this script($subcommand, $argument = null)
|
||||
* @method $this shutdown(bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false)
|
||||
* @method $this auth($password)
|
||||
* @method $this echo($message)
|
||||
* @method $this ping($message = null)
|
||||
* @method $this select($database)
|
||||
* @method $this bgrewriteaof()
|
||||
* @method $this bgsave()
|
||||
* @method $this client($subcommand, $argument = null)
|
||||
* @method $this config($subcommand, $argument = null)
|
||||
* @method $this dbsize()
|
||||
* @method $this flushall()
|
||||
* @method $this flushdb()
|
||||
* @method $this info($section = null)
|
||||
* @method $this lastsave()
|
||||
* @method $this save()
|
||||
* @method $this slaveof($host, $port)
|
||||
* @method $this slowlog($subcommand, $argument = null)
|
||||
* @method $this time()
|
||||
* @method $this command()
|
||||
* @method $this geoadd($key, $longitude, $latitude, $member)
|
||||
* @method $this geohash($key, array $members)
|
||||
* @method $this geopos($key, array $members)
|
||||
* @method $this geodist($key, $member1, $member2, $unit = null)
|
||||
* @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
|
||||
* @method $this geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
|
||||
* @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
|
||||
*
|
||||
* Container commands
|
||||
* @property CLUSTER $cluster
|
||||
* @property FunctionContainer $function
|
||||
* @property FTCONFIG $ftconfig
|
||||
* @property FTCURSOR $ftcursor
|
||||
* @property JSONDEBUG $jsondebug
|
||||
* @property ACL $acl
|
||||
*/
|
||||
interface ClientContextInterface
|
||||
{
|
||||
/**
|
||||
* Sends the specified command instance to Redis.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeCommand(CommandInterface $command);
|
||||
|
||||
/**
|
||||
* Sends the specified command with its arguments to Redis.
|
||||
*
|
||||
* @param string $method Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $arguments);
|
||||
|
||||
/**
|
||||
* Starts the execution of the context.
|
||||
*
|
||||
* @param mixed $callable Optional callback for execution.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function execute($callable = null);
|
||||
}
|
||||
20
plugins/cache-redis/Predis/ClientException.php
Normal file
20
plugins/cache-redis/Predis/ClientException.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
/**
|
||||
* Exception class that identifies client-side errors.
|
||||
*/
|
||||
class ClientException extends PredisException
|
||||
{
|
||||
}
|
||||
431
plugins/cache-redis/Predis/ClientInterface.php
Normal file
431
plugins/cache-redis/Predis/ClientInterface.php
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
use Predis\Command\Argument\Geospatial\ByInterface;
|
||||
use Predis\Command\Argument\Geospatial\FromInterface;
|
||||
use Predis\Command\Argument\Search\AggregateArguments;
|
||||
use Predis\Command\Argument\Search\AlterArguments;
|
||||
use Predis\Command\Argument\Search\CreateArguments;
|
||||
use Predis\Command\Argument\Search\DropArguments;
|
||||
use Predis\Command\Argument\Search\ExplainArguments;
|
||||
use Predis\Command\Argument\Search\ProfileArguments;
|
||||
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
|
||||
use Predis\Command\Argument\Search\SearchArguments;
|
||||
use Predis\Command\Argument\Search\SugAddArguments;
|
||||
use Predis\Command\Argument\Search\SugGetArguments;
|
||||
use Predis\Command\Argument\Search\SynUpdateArguments;
|
||||
use Predis\Command\Argument\Server\LimitOffsetCount;
|
||||
use Predis\Command\Argument\Server\To;
|
||||
use Predis\Command\Argument\TimeSeries\AddArguments;
|
||||
use Predis\Command\Argument\TimeSeries\AlterArguments as TSAlterArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments as TSCreateArguments;
|
||||
use Predis\Command\Argument\TimeSeries\DecrByArguments;
|
||||
use Predis\Command\Argument\TimeSeries\GetArguments;
|
||||
use Predis\Command\Argument\TimeSeries\IncrByArguments;
|
||||
use Predis\Command\Argument\TimeSeries\InfoArguments;
|
||||
use Predis\Command\Argument\TimeSeries\MGetArguments;
|
||||
use Predis\Command\Argument\TimeSeries\MRangeArguments;
|
||||
use Predis\Command\Argument\TimeSeries\RangeArguments;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\FactoryInterface;
|
||||
use Predis\Command\Redis\Container\ACL;
|
||||
use Predis\Command\Redis\Container\CLUSTER;
|
||||
use Predis\Command\Redis\Container\FunctionContainer;
|
||||
use Predis\Command\Redis\Container\Json\JSONDEBUG;
|
||||
use Predis\Command\Redis\Container\Search\FTCONFIG;
|
||||
use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
use Predis\Configuration\OptionsInterface;
|
||||
use Predis\Connection\ConnectionInterface;
|
||||
use Predis\Response\Status;
|
||||
|
||||
/**
|
||||
* Interface defining a client able to execute commands against Redis.
|
||||
*
|
||||
* All the commands exposed by the client generally have the same signature as
|
||||
* described by the Redis documentation, but some of them offer an additional
|
||||
* and more friendly interface to ease programming which is described in the
|
||||
* following list of methods:
|
||||
*
|
||||
* @method int copy(string $source, string $destination, int $db = -1, bool $replace = false)
|
||||
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method string|null dump(string $key)
|
||||
* @method int exists(string $key)
|
||||
* @method int expire(string $key, int $seconds, string $expireOption = '')
|
||||
* @method int expireat(string $key, int $timestamp, string $expireOption = '')
|
||||
* @method int expiretime(string $key)
|
||||
* @method array keys(string $pattern)
|
||||
* @method int move(string $key, int $db)
|
||||
* @method mixed object($subcommand, string $key)
|
||||
* @method int persist(string $key)
|
||||
* @method int pexpire(string $key, int $milliseconds)
|
||||
* @method int pexpireat(string $key, int $timestamp)
|
||||
* @method int pttl(string $key)
|
||||
* @method string|null randomkey()
|
||||
* @method mixed rename(string $key, string $target)
|
||||
* @method int renamenx(string $key, string $target)
|
||||
* @method array scan($cursor, array $options = null)
|
||||
* @method array sort(string $key, array $options = null)
|
||||
* @method array sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false)
|
||||
* @method int ttl(string $key)
|
||||
* @method mixed type(string $key)
|
||||
* @method int append(string $key, $value)
|
||||
* @method int bfadd(string $key, $item)
|
||||
* @method int bfexists(string $key, $item)
|
||||
* @method array bfinfo(string $key, string $modifier = '')
|
||||
* @method array bfinsert(string $key, int $capacity = -1, float $error = -1, int $expansion = -1, bool $noCreate = false, bool $nonScaling = false, string ...$item)
|
||||
* @method Status bfloadchunk(string $key, int $iterator, $data)
|
||||
* @method array bfmadd(string $key, ...$item)
|
||||
* @method array bfmexists(string $key, ...$item)
|
||||
* @method Status bfreserve(string $key, float $errorRate, int $capacity, int $expansion = -1, bool $nonScaling = false)
|
||||
* @method array bfscandump(string $key, int $iterator)
|
||||
* @method int bitcount(string $key, $start = null, $end = null, string $index = 'byte')
|
||||
* @method int bitop($operation, $destkey, $key)
|
||||
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
|
||||
* @method int bitpos(string $key, $bit, $start = null, $end = null, string $index = 'byte')
|
||||
* @method array blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
|
||||
* @method array bzpopmax(array $keys, int $timeout)
|
||||
* @method array bzpopmin(array $keys, int $timeout)
|
||||
* @method array bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
|
||||
* @method int cfadd(string $key, $item)
|
||||
* @method int cfaddnx(string $key, $item)
|
||||
* @method int cfcount(string $key, $item)
|
||||
* @method int cfdel(string $key, $item)
|
||||
* @method int cfexists(string $key, $item)
|
||||
* @method Status cfloadchunk(string $key, int $iterator, $data)
|
||||
* @method int cfmexists(string $key, ...$item)
|
||||
* @method array cfinfo(string $key)
|
||||
* @method array cfinsert(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
|
||||
* @method array cfinsertnx(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
|
||||
* @method Status cfreserve(string $key, int $capacity, int $bucketSize = -1, int $maxIterations = -1, int $expansion = -1)
|
||||
* @method array cfscandump(string $key, int $iterator)
|
||||
* @method array cmsincrby(string $key, string|int...$itemIncrementDictionary)
|
||||
* @method array cmsinfo(string $key)
|
||||
* @method Status cmsinitbydim(string $key, int $width, int $depth)
|
||||
* @method Status cmsinitbyprob(string $key, float $errorRate, float $probability)
|
||||
* @method Status cmsmerge(string $destination, array $sources, array $weights = [])
|
||||
* @method array cmsquery(string $key, string ...$item)
|
||||
* @method int decr(string $key)
|
||||
* @method int decrby(string $key, int $decrement)
|
||||
* @method Status failover(?To $to = null, bool $abort = false, int $timeout = -1)
|
||||
* @method mixed fcall(string $function, array $keys, ...$args)
|
||||
* @method mixed fcall_ro(string $function, array $keys, ...$args)
|
||||
* @method array ftaggregate(string $index, string $query, ?AggregateArguments $arguments = null)
|
||||
* @method Status ftaliasadd(string $alias, string $index)
|
||||
* @method Status ftaliasdel(string $alias)
|
||||
* @method Status ftaliasupdate(string $alias, string $index)
|
||||
* @method Status ftalter(string $index, FieldInterface[] $schema, ?AlterArguments $arguments = null)
|
||||
* @method Status ftcreate(string $index, FieldInterface[] $schema, ?CreateArguments $arguments = null)
|
||||
* @method int ftdictadd(string $dict, ...$term)
|
||||
* @method int ftdictdel(string $dict, ...$term)
|
||||
* @method array ftdictdump(string $dict)
|
||||
* @method Status ftdropindex(string $index, ?DropArguments $arguments = null)
|
||||
* @method string ftexplain(string $index, string $query, ?ExplainArguments $arguments = null)
|
||||
* @method array ftinfo(string $index)
|
||||
* @method array ftprofile(string $index, ProfileArguments $arguments)
|
||||
* @method array ftsearch(string $index, string $query, ?SearchArguments $arguments = null)
|
||||
* @method array ftspellcheck(string $index, string $query, ?SearchArguments $arguments = null)
|
||||
* @method int ftsugadd(string $key, string $string, float $score, ?SugAddArguments $arguments = null)
|
||||
* @method int ftsugdel(string $key, string $string)
|
||||
* @method array ftsugget(string $key, string $prefix, ?SugGetArguments $arguments = null)
|
||||
* @method int ftsuglen(string $key)
|
||||
* @method array ftsyndump(string $index)
|
||||
* @method Status ftsynupdate(string $index, string $synonymGroupId, ?SynUpdateArguments $arguments = null, string ...$terms)
|
||||
* @method array fttagvals(string $index, string $fieldName)
|
||||
* @method string|null get(string $key)
|
||||
* @method int getbit(string $key, $offset)
|
||||
* @method int|null getex(string $key, $modifier = '', $value = false)
|
||||
* @method string getrange(string $key, $start, $end)
|
||||
* @method string getdel(string $key)
|
||||
* @method string|null getset(string $key, $value)
|
||||
* @method int incr(string $key)
|
||||
* @method int incrby(string $key, int $increment)
|
||||
* @method string incrbyfloat(string $key, int|float $increment)
|
||||
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method Status psetex(string $key, $milliseconds, $value)
|
||||
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit(string $key, $offset, $value)
|
||||
* @method Status setex(string $key, $seconds, $value)
|
||||
* @method int setnx(string $key, $value)
|
||||
* @method int setrange(string $key, $offset, $value)
|
||||
* @method int strlen(string $key)
|
||||
* @method int hdel(string $key, array $fields)
|
||||
* @method int hexists(string $key, string $field)
|
||||
* @method string|null hget(string $key, string $field)
|
||||
* @method array hgetall(string $key)
|
||||
* @method int hincrby(string $key, string $field, int $increment)
|
||||
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
|
||||
* @method array hkeys(string $key)
|
||||
* @method int hlen(string $key)
|
||||
* @method array hmget(string $key, array $fields)
|
||||
* @method mixed hmset(string $key, array $dictionary)
|
||||
* @method array hrandfield(string $key, int $count = 1, bool $withValues = false)
|
||||
* @method array hscan(string $key, $cursor, array $options = null)
|
||||
* @method int hset(string $key, string $field, string $value)
|
||||
* @method int hsetnx(string $key, string $field, string $value)
|
||||
* @method array hvals(string $key)
|
||||
* @method int hstrlen(string $key, string $field)
|
||||
* @method array jsonarrappend(string $key, string $path = '$', ...$value)
|
||||
* @method array jsonarrindex(string $key, string $path, string $value, int $start = 0, int $stop = 0)
|
||||
* @method array jsonarrinsert(string $key, string $path, int $index, string ...$value)
|
||||
* @method array jsonarrlen(string $key, string $path = '$')
|
||||
* @method array jsonarrpop(string $key, string $path = '$', int $index = -1)
|
||||
* @method int jsonclear(string $key, string $path = '$')
|
||||
* @method array jsonarrtrim(string $key, string $path, int $start, int $stop)
|
||||
* @method int jsondel(string $key, string $path = '$')
|
||||
* @method int jsonforget(string $key, string $path = '$')
|
||||
* @method string jsonget(string $key, string $indent = '', string $newline = '', string $space = '', string ...$paths)
|
||||
* @method string jsonnumincrby(string $key, string $path, int $value)
|
||||
* @method Status jsonmerge(string $key, string $path, string $value)
|
||||
* @method array jsonmget(array $keys, string $path)
|
||||
* @method Status jsonmset(string ...$keyPathValue)
|
||||
* @method array jsonobjkeys(string $key, string $path = '$')
|
||||
* @method array jsonobjlen(string $key, string $path = '$')
|
||||
* @method array jsonresp(string $key, string $path = '$')
|
||||
* @method string jsonset(string $key, string $path, string $value, ?string $subcommand = null)
|
||||
* @method array jsonstrappend(string $key, string $path, string $value)
|
||||
* @method array jsonstrlen(string $key, string $path = '$')
|
||||
* @method array jsontoggle(string $key, string $path)
|
||||
* @method array jsontype(string $key, string $path = '$')
|
||||
* @method string blmove(string $source, string $destination, string $where, string $to, int $timeout)
|
||||
* @method array|null blpop(array|string $keys, int|float $timeout)
|
||||
* @method array|null brpop(array|string $keys, int|float $timeout)
|
||||
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
|
||||
* @method mixed lcs(string $key1, string $key2, bool $len = false, bool $idx = false, int $minMatchLen = 0, bool $withMatchLen = false)
|
||||
* @method string|null lindex(string $key, int $index)
|
||||
* @method int linsert(string $key, $whence, $pivot, $value)
|
||||
* @method int llen(string $key)
|
||||
* @method string lmove(string $source, string $destination, string $where, string $to)
|
||||
* @method array|null lmpop(array $keys, string $modifier = 'left', int $count = 1)
|
||||
* @method string|null lpop(string $key)
|
||||
* @method int lpush(string $key, array $values)
|
||||
* @method int lpushx(string $key, array $values)
|
||||
* @method string[] lrange(string $key, int $start, int $stop)
|
||||
* @method int lrem(string $key, int $count, string $value)
|
||||
* @method mixed lset(string $key, int $index, string $value)
|
||||
* @method mixed ltrim(string $key, int $start, int $stop)
|
||||
* @method string|null rpop(string $key)
|
||||
* @method string|null rpoplpush(string $source, string $destination)
|
||||
* @method int rpush(string $key, array $values)
|
||||
* @method int rpushx(string $key, array $values)
|
||||
* @method int sadd(string $key, array $members)
|
||||
* @method int scard(string $key)
|
||||
* @method string[] sdiff(array|string $keys)
|
||||
* @method int sdiffstore(string $destination, array|string $keys)
|
||||
* @method string[] sinter(array|string $keys)
|
||||
* @method int sintercard(array $keys, int $limit = 0)
|
||||
* @method int sinterstore(string $destination, array|string $keys)
|
||||
* @method int sismember(string $key, string $member)
|
||||
* @method string[] smembers(string $key)
|
||||
* @method array smismember(string $key, string ...$members)
|
||||
* @method int smove(string $source, string $destination, string $member)
|
||||
* @method string|array|null spop(string $key, int $count = null)
|
||||
* @method string|null srandmember(string $key, int $count = null)
|
||||
* @method int srem(string $key, array|string $member)
|
||||
* @method array sscan(string $key, int $cursor, array $options = null)
|
||||
* @method string[] sunion(array|string $keys)
|
||||
* @method int sunionstore(string $destination, array|string $keys)
|
||||
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method Status tdigestadd(string $key, float ...$value)
|
||||
* @method array tdigestbyrank(string $key, int ...$rank)
|
||||
* @method array tdigestbyrevrank(string $key, int ...$reverseRank)
|
||||
* @method array tdigestcdf(string $key, int ...$value)
|
||||
* @method Status tdigestcreate(string $key, int $compression = 0)
|
||||
* @method array tdigestinfo(string $key)
|
||||
* @method string tdigestmax(string $key)
|
||||
* @method Status tdigestmerge(string $destinationKey, array $sourceKeys, int $compression = 0, bool $override = false)
|
||||
* @method string[] tdigestquantile(string $key, float ...$quantile)
|
||||
* @method string tdigestmin(string $key)
|
||||
* @method array tdigestrank(string $key, float ...$value)
|
||||
* @method Status tdigestreset(string $key)
|
||||
* @method array tdigestrevrank(string $key, float ...$value)
|
||||
* @method string tdigesttrimmed_mean(string $key, float $lowCutQuantile, float $highCutQuantile)
|
||||
* @method array topkadd(string $key, ...$items)
|
||||
* @method array topkincrby(string $key, ...$itemIncrement)
|
||||
* @method array topkinfo(string $key)
|
||||
* @method array topklist(string $key, bool $withCount = false)
|
||||
* @method array topkquery(string $key, ...$items)
|
||||
* @method Status topkreserve(string $key, int $topK, int $width = 8, int $depth = 7, float $decay = 0.9)
|
||||
* @method int tsadd(string $key, int $timestamp, float $value, ?AddArguments $arguments = null)
|
||||
* @method Status tsalter(string $key, ?TSAlterArguments $arguments = null)
|
||||
* @method Status tscreate(string $key, ?TSCreateArguments $arguments = null)
|
||||
* @method Status tscreaterule(string $sourceKey, string $destKey, string $aggregator, int $bucketDuration, int $alignTimestamp = 0)
|
||||
* @method int tsdecrby(string $key, float $value, ?DecrByArguments $arguments = null)
|
||||
* @method int tsdel(string $key, int $fromTimestamp, int $toTimestamp)
|
||||
* @method Status tsdeleterule(string $sourceKey, string $destKey)
|
||||
* @method array tsget(string $key, GetArguments $arguments = null)
|
||||
* @method int tsincrby(string $key, float $value, ?IncrByArguments $arguments = null)
|
||||
* @method array tsinfo(string $key, ?InfoArguments $arguments = null)
|
||||
* @method array tsmadd(mixed ...$keyTimestampValue)
|
||||
* @method array tsmget(MGetArguments $arguments, string ...$filterExpression)
|
||||
* @method array tsmrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
|
||||
* @method array tsmrevrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
|
||||
* @method array tsqueryindex(string ...$filterExpression)
|
||||
* @method array tsrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
|
||||
* @method array tsrevrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
|
||||
* @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null)
|
||||
* @method int xdel(string $key, string ...$id)
|
||||
* @method int xlen(string $key)
|
||||
* @method array xrevrange(string $key, string $end, string $start, ?int $count = null)
|
||||
* @method array xrange(string $key, string $start, string $end, ?int $count = null)
|
||||
* @method string xtrim(string $key, array|string $strategy, string $threshold, array $options = null)
|
||||
* @method int zadd(string $key, array $membersAndScoresDictionary)
|
||||
* @method int zcard(string $key)
|
||||
* @method string zcount(string $key, int|string $min, int|string $max)
|
||||
* @method array zdiff(array $keys, bool $withScores = false)
|
||||
* @method int zdiffstore(string $destination, array $keys)
|
||||
* @method string zincrby(string $key, int $increment, string $member)
|
||||
* @method int zintercard(array $keys, int $limit = 0)
|
||||
* @method int zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
|
||||
* @method array zinter(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
|
||||
* @method array zmpop(array $keys, string $modifier = 'min', int $count = 1)
|
||||
* @method array zmscore(string $key, string ...$member)
|
||||
* @method array zpopmin(string $key, int $count = 1)
|
||||
* @method array zpopmax(string $key, int $count = 1)
|
||||
* @method mixed zrandmember(string $key, int $count = 1, bool $withScores = false)
|
||||
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
|
||||
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
|
||||
* @method int zrangestore(string $destination, string $source, int|string $min, int|string $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0)
|
||||
* @method int|null zrank(string $key, string $member)
|
||||
* @method int zrem(string $key, string ...$member)
|
||||
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
|
||||
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
|
||||
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
|
||||
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
|
||||
* @method int|null zrevrank(string $key, string $member)
|
||||
* @method array zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
|
||||
* @method int zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
|
||||
* @method string|null zscore(string $key, string $member)
|
||||
* @method array zscan(string $key, int $cursor, array $options = null)
|
||||
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
|
||||
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
|
||||
* @method int zremrangebylex(string $key, string $min, string $max)
|
||||
* @method int zlexcount(string $key, string $min, string $max)
|
||||
* @method int pexpiretime(string $key)
|
||||
* @method int pfadd(string $key, array $elements)
|
||||
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
|
||||
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed discard()
|
||||
* @method array|null exec()
|
||||
* @method mixed multi()
|
||||
* @method mixed unwatch()
|
||||
* @method array waitaof(int $numLocal, int $numReplicas, int $timeout)
|
||||
* @method mixed watch(string $key)
|
||||
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
|
||||
* @method mixed eval_ro(string $script, array $keys, ...$argument)
|
||||
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
|
||||
* @method mixed evalsha_ro(string $sha1, array $keys, ...$argument)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method Status shutdown(bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false)
|
||||
* @method mixed auth(string $password)
|
||||
* @method string echo(string $message)
|
||||
* @method mixed ping(string $message = null)
|
||||
* @method mixed select(int $database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof(string $host, int $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
* @method int geoadd(string $key, $longitude, $latitude, $member)
|
||||
* @method array geohash(string $key, array $members)
|
||||
* @method array geopos(string $key, array $members)
|
||||
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
|
||||
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
|
||||
* @method array geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
|
||||
* @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
|
||||
*
|
||||
* Container commands
|
||||
* @property CLUSTER $cluster
|
||||
* @property FunctionContainer $function
|
||||
* @property FTCONFIG $ftconfig
|
||||
* @property FTCURSOR $ftcursor
|
||||
* @property JSONDEBUG $jsondebug
|
||||
* @property ACL $acl
|
||||
*/
|
||||
interface ClientInterface
|
||||
{
|
||||
/**
|
||||
* Returns the command factory used by the client.
|
||||
*
|
||||
* @return FactoryInterface
|
||||
*/
|
||||
public function getCommandFactory();
|
||||
|
||||
/**
|
||||
* Returns the client options specified upon initialization.
|
||||
*
|
||||
* @return OptionsInterface
|
||||
*/
|
||||
public function getOptions();
|
||||
|
||||
/**
|
||||
* Opens the underlying connection to the server.
|
||||
*/
|
||||
public function connect();
|
||||
|
||||
/**
|
||||
* Closes the underlying connection from the server.
|
||||
*/
|
||||
public function disconnect();
|
||||
|
||||
/**
|
||||
* Returns the underlying connection instance.
|
||||
*
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
public function getConnection();
|
||||
|
||||
/**
|
||||
* Creates a new instance of the specified Redis command.
|
||||
*
|
||||
* @param string $method Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
*
|
||||
* @return CommandInterface
|
||||
*/
|
||||
public function createCommand($method, $arguments = []);
|
||||
|
||||
/**
|
||||
* Executes the specified Redis command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeCommand(CommandInterface $command);
|
||||
|
||||
/**
|
||||
* Creates a Redis command with the specified arguments and sends a request
|
||||
* to the server.
|
||||
*
|
||||
* @param string $method Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $arguments);
|
||||
}
|
||||
493
plugins/cache-redis/Predis/Cluster/ClusterStrategy.php
Normal file
493
plugins/cache-redis/Predis/Cluster/ClusterStrategy.php
Normal file
|
|
@ -0,0 +1,493 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\ScriptCommand;
|
||||
|
||||
/**
|
||||
* Common class implementing the logic needed to support clustering strategies.
|
||||
*/
|
||||
abstract class ClusterStrategy implements StrategyInterface
|
||||
{
|
||||
protected $commands;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->commands = $this->getDefaultCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default map of supported commands with their handlers.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaultCommands()
|
||||
{
|
||||
$getKeyFromFirstArgument = [$this, 'getKeyFromFirstArgument'];
|
||||
$getKeyFromAllArguments = [$this, 'getKeyFromAllArguments'];
|
||||
|
||||
return [
|
||||
/* commands operating on the key space */
|
||||
'EXISTS' => $getKeyFromAllArguments,
|
||||
'DEL' => $getKeyFromAllArguments,
|
||||
'TYPE' => $getKeyFromFirstArgument,
|
||||
'EXPIRE' => $getKeyFromFirstArgument,
|
||||
'EXPIREAT' => $getKeyFromFirstArgument,
|
||||
'PERSIST' => $getKeyFromFirstArgument,
|
||||
'PEXPIRE' => $getKeyFromFirstArgument,
|
||||
'PEXPIREAT' => $getKeyFromFirstArgument,
|
||||
'TTL' => $getKeyFromFirstArgument,
|
||||
'PTTL' => $getKeyFromFirstArgument,
|
||||
'SORT' => [$this, 'getKeyFromSortCommand'],
|
||||
'DUMP' => $getKeyFromFirstArgument,
|
||||
'RESTORE' => $getKeyFromFirstArgument,
|
||||
'FLUSHDB' => [$this, 'getFakeKey'],
|
||||
|
||||
/* commands operating on string values */
|
||||
'APPEND' => $getKeyFromFirstArgument,
|
||||
'DECR' => $getKeyFromFirstArgument,
|
||||
'DECRBY' => $getKeyFromFirstArgument,
|
||||
'GET' => $getKeyFromFirstArgument,
|
||||
'GETBIT' => $getKeyFromFirstArgument,
|
||||
'MGET' => $getKeyFromAllArguments,
|
||||
'SET' => $getKeyFromFirstArgument,
|
||||
'GETRANGE' => $getKeyFromFirstArgument,
|
||||
'GETSET' => $getKeyFromFirstArgument,
|
||||
'INCR' => $getKeyFromFirstArgument,
|
||||
'INCRBY' => $getKeyFromFirstArgument,
|
||||
'INCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'SETBIT' => $getKeyFromFirstArgument,
|
||||
'SETEX' => $getKeyFromFirstArgument,
|
||||
'MSET' => [$this, 'getKeyFromInterleavedArguments'],
|
||||
'MSETNX' => [$this, 'getKeyFromInterleavedArguments'],
|
||||
'SETNX' => $getKeyFromFirstArgument,
|
||||
'SETRANGE' => $getKeyFromFirstArgument,
|
||||
'STRLEN' => $getKeyFromFirstArgument,
|
||||
'SUBSTR' => $getKeyFromFirstArgument,
|
||||
'BITOP' => [$this, 'getKeyFromBitOp'],
|
||||
'BITCOUNT' => $getKeyFromFirstArgument,
|
||||
'BITFIELD' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on lists */
|
||||
'LINSERT' => $getKeyFromFirstArgument,
|
||||
'LINDEX' => $getKeyFromFirstArgument,
|
||||
'LLEN' => $getKeyFromFirstArgument,
|
||||
'LPOP' => $getKeyFromFirstArgument,
|
||||
'RPOP' => $getKeyFromFirstArgument,
|
||||
'RPOPLPUSH' => $getKeyFromAllArguments,
|
||||
'BLPOP' => [$this, 'getKeyFromBlockingListCommands'],
|
||||
'BRPOP' => [$this, 'getKeyFromBlockingListCommands'],
|
||||
'BRPOPLPUSH' => [$this, 'getKeyFromBlockingListCommands'],
|
||||
'LPUSH' => $getKeyFromFirstArgument,
|
||||
'LPUSHX' => $getKeyFromFirstArgument,
|
||||
'RPUSH' => $getKeyFromFirstArgument,
|
||||
'RPUSHX' => $getKeyFromFirstArgument,
|
||||
'LRANGE' => $getKeyFromFirstArgument,
|
||||
'LREM' => $getKeyFromFirstArgument,
|
||||
'LSET' => $getKeyFromFirstArgument,
|
||||
'LTRIM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sets */
|
||||
'SADD' => $getKeyFromFirstArgument,
|
||||
'SCARD' => $getKeyFromFirstArgument,
|
||||
'SDIFF' => $getKeyFromAllArguments,
|
||||
'SDIFFSTORE' => $getKeyFromAllArguments,
|
||||
'SINTER' => $getKeyFromAllArguments,
|
||||
'SINTERSTORE' => $getKeyFromAllArguments,
|
||||
'SUNION' => $getKeyFromAllArguments,
|
||||
'SUNIONSTORE' => $getKeyFromAllArguments,
|
||||
'SISMEMBER' => $getKeyFromFirstArgument,
|
||||
'SMEMBERS' => $getKeyFromFirstArgument,
|
||||
'SSCAN' => $getKeyFromFirstArgument,
|
||||
'SPOP' => $getKeyFromFirstArgument,
|
||||
'SRANDMEMBER' => $getKeyFromFirstArgument,
|
||||
'SREM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'ZADD' => $getKeyFromFirstArgument,
|
||||
'ZCARD' => $getKeyFromFirstArgument,
|
||||
'ZCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZINCRBY' => $getKeyFromFirstArgument,
|
||||
'ZINTERSTORE' => [$this, 'getKeyFromZsetAggregationCommands'],
|
||||
'ZRANGE' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZRANK' => $getKeyFromFirstArgument,
|
||||
'ZREM' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANK' => $getKeyFromFirstArgument,
|
||||
'ZSCORE' => $getKeyFromFirstArgument,
|
||||
'ZUNIONSTORE' => [$this, 'getKeyFromZsetAggregationCommands'],
|
||||
'ZSCAN' => $getKeyFromFirstArgument,
|
||||
'ZLEXCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on hashes */
|
||||
'HDEL' => $getKeyFromFirstArgument,
|
||||
'HEXISTS' => $getKeyFromFirstArgument,
|
||||
'HGET' => $getKeyFromFirstArgument,
|
||||
'HGETALL' => $getKeyFromFirstArgument,
|
||||
'HMGET' => $getKeyFromFirstArgument,
|
||||
'HMSET' => $getKeyFromFirstArgument,
|
||||
'HINCRBY' => $getKeyFromFirstArgument,
|
||||
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'HKEYS' => $getKeyFromFirstArgument,
|
||||
'HLEN' => $getKeyFromFirstArgument,
|
||||
'HSET' => $getKeyFromFirstArgument,
|
||||
'HSETNX' => $getKeyFromFirstArgument,
|
||||
'HVALS' => $getKeyFromFirstArgument,
|
||||
'HSCAN' => $getKeyFromFirstArgument,
|
||||
'HSTRLEN' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'PFADD' => $getKeyFromFirstArgument,
|
||||
'PFCOUNT' => $getKeyFromAllArguments,
|
||||
'PFMERGE' => $getKeyFromAllArguments,
|
||||
|
||||
/* scripting */
|
||||
'EVAL' => [$this, 'getKeyFromScriptingCommands'],
|
||||
'EVALSHA' => [$this, 'getKeyFromScriptingCommands'],
|
||||
|
||||
/* server */
|
||||
'INFO' => [$this, 'getFakeKey'],
|
||||
|
||||
/* commands performing geospatial operations */
|
||||
'GEOADD' => $getKeyFromFirstArgument,
|
||||
'GEOHASH' => $getKeyFromFirstArgument,
|
||||
'GEOPOS' => $getKeyFromFirstArgument,
|
||||
'GEODIST' => $getKeyFromFirstArgument,
|
||||
'GEORADIUS' => [$this, 'getKeyFromGeoradiusCommands'],
|
||||
'GEORADIUSBYMEMBER' => [$this, 'getKeyFromGeoradiusCommands'],
|
||||
|
||||
/* cluster */
|
||||
'CLUSTER' => [$this, 'getFakeKey'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of IDs for the supported commands.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedCommands()
|
||||
{
|
||||
return array_keys($this->commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an handler for the specified command ID.
|
||||
*
|
||||
* The signature of the callback must have a single parameter of type
|
||||
* Predis\Command\CommandInterface.
|
||||
*
|
||||
* When the callback argument is omitted or NULL, the previously associated
|
||||
* handler for the specified command ID is removed.
|
||||
*
|
||||
* @param string $commandID Command ID.
|
||||
* @param mixed $callback A valid callable object, or NULL to unset the handler.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setCommandHandler($commandID, $callback = null)
|
||||
{
|
||||
$commandID = strtoupper($commandID);
|
||||
|
||||
if (!isset($callback)) {
|
||||
unset($this->commands[$commandID]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_callable($callback)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The argument must be a callable object or NULL.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->commands[$commandID] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fake key for commands with no key argument.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFakeKey(): string
|
||||
{
|
||||
return 'key';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from the first argument of a command instance.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyFromFirstArgument(CommandInterface $command)
|
||||
{
|
||||
return $command->getArgument(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from a command with multiple keys only when all keys in
|
||||
* the arguments array produce the same hash.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromAllArguments(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
if (!$this->checkSameSlotForKeys($arguments)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $arguments[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from a command with multiple keys only when all keys in
|
||||
* the arguments array produce the same hash.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromInterleavedArguments(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$keys = [];
|
||||
|
||||
for ($i = 0; $i < count($arguments); $i += 2) {
|
||||
$keys[] = $arguments[$i];
|
||||
}
|
||||
|
||||
if (!$this->checkSameSlotForKeys($keys)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $arguments[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from SORT command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromSortCommand(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$firstKey = $arguments[0];
|
||||
|
||||
if (1 === $argc = count($arguments)) {
|
||||
return $firstKey;
|
||||
}
|
||||
|
||||
$keys = [$firstKey];
|
||||
|
||||
for ($i = 1; $i < $argc; ++$i) {
|
||||
if (strtoupper($arguments[$i]) === 'STORE') {
|
||||
$keys[] = $arguments[++$i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->checkSameSlotForKeys($keys)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $firstKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from BLPOP and BRPOP commands.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromBlockingListCommands(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
if (!$this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $arguments[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from BITOP command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromBitOp(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
if (!$this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $arguments[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromGeoradiusCommands(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$argc = count($arguments);
|
||||
$startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
|
||||
|
||||
if ($argc > $startIndex) {
|
||||
$keys = [$arguments[0]];
|
||||
|
||||
for ($i = $startIndex; $i < $argc; ++$i) {
|
||||
$argument = strtoupper($arguments[$i]);
|
||||
if ($argument === 'STORE' || $argument === 'STOREDIST') {
|
||||
$keys[] = $arguments[++$i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->checkSameSlotForKeys($keys)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $arguments[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$keys = array_merge([$arguments[0]], array_slice($arguments, 2, $arguments[1]));
|
||||
|
||||
if (!$this->checkSameSlotForKeys($keys)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $arguments[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from EVAL and EVALSHA commands.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getKeyFromScriptingCommands(CommandInterface $command)
|
||||
{
|
||||
$keys = $command instanceof ScriptCommand
|
||||
? $command->getKeys()
|
||||
: array_slice($args = $command->getArguments(), 2, $args[1]);
|
||||
|
||||
if (!$keys || !$this->checkSameSlotForKeys($keys)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $keys[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSlot(CommandInterface $command)
|
||||
{
|
||||
$slot = $command->getSlot();
|
||||
|
||||
if (!isset($slot) && isset($this->commands[$cmdID = $command->getId()])) {
|
||||
$key = call_user_func($this->commands[$cmdID], $command);
|
||||
|
||||
if (isset($key)) {
|
||||
$slot = $this->getSlotByKey($key);
|
||||
$command->setSlot($slot);
|
||||
}
|
||||
}
|
||||
|
||||
return $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified array of keys will generate the same hash.
|
||||
*
|
||||
* @param array $keys Array of keys.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkSameSlotForKeys(array $keys)
|
||||
{
|
||||
if (!$count = count($keys)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentSlot = $this->getSlotByKey($keys[0]);
|
||||
|
||||
for ($i = 1; $i < $count; ++$i) {
|
||||
$nextSlot = $this->getSlotByKey($keys[$i]);
|
||||
|
||||
if ($currentSlot !== $nextSlot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentSlot = $nextSlot;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the hashable part of a key (delimited by "{...}"), or the
|
||||
* whole key if a key tag is not found in the string.
|
||||
*
|
||||
* @param string $key A key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function extractKeyTag($key)
|
||||
{
|
||||
if (false !== $start = strpos($key, '{')) {
|
||||
if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) {
|
||||
$key = substr($key, $start, $end - $start);
|
||||
}
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Distributor;
|
||||
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
|
||||
/**
|
||||
* A distributor implements the logic to automatically distribute keys among
|
||||
* several nodes for client-side sharding.
|
||||
*/
|
||||
interface DistributorInterface
|
||||
{
|
||||
/**
|
||||
* Adds a node to the distributor with an optional weight.
|
||||
*
|
||||
* @param mixed $node Node object.
|
||||
* @param int $weight Weight for the node.
|
||||
*/
|
||||
public function add($node, $weight = null);
|
||||
|
||||
/**
|
||||
* Removes a node from the distributor.
|
||||
*
|
||||
* @param mixed $node Node object.
|
||||
*/
|
||||
public function remove($node);
|
||||
|
||||
/**
|
||||
* Returns the corresponding slot of a node from the distributor using the
|
||||
* computed hash of a key.
|
||||
*
|
||||
* @param mixed $hash
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSlot($hash);
|
||||
|
||||
/**
|
||||
* Returns a node from the distributor using its assigned slot ID.
|
||||
*
|
||||
* @param mixed $slot
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getBySlot($slot);
|
||||
|
||||
/**
|
||||
* Returns a node from the distributor using the computed hash of a key.
|
||||
*
|
||||
* @param mixed $hash
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getByHash($hash);
|
||||
|
||||
/**
|
||||
* Returns a node from the distributor mapping to the specified value.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($value);
|
||||
|
||||
/**
|
||||
* Returns the underlying hash generator instance.
|
||||
*
|
||||
* @return HashGeneratorInterface
|
||||
*/
|
||||
public function getHashGenerator();
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Distributor;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Exception class that identifies empty rings.
|
||||
*/
|
||||
class EmptyRingException extends Exception
|
||||
{
|
||||
}
|
||||
268
plugins/cache-redis/Predis/Cluster/Distributor/HashRing.php
Normal file
268
plugins/cache-redis/Predis/Cluster/Distributor/HashRing.php
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Distributor;
|
||||
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
|
||||
/**
|
||||
* This class implements an hashring-based distributor that uses the same
|
||||
* algorithm of memcache to distribute keys in a cluster using client-side
|
||||
* sharding.
|
||||
* @author Lorenzo Castelli <lcastelli@gmail.com>
|
||||
*/
|
||||
class HashRing implements DistributorInterface, HashGeneratorInterface
|
||||
{
|
||||
public const DEFAULT_REPLICAS = 128;
|
||||
public const DEFAULT_WEIGHT = 100;
|
||||
|
||||
private $ring;
|
||||
private $ringKeys;
|
||||
private $ringKeysCount;
|
||||
private $replicas;
|
||||
private $nodeHashCallback;
|
||||
private $nodes = [];
|
||||
|
||||
/**
|
||||
* @param int $replicas Number of replicas in the ring.
|
||||
* @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
|
||||
*/
|
||||
public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null)
|
||||
{
|
||||
$this->replicas = $replicas;
|
||||
$this->nodeHashCallback = $nodeHashCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node to the ring with an optional weight.
|
||||
*
|
||||
* @param mixed $node Node object.
|
||||
* @param int $weight Weight for the node.
|
||||
*/
|
||||
public function add($node, $weight = null)
|
||||
{
|
||||
// In case of collisions in the hashes of the nodes, the node added
|
||||
// last wins, thus the order in which nodes are added is significant.
|
||||
$this->nodes[] = [
|
||||
'object' => $node,
|
||||
'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT,
|
||||
];
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove($node)
|
||||
{
|
||||
// A node is removed by resetting the ring so that it's recreated from
|
||||
// scratch, in order to reassign possible hashes with collisions to the
|
||||
// right node according to the order in which they were added in the
|
||||
// first place.
|
||||
for ($i = 0; $i < count($this->nodes); ++$i) {
|
||||
if ($this->nodes[$i]['object'] === $node) {
|
||||
array_splice($this->nodes, $i, 1);
|
||||
$this->reset();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the distributor.
|
||||
*/
|
||||
private function reset()
|
||||
{
|
||||
unset(
|
||||
$this->ring,
|
||||
$this->ringKeys,
|
||||
$this->ringKeysCount
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initialization status of the distributor.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isInitialized()
|
||||
{
|
||||
return isset($this->ringKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total weight of all the nodes in the distributor.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function computeTotalWeight()
|
||||
{
|
||||
$totalWeight = 0;
|
||||
|
||||
foreach ($this->nodes as $node) {
|
||||
$totalWeight += $node['weight'];
|
||||
}
|
||||
|
||||
return $totalWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the distributor.
|
||||
*/
|
||||
private function initialize()
|
||||
{
|
||||
if ($this->isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->nodes) {
|
||||
throw new EmptyRingException('Cannot initialize an empty hashring.');
|
||||
}
|
||||
|
||||
$this->ring = [];
|
||||
$totalWeight = $this->computeTotalWeight();
|
||||
$nodesCount = count($this->nodes);
|
||||
|
||||
foreach ($this->nodes as $node) {
|
||||
$weightRatio = $node['weight'] / $totalWeight;
|
||||
$this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio);
|
||||
}
|
||||
|
||||
ksort($this->ring, SORT_NUMERIC);
|
||||
$this->ringKeys = array_keys($this->ring);
|
||||
$this->ringKeysCount = count($this->ringKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the logic needed to add a node to the hashring.
|
||||
*
|
||||
* @param array $ring Source hashring.
|
||||
* @param mixed $node Node object to be added.
|
||||
* @param int $totalNodes Total number of nodes.
|
||||
* @param int $replicas Number of replicas in the ring.
|
||||
* @param float $weightRatio Weight ratio for the node.
|
||||
*/
|
||||
protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
|
||||
{
|
||||
$nodeObject = $node['object'];
|
||||
$nodeHash = $this->getNodeHash($nodeObject);
|
||||
$replicas = (int) round($weightRatio * $totalNodes * $replicas);
|
||||
|
||||
for ($i = 0; $i < $replicas; ++$i) {
|
||||
$key = $this->hash("$nodeHash:$i");
|
||||
$ring[$key] = $nodeObject;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNodeHash($nodeObject)
|
||||
{
|
||||
if (!isset($this->nodeHashCallback)) {
|
||||
return (string) $nodeObject;
|
||||
}
|
||||
|
||||
return call_user_func($this->nodeHashCallback, $nodeObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hash($value)
|
||||
{
|
||||
return crc32($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getByHash($hash)
|
||||
{
|
||||
return $this->ring[$this->getSlot($hash)];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBySlot($slot)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
if (isset($this->ring[$slot])) {
|
||||
return $this->ring[$slot];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSlot($hash)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$ringKeys = $this->ringKeys;
|
||||
$upper = $this->ringKeysCount - 1;
|
||||
$lower = 0;
|
||||
|
||||
while ($lower <= $upper) {
|
||||
$index = ($lower + $upper) >> 1;
|
||||
$item = $ringKeys[$index];
|
||||
|
||||
if ($item > $hash) {
|
||||
$upper = $index - 1;
|
||||
} elseif ($item < $hash) {
|
||||
$lower = $index + 1;
|
||||
} else {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($value)
|
||||
{
|
||||
$hash = $this->hash($value);
|
||||
|
||||
return $this->getByHash($hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a strategy to deal with wrap-around errors during binary searches.
|
||||
*
|
||||
* @param int $upper
|
||||
* @param int $lower
|
||||
* @param int $ringKeysCount
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
|
||||
{
|
||||
// Binary search for the last item in ringkeys with a value less or
|
||||
// equal to the key. If no such item exists, return the last item.
|
||||
return $upper >= 0 ? $upper : $ringKeysCount - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHashGenerator()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Distributor;
|
||||
|
||||
/**
|
||||
* This class implements an hashring-based distributor that uses the same
|
||||
* algorithm of libketama to distribute keys in a cluster using client-side
|
||||
* sharding.
|
||||
* @author Lorenzo Castelli <lcastelli@gmail.com>
|
||||
*/
|
||||
class KetamaRing extends HashRing
|
||||
{
|
||||
public const DEFAULT_REPLICAS = 160;
|
||||
|
||||
/**
|
||||
* @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
|
||||
*/
|
||||
public function __construct($nodeHashCallback = null)
|
||||
{
|
||||
parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
|
||||
{
|
||||
$nodeObject = $node['object'];
|
||||
$nodeHash = $this->getNodeHash($nodeObject);
|
||||
$replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4));
|
||||
|
||||
for ($i = 0; $i < $replicas; ++$i) {
|
||||
$unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));
|
||||
|
||||
foreach ($unpackedDigest as $key) {
|
||||
$ring[$key] = $nodeObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hash($value)
|
||||
{
|
||||
$hash = unpack('V', md5($value, true));
|
||||
|
||||
return $hash[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
|
||||
{
|
||||
// Binary search for the first item in ringkeys with a value greater
|
||||
// or equal to the key. If no such item exists, return the first item.
|
||||
return $lower < $ringKeysCount ? $lower : 0;
|
||||
}
|
||||
}
|
||||
73
plugins/cache-redis/Predis/Cluster/Hash/CRC16.php
Normal file
73
plugins/cache-redis/Predis/Cluster/Hash/CRC16.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Hash;
|
||||
|
||||
/**
|
||||
* Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
|
||||
*/
|
||||
class CRC16 implements HashGeneratorInterface
|
||||
{
|
||||
private static $CCITT_16 = [
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
|
||||
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
|
||||
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
|
||||
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
|
||||
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
|
||||
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
|
||||
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
|
||||
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
|
||||
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
|
||||
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
|
||||
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
||||
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
|
||||
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
|
||||
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
|
||||
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
|
||||
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
|
||||
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
|
||||
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
|
||||
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
|
||||
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
|
||||
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
|
||||
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hash($value)
|
||||
{
|
||||
// CRC-CCITT-16 algorithm
|
||||
$crc = 0;
|
||||
$CCITT_16 = self::$CCITT_16;
|
||||
|
||||
$value = (string) $value;
|
||||
$strlen = strlen($value);
|
||||
|
||||
for ($i = 0; $i < $strlen; ++$i) {
|
||||
$crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
|
||||
}
|
||||
|
||||
return $crc;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Hash;
|
||||
|
||||
/**
|
||||
* An hash generator implements the logic used to calculate the hash of a key to
|
||||
* distribute operations among Redis nodes.
|
||||
*/
|
||||
interface HashGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* Generates an hash from a string to be used for distribution.
|
||||
*
|
||||
* @param string $value String value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function hash($value);
|
||||
}
|
||||
42
plugins/cache-redis/Predis/Cluster/Hash/PhpiredisCRC16.php
Normal file
42
plugins/cache-redis/Predis/Cluster/Hash/PhpiredisCRC16.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Hash;
|
||||
|
||||
use Predis\NotSupportedException;
|
||||
|
||||
/**
|
||||
* Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
|
||||
*
|
||||
* @deprecated 2.1.2
|
||||
*/
|
||||
class PhpiredisCRC16 implements HashGeneratorInterface
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
if (!function_exists('phpiredis_utils_crc16')) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new NotSupportedException(
|
||||
'This hash generator requires a compatible version of ext-phpiredis'
|
||||
);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hash($value)
|
||||
{
|
||||
return phpiredis_utils_crc16($value);
|
||||
}
|
||||
}
|
||||
77
plugins/cache-redis/Predis/Cluster/PredisStrategy.php
Normal file
77
plugins/cache-redis/Predis/Cluster/PredisStrategy.php
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use Predis\Cluster\Distributor\DistributorInterface;
|
||||
use Predis\Cluster\Distributor\HashRing;
|
||||
|
||||
/**
|
||||
* Default cluster strategy used by Predis to handle client-side sharding.
|
||||
*/
|
||||
class PredisStrategy extends ClusterStrategy
|
||||
{
|
||||
protected $distributor;
|
||||
|
||||
/**
|
||||
* @param DistributorInterface $distributor Optional distributor instance.
|
||||
*/
|
||||
public function __construct(DistributorInterface $distributor = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->distributor = $distributor ?: new HashRing();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSlotByKey($key)
|
||||
{
|
||||
$key = $this->extractKeyTag($key);
|
||||
$hash = $this->distributor->hash($key);
|
||||
|
||||
return $this->distributor->getSlot($hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkSameSlotForKeys(array $keys)
|
||||
{
|
||||
if (!$count = count($keys)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentKey = $this->extractKeyTag($keys[0]);
|
||||
|
||||
for ($i = 1; $i < $count; ++$i) {
|
||||
$nextKey = $this->extractKeyTag($keys[$i]);
|
||||
|
||||
if ($currentKey !== $nextKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentKey = $nextKey;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDistributor()
|
||||
{
|
||||
return $this->distributor;
|
||||
}
|
||||
}
|
||||
55
plugins/cache-redis/Predis/Cluster/RedisStrategy.php
Normal file
55
plugins/cache-redis/Predis/Cluster/RedisStrategy.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use Predis\Cluster\Hash\CRC16;
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
use Predis\NotSupportedException;
|
||||
|
||||
/**
|
||||
* Default class used by Predis to calculate hashes out of keys of
|
||||
* commands supported by redis-cluster.
|
||||
*/
|
||||
class RedisStrategy extends ClusterStrategy
|
||||
{
|
||||
protected $hashGenerator;
|
||||
|
||||
/**
|
||||
* @param HashGeneratorInterface $hashGenerator Hash generator instance.
|
||||
*/
|
||||
public function __construct(HashGeneratorInterface $hashGenerator = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->hashGenerator = $hashGenerator ?: new CRC16();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSlotByKey($key)
|
||||
{
|
||||
$key = $this->extractKeyTag($key);
|
||||
|
||||
return $this->hashGenerator->hash($key) & 0x3FFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDistributor()
|
||||
{
|
||||
$class = get_class($this);
|
||||
throw new NotSupportedException("$class does not provide an external distributor");
|
||||
}
|
||||
}
|
||||
209
plugins/cache-redis/Predis/Cluster/SlotMap.php
Normal file
209
plugins/cache-redis/Predis/Cluster/SlotMap.php
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use OutOfBoundsException;
|
||||
use Predis\Connection\NodeConnectionInterface;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Slot map for redis-cluster.
|
||||
*/
|
||||
class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
{
|
||||
private $slots = [];
|
||||
|
||||
/**
|
||||
* Checks if the given slot is valid.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($slot)
|
||||
{
|
||||
return $slot >= 0x0000 && $slot <= 0x3FFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given slot range is valid.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidRange($first, $last)
|
||||
{
|
||||
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the slot map.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->slots = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the slot map is empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slot map as a dictionary of $slot => $node.
|
||||
*
|
||||
* The order of the slots in the dictionary is not guaranteed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of unique nodes in the slot map.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNodes()
|
||||
{
|
||||
return array_keys(array_flip($this->slots));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot range to a node.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @throws OutOfBoundsException
|
||||
*/
|
||||
public function setSlots($first, $last, $connection)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified slot range.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSlots($first, $last)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last");
|
||||
}
|
||||
|
||||
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified slot is assigned.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetExists($slot)
|
||||
{
|
||||
return isset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($slot)
|
||||
{
|
||||
return $this->slots[$slot] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot to a node.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetSet($slot, $connection)
|
||||
{
|
||||
if (!static::isValid($slot)) {
|
||||
throw new OutOfBoundsException("Invalid slot $slot for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots[(int) $slot] = (string) $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetUnset($slot)
|
||||
{
|
||||
unset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current number of assigned slots.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the slot map.
|
||||
*
|
||||
* @return Traversable<int, string>
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->slots);
|
||||
}
|
||||
}
|
||||
52
plugins/cache-redis/Predis/Cluster/StrategyInterface.php
Normal file
52
plugins/cache-redis/Predis/Cluster/StrategyInterface.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use Predis\Cluster\Distributor\DistributorInterface;
|
||||
use Predis\Command\CommandInterface;
|
||||
|
||||
/**
|
||||
* Interface for classes defining the strategy used to calculate an hash out of
|
||||
* keys extracted from supported commands.
|
||||
*
|
||||
* This is mostly useful to support clustering via client-side sharding.
|
||||
*/
|
||||
interface StrategyInterface
|
||||
{
|
||||
/**
|
||||
* Returns a slot for the given command used for clustering distribution or
|
||||
* NULL when this is not possible.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getSlot(CommandInterface $command);
|
||||
|
||||
/**
|
||||
* Returns a slot for the given key used for clustering distribution or NULL
|
||||
* when this is not possible.
|
||||
*
|
||||
* @param string $key Key string.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getSlotByKey($key);
|
||||
|
||||
/**
|
||||
* Returns a distributor instance to be used by the cluster.
|
||||
*
|
||||
* @return DistributorInterface
|
||||
*/
|
||||
public function getDistributor();
|
||||
}
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Collection\Iterator;
|
||||
|
||||
use Iterator;
|
||||
use Predis\ClientInterface;
|
||||
use Predis\NotSupportedException;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
/**
|
||||
* Provides the base implementation for a fully-rewindable PHP iterator that can
|
||||
* incrementally iterate over cursor-based collections stored on Redis using the
|
||||
* commands in the `SCAN` family.
|
||||
*
|
||||
* Given their incremental nature with multiple fetches, these kind of iterators
|
||||
* offer limited guarantees about the returned elements because the collection
|
||||
* can change several times during the iteration process.
|
||||
*
|
||||
* @see http://redis.io/commands/scan
|
||||
*/
|
||||
abstract class CursorBasedIterator implements Iterator
|
||||
{
|
||||
protected $client;
|
||||
protected $match;
|
||||
protected $count;
|
||||
|
||||
protected $valid;
|
||||
protected $fetchmore;
|
||||
protected $elements;
|
||||
protected $cursor;
|
||||
protected $position;
|
||||
protected $current;
|
||||
|
||||
/**
|
||||
* @param ClientInterface $client Client connected to Redis.
|
||||
* @param string $match Pattern to match during the server-side iteration.
|
||||
* @param int $count Hint used by Redis to compute the number of results per iteration.
|
||||
*/
|
||||
public function __construct(ClientInterface $client, $match = null, $count = null)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->match = $match;
|
||||
$this->count = $count;
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the client supports the specified Redis command required to
|
||||
* fetch elements from the server to perform the iteration.
|
||||
*
|
||||
* @param ClientInterface $client Client connected to Redis.
|
||||
* @param string $commandID Command ID.
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
protected function requiredCommand(ClientInterface $client, $commandID)
|
||||
{
|
||||
if (!$client->getCommandFactory()->supports($commandID)) {
|
||||
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the inner state of the iterator.
|
||||
*/
|
||||
protected function reset()
|
||||
{
|
||||
$this->valid = true;
|
||||
$this->fetchmore = true;
|
||||
$this->elements = [];
|
||||
$this->cursor = 0;
|
||||
$this->position = -1;
|
||||
$this->current = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of options for the `SCAN` command.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getScanOptions()
|
||||
{
|
||||
$options = [];
|
||||
|
||||
if (strlen(strval($this->match)) > 0) {
|
||||
$options['MATCH'] = $this->match;
|
||||
}
|
||||
|
||||
if ($this->count > 0) {
|
||||
$options['COUNT'] = $this->count;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a new set of elements from the remote collection, effectively
|
||||
* advancing the iteration process.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function executeCommand();
|
||||
|
||||
/**
|
||||
* Populates the local buffer of elements fetched from the server during
|
||||
* the iteration.
|
||||
*/
|
||||
protected function fetch()
|
||||
{
|
||||
[$cursor, $elements] = $this->executeCommand();
|
||||
|
||||
if (!$cursor) {
|
||||
$this->fetchmore = false;
|
||||
}
|
||||
|
||||
$this->cursor = $cursor;
|
||||
$this->elements = $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts next values for key() and current().
|
||||
*/
|
||||
protected function extractNext()
|
||||
{
|
||||
++$this->position;
|
||||
$this->current = array_shift($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
$this->reset();
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
tryFetch:
|
||||
if (!$this->elements && $this->fetchmore) {
|
||||
$this->fetch();
|
||||
}
|
||||
|
||||
if ($this->elements) {
|
||||
$this->extractNext();
|
||||
} elseif ($this->cursor) {
|
||||
goto tryFetch;
|
||||
} else {
|
||||
$this->valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->valid;
|
||||
}
|
||||
}
|
||||
57
plugins/cache-redis/Predis/Collection/Iterator/HashKey.php
Normal file
57
plugins/cache-redis/Predis/Collection/Iterator/HashKey.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Collection\Iterator;
|
||||
|
||||
use Predis\ClientInterface;
|
||||
|
||||
/**
|
||||
* Abstracts the iteration of fields and values of an hash by leveraging the
|
||||
* HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
|
||||
*
|
||||
* @see http://redis.io/commands/scan
|
||||
*/
|
||||
class HashKey extends CursorBasedIterator
|
||||
{
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(ClientInterface $client, $key, $match = null, $count = null)
|
||||
{
|
||||
$this->requiredCommand($client, 'HSCAN');
|
||||
|
||||
parent::__construct($client, $match, $count);
|
||||
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function executeCommand()
|
||||
{
|
||||
return $this->client->hscan($this->key, $this->cursor, $this->getScanOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function extractNext()
|
||||
{
|
||||
$this->position = key($this->elements);
|
||||
$this->current = current($this->elements);
|
||||
|
||||
unset($this->elements[$this->position]);
|
||||
}
|
||||
}
|
||||
42
plugins/cache-redis/Predis/Collection/Iterator/Keyspace.php
Normal file
42
plugins/cache-redis/Predis/Collection/Iterator/Keyspace.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Collection\Iterator;
|
||||
|
||||
use Predis\ClientInterface;
|
||||
|
||||
/**
|
||||
* Abstracts the iteration of the keyspace on a Redis instance by leveraging the
|
||||
* SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
|
||||
*
|
||||
* @see http://redis.io/commands/scan
|
||||
*/
|
||||
class Keyspace extends CursorBasedIterator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(ClientInterface $client, $match = null, $count = null)
|
||||
{
|
||||
$this->requiredCommand($client, 'SCAN');
|
||||
|
||||
parent::__construct($client, $match, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function executeCommand()
|
||||
{
|
||||
return $this->client->scan($this->cursor, $this->getScanOptions());
|
||||
}
|
||||
}
|
||||
183
plugins/cache-redis/Predis/Collection/Iterator/ListKey.php
Normal file
183
plugins/cache-redis/Predis/Collection/Iterator/ListKey.php
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Collection\Iterator;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use Predis\ClientInterface;
|
||||
use Predis\NotSupportedException;
|
||||
use ReturnTypeWillChange;
|
||||
|
||||
/**
|
||||
* Abstracts the iteration of items stored in a list by leveraging the LRANGE
|
||||
* command wrapped in a fully-rewindable PHP iterator.
|
||||
*
|
||||
* This iterator tries to emulate the behaviour of cursor-based iterators based
|
||||
* on the SCAN-family of commands introduced in Redis <= 2.8, meaning that due
|
||||
* to its incremental nature with multiple fetches it can only offer limited
|
||||
* guarantees on the returned elements because the collection can change several
|
||||
* times (trimmed, deleted, overwritten) during the iteration process.
|
||||
*
|
||||
* @see http://redis.io/commands/lrange
|
||||
*/
|
||||
class ListKey implements Iterator
|
||||
{
|
||||
protected $client;
|
||||
protected $count;
|
||||
protected $key;
|
||||
|
||||
protected $valid;
|
||||
protected $fetchmore;
|
||||
protected $elements;
|
||||
protected $position;
|
||||
protected $current;
|
||||
|
||||
/**
|
||||
* @param ClientInterface $client Client connected to Redis.
|
||||
* @param string $key Redis list key.
|
||||
* @param int $count Number of items retrieved on each fetch operation.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(ClientInterface $client, $key, $count = 10)
|
||||
{
|
||||
$this->requiredCommand($client, 'LRANGE');
|
||||
|
||||
if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) {
|
||||
throw new InvalidArgumentException('The $count argument must be a positive integer.');
|
||||
}
|
||||
|
||||
$this->client = $client;
|
||||
$this->key = $key;
|
||||
$this->count = $count;
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the client instance supports the specified Redis command
|
||||
* required to fetch elements from the server to perform the iteration.
|
||||
*
|
||||
* @param ClientInterface $client Client connected to Redis.
|
||||
* @param string $commandID Command ID.
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*/
|
||||
protected function requiredCommand(ClientInterface $client, $commandID)
|
||||
{
|
||||
if (!$client->getCommandFactory()->supports($commandID)) {
|
||||
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the inner state of the iterator.
|
||||
*/
|
||||
protected function reset()
|
||||
{
|
||||
$this->valid = true;
|
||||
$this->fetchmore = true;
|
||||
$this->elements = [];
|
||||
$this->position = -1;
|
||||
$this->current = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a new set of elements from the remote collection, effectively
|
||||
* advancing the iteration process.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function executeCommand()
|
||||
{
|
||||
return $this->client->lrange($this->key, $this->position + 1, $this->position + $this->count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the local buffer of elements fetched from the server during the
|
||||
* iteration.
|
||||
*/
|
||||
protected function fetch()
|
||||
{
|
||||
$elements = $this->executeCommand();
|
||||
|
||||
if (count($elements) < $this->count) {
|
||||
$this->fetchmore = false;
|
||||
}
|
||||
|
||||
$this->elements = $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts next values for key() and current().
|
||||
*/
|
||||
protected function extractNext()
|
||||
{
|
||||
++$this->position;
|
||||
$this->current = array_shift($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
$this->reset();
|
||||
$this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
if (!$this->elements && $this->fetchmore) {
|
||||
$this->fetch();
|
||||
}
|
||||
|
||||
if ($this->elements) {
|
||||
$this->extractNext();
|
||||
} else {
|
||||
$this->valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->valid;
|
||||
}
|
||||
}
|
||||
46
plugins/cache-redis/Predis/Collection/Iterator/SetKey.php
Normal file
46
plugins/cache-redis/Predis/Collection/Iterator/SetKey.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Collection\Iterator;
|
||||
|
||||
use Predis\ClientInterface;
|
||||
|
||||
/**
|
||||
* Abstracts the iteration of members stored in a set by leveraging the SSCAN
|
||||
* command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
|
||||
*
|
||||
* @see http://redis.io/commands/scan
|
||||
*/
|
||||
class SetKey extends CursorBasedIterator
|
||||
{
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(ClientInterface $client, $key, $match = null, $count = null)
|
||||
{
|
||||
$this->requiredCommand($client, 'SSCAN');
|
||||
|
||||
parent::__construct($client, $match, $count);
|
||||
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function executeCommand()
|
||||
{
|
||||
return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Collection\Iterator;
|
||||
|
||||
use Predis\ClientInterface;
|
||||
|
||||
/**
|
||||
* Abstracts the iteration of members stored in a sorted set by leveraging the
|
||||
* ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
|
||||
*
|
||||
* @see http://redis.io/commands/scan
|
||||
*/
|
||||
class SortedSetKey extends CursorBasedIterator
|
||||
{
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(ClientInterface $client, $key, $match = null, $count = null)
|
||||
{
|
||||
$this->requiredCommand($client, 'ZSCAN');
|
||||
|
||||
parent::__construct($client, $match, $count);
|
||||
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function executeCommand()
|
||||
{
|
||||
return $this->client->zscan($this->key, $this->cursor, $this->getScanOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function extractNext()
|
||||
{
|
||||
$this->position = key($this->elements);
|
||||
$this->current = current($this->elements);
|
||||
|
||||
unset($this->elements[$this->position]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument;
|
||||
|
||||
/**
|
||||
* Allows to use object-oriented approach to handle complex conditional arguments.
|
||||
*/
|
||||
interface ArrayableArgument
|
||||
{
|
||||
/**
|
||||
* Get the instance as an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Geospatial;
|
||||
|
||||
use UnexpectedValueException;
|
||||
|
||||
abstract class AbstractBy implements ByInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private static $unitEnum = ['m', 'km', 'ft', 'mi'];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $unit;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
abstract public function toArray(): array;
|
||||
|
||||
/**
|
||||
* @param string $unit
|
||||
* @return void
|
||||
*/
|
||||
protected function setUnit(string $unit): void
|
||||
{
|
||||
if (!in_array($unit, self::$unitEnum, true)) {
|
||||
throw new UnexpectedValueException('Wrong value given for unit');
|
||||
}
|
||||
|
||||
$this->unit = $unit;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Geospatial;
|
||||
|
||||
class ByBox extends AbstractBy
|
||||
{
|
||||
private const KEYWORD = 'BYBOX';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $width;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $height;
|
||||
|
||||
public function __construct(int $width, int $height, string $unit)
|
||||
{
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
$this->setUnit($unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [self::KEYWORD, $this->width, $this->height, $this->unit];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Geospatial;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
interface ByInterface extends ArrayableArgument
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Geospatial;
|
||||
|
||||
class ByRadius extends AbstractBy
|
||||
{
|
||||
private const KEYWORD = 'BYRADIUS';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $radius;
|
||||
|
||||
public function __construct(int $radius, string $unit)
|
||||
{
|
||||
$this->radius = $radius;
|
||||
$this->setUnit($unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [self::KEYWORD, $this->radius, $this->unit];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Geospatial;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
interface FromInterface extends ArrayableArgument
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Geospatial;
|
||||
|
||||
class FromLonLat implements FromInterface
|
||||
{
|
||||
private const KEYWORD = 'FROMLONLAT';
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $longitude;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $latitude;
|
||||
|
||||
public function __construct(float $longitude, float $latitude)
|
||||
{
|
||||
$this->longitude = $longitude;
|
||||
$this->latitude = $latitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [self::KEYWORD, $this->longitude, $this->latitude];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Geospatial;
|
||||
|
||||
class FromMember implements FromInterface
|
||||
{
|
||||
private const KEYWORD = 'FROMMEMBER';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $member;
|
||||
|
||||
public function __construct(string $member)
|
||||
{
|
||||
$this->member = $member;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [self::KEYWORD, $this->member];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
class AggregateArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $sortingEnum = [
|
||||
'asc' => 'ASC',
|
||||
'desc' => 'DESC',
|
||||
];
|
||||
|
||||
/**
|
||||
* Loads document attributes from the source document.
|
||||
*
|
||||
* @param string ...$fields Could be just '*' to load all fields
|
||||
* @return $this
|
||||
*/
|
||||
public function load(string ...$fields): self
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
|
||||
$this->arguments[] = 'LOAD';
|
||||
|
||||
if ($arguments[0] === '*') {
|
||||
$this->arguments[] = '*';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->arguments[] = count($arguments);
|
||||
$this->arguments = array_merge($this->arguments, $arguments);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads document attributes from the source document.
|
||||
*
|
||||
* @param string ...$properties
|
||||
* @return $this
|
||||
*/
|
||||
public function groupBy(string ...$properties): self
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
|
||||
array_push($this->arguments, 'GROUPBY', count($arguments));
|
||||
$this->arguments = array_merge($this->arguments, $arguments);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups the results in the pipeline based on one or more properties.
|
||||
*
|
||||
* If you want to add alias property to your argument just add "true" value in arguments enumeration,
|
||||
* next value will be considered as alias to previous one.
|
||||
*
|
||||
* Example: 'argument', true, 'name' => 'argument' AS 'name'
|
||||
*
|
||||
* @param string $function
|
||||
* @param string|bool ...$argument
|
||||
* @return $this
|
||||
*/
|
||||
public function reduce(string $function, ...$argument): self
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
$functionValue = array_shift($arguments);
|
||||
$argumentsCounter = 0;
|
||||
|
||||
for ($i = 0, $iMax = count($arguments); $i < $iMax; $i++) {
|
||||
if (true === $arguments[$i]) {
|
||||
$arguments[$i] = 'AS';
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$argumentsCounter++;
|
||||
}
|
||||
|
||||
array_push($this->arguments, 'REDUCE', $functionValue);
|
||||
$this->arguments = array_merge($this->arguments, [$argumentsCounter], $arguments);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the pipeline up until the point of SORTBY, using a list of properties.
|
||||
*
|
||||
* @param int $max
|
||||
* @param string ...$properties Enumeration of properties, including sorting direction (ASC, DESC)
|
||||
* @return $this
|
||||
*/
|
||||
public function sortBy(int $max = 0, ...$properties): self
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
$maxValue = array_shift($arguments);
|
||||
|
||||
$this->arguments[] = 'SORTBY';
|
||||
$this->arguments = array_merge($this->arguments, [count($arguments)], $arguments);
|
||||
|
||||
if ($maxValue !== 0) {
|
||||
array_push($this->arguments, 'MAX', $maxValue);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a 1-to-1 transformation on one or more properties and either stores the result
|
||||
* as a new property down the pipeline or replaces any property using this transformation.
|
||||
*
|
||||
* @param string $expression
|
||||
* @param string $as
|
||||
* @return $this
|
||||
*/
|
||||
public function apply(string $expression, string $as = ''): self
|
||||
{
|
||||
array_push($this->arguments, 'APPLY', $expression);
|
||||
|
||||
if ($as !== '') {
|
||||
array_push($this->arguments, 'AS', $as);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan part of the results with a quicker alternative than LIMIT.
|
||||
*
|
||||
* @param int $readSize
|
||||
* @param int $idleTime
|
||||
* @return $this
|
||||
*/
|
||||
public function withCursor(int $readSize = 0, int $idleTime = 0): self
|
||||
{
|
||||
$this->arguments[] = 'WITHCURSOR';
|
||||
|
||||
if ($readSize !== 0) {
|
||||
array_push($this->arguments, 'COUNT', $readSize);
|
||||
}
|
||||
|
||||
if ($idleTime !== 0) {
|
||||
array_push($this->arguments, 'MAXIDLE', $idleTime);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
class AlterArguments extends CommonArguments
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
class CommonArguments implements ArrayableArgument
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* Adds default language for documents within an index.
|
||||
*
|
||||
* @param string $defaultLanguage
|
||||
* @return $this
|
||||
*/
|
||||
public function language(string $defaultLanguage = 'english'): self
|
||||
{
|
||||
$this->arguments[] = 'LANGUAGE';
|
||||
$this->arguments[] = $defaultLanguage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the dialect version under which to execute the query.
|
||||
* If not specified, the query will execute under the default dialect version
|
||||
* set during module initial loading or via FT.CONFIG SET command.
|
||||
*
|
||||
* @param string $dialect
|
||||
* @return $this
|
||||
*/
|
||||
public function dialect(string $dialect): self
|
||||
{
|
||||
$this->arguments[] = 'DIALECT';
|
||||
$this->arguments[] = $dialect;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, does not scan and index.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function skipInitialScan(): self
|
||||
{
|
||||
$this->arguments[] = 'SKIPINITIALSCAN';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an arbitrary, binary safe payload that is exposed to custom scoring functions.
|
||||
*
|
||||
* @param string $payload
|
||||
* @return $this
|
||||
*/
|
||||
public function payload(string $payload): self
|
||||
{
|
||||
$this->arguments[] = 'PAYLOAD';
|
||||
$this->arguments[] = $payload;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Also returns the relative internal score of each document.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withScores(): self
|
||||
{
|
||||
$this->arguments[] = 'WITHSCORES';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves optional document payloads.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withPayloads(): self
|
||||
{
|
||||
$this->arguments[] = 'WITHPAYLOADS';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not try to use stemming for query expansion but searches the query terms verbatim.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function verbatim(): self
|
||||
{
|
||||
$this->arguments[] = 'VERBATIM';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the timeout parameter of the module.
|
||||
*
|
||||
* @param int $timeout
|
||||
* @return $this
|
||||
*/
|
||||
public function timeout(int $timeout): self
|
||||
{
|
||||
$this->arguments[] = 'TIMEOUT';
|
||||
$this->arguments[] = $timeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an arbitrary, binary safe payload that is exposed to custom scoring functions.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param int $num
|
||||
* @return $this
|
||||
*/
|
||||
public function limit(int $offset, int $num): self
|
||||
{
|
||||
array_push($this->arguments, 'LIMIT', $offset, $num);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds filter expression into index.
|
||||
*
|
||||
* @param string $filter
|
||||
* @return $this
|
||||
*/
|
||||
public function filter(string $filter): self
|
||||
{
|
||||
$this->arguments[] = 'FILTER';
|
||||
$this->arguments[] = $filter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines one or more value parameters. Each parameter has a name and a value.
|
||||
*
|
||||
* Example: ['name1', 'value1', 'name2', 'value2'...]
|
||||
*
|
||||
* @param array $nameValuesDictionary
|
||||
* @return $this
|
||||
*/
|
||||
public function params(array $nameValuesDictionary): self
|
||||
{
|
||||
$this->arguments[] = 'PARAMS';
|
||||
$this->arguments[] = count($nameValuesDictionary);
|
||||
$this->arguments = array_merge($this->arguments, $nameValuesDictionary);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class CreateArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $supportedDataTypesEnum = [
|
||||
'hash' => 'HASH',
|
||||
'json' => 'JSON',
|
||||
];
|
||||
|
||||
/**
|
||||
* Specify data type for given index. To index JSON you must have the RedisJSON module to be installed.
|
||||
*
|
||||
* @param string $modifier
|
||||
* @return $this
|
||||
*/
|
||||
public function on(string $modifier = 'HASH'): self
|
||||
{
|
||||
if (in_array(strtoupper($modifier), $this->supportedDataTypesEnum)) {
|
||||
$this->arguments[] = 'ON';
|
||||
$this->arguments[] = $this->supportedDataTypesEnum[strtolower($modifier)];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$enumValues = implode(', ', array_values($this->supportedDataTypesEnum));
|
||||
throw new InvalidArgumentException("Wrong modifier value given. Currently supports: {$enumValues}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more prefixes into index.
|
||||
*
|
||||
* @param array $prefixes
|
||||
* @return $this
|
||||
*/
|
||||
public function prefix(array $prefixes): self
|
||||
{
|
||||
$this->arguments[] = 'PREFIX';
|
||||
$this->arguments[] = count($prefixes);
|
||||
$this->arguments = array_merge($this->arguments, $prefixes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document attribute set as document language.
|
||||
*
|
||||
* @param string $languageAttribute
|
||||
* @return $this
|
||||
*/
|
||||
public function languageField(string $languageAttribute): self
|
||||
{
|
||||
$this->arguments[] = 'LANGUAGE_FIELD';
|
||||
$this->arguments[] = $languageAttribute;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default score for documents in the index.
|
||||
*
|
||||
* @param float $defaultScore
|
||||
* @return $this
|
||||
*/
|
||||
public function score(float $defaultScore = 1.0): self
|
||||
{
|
||||
$this->arguments[] = 'SCORE';
|
||||
$this->arguments[] = $defaultScore;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document attribute that used as the document rank based on the user ranking.
|
||||
*
|
||||
* @param string $scoreAttribute
|
||||
* @return $this
|
||||
*/
|
||||
public function scoreField(string $scoreAttribute): self
|
||||
{
|
||||
$this->arguments[] = 'SCORE_FIELD';
|
||||
$this->arguments[] = $scoreAttribute;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces RediSearch to encode indexes as if there were more than 32 text attributes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function maxTextFields(): self
|
||||
{
|
||||
$this->arguments[] = 'MAXTEXTFIELDS';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not store term offsets for documents.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function noOffsets(): self
|
||||
{
|
||||
$this->arguments[] = 'NOOFFSETS';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a lightweight temporary index that expires after a specified period of inactivity, in seconds.
|
||||
*
|
||||
* @param int $seconds
|
||||
* @return $this
|
||||
*/
|
||||
public function temporary(int $seconds): self
|
||||
{
|
||||
$this->arguments[] = 'TEMPORARY';
|
||||
$this->arguments[] = $seconds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conserves storage space and memory by disabling highlighting support.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function noHl(): self
|
||||
{
|
||||
$this->arguments[] = 'NOHL';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not store attribute bits for each term.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function noFields(): self
|
||||
{
|
||||
$this->arguments[] = 'NOFIELDS';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoids saving the term frequencies in the index.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function noFreqs(): self
|
||||
{
|
||||
$this->arguments[] = 'NOFREQS';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index with a custom stopword list, to be ignored during indexing and search time.
|
||||
*
|
||||
* @param array $stopWords
|
||||
* @return $this
|
||||
*/
|
||||
public function stopWords(array $stopWords): self
|
||||
{
|
||||
$this->arguments[] = 'STOPWORDS';
|
||||
$this->arguments[] = count($stopWords);
|
||||
$this->arguments = array_merge($this->arguments, $stopWords);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
class CursorArguments implements ArrayableArgument
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* Is number of results to read. This parameter overrides COUNT specified in FT.AGGREGATE.
|
||||
*
|
||||
* @param int $readSize
|
||||
* @return $this
|
||||
*/
|
||||
public function count(int $readSize): self
|
||||
{
|
||||
array_push($this->arguments, 'COUNT', $readSize);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
class DropArguments implements ArrayableArgument
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* Drop operation that, if set, deletes the actual document hashes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function dd(): self
|
||||
{
|
||||
$this->arguments[] = 'DD';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
class ExplainArguments extends CommonArguments
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
class ProfileArguments implements ArrayableArgument
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* Adds search context.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function search(): self
|
||||
{
|
||||
$this->arguments[] = 'SEARCH';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds aggregate context.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function aggregate(): self
|
||||
{
|
||||
$this->arguments[] = 'AGGREGATE';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes details of reader iterator.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function limited(): self
|
||||
{
|
||||
$this->arguments[] = 'LIMITED';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is query string, as if sent to FT.SEARCH.
|
||||
*
|
||||
* @param string $query
|
||||
* @return $this
|
||||
*/
|
||||
public function query(string $query): self
|
||||
{
|
||||
$this->arguments[] = 'QUERY';
|
||||
$this->arguments[] = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search\SchemaFields;
|
||||
|
||||
abstract class AbstractField implements FieldInterface
|
||||
{
|
||||
public const SORTABLE = true;
|
||||
public const NOT_SORTABLE = false;
|
||||
public const SORTABLE_UNF = 'UNF';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldArguments = [];
|
||||
|
||||
/**
|
||||
* @param string $fieldType
|
||||
* @param string $identifier
|
||||
* @param string $alias
|
||||
* @param bool|string $sortable
|
||||
* @param bool $noIndex
|
||||
* @return void
|
||||
*/
|
||||
protected function setCommonOptions(
|
||||
string $fieldType,
|
||||
string $identifier,
|
||||
string $alias = '',
|
||||
$sortable = self::NOT_SORTABLE,
|
||||
bool $noIndex = false
|
||||
): void {
|
||||
$this->fieldArguments[] = $identifier;
|
||||
|
||||
if ($alias !== '') {
|
||||
$this->fieldArguments[] = 'AS';
|
||||
$this->fieldArguments[] = $alias;
|
||||
}
|
||||
|
||||
$this->fieldArguments[] = $fieldType;
|
||||
|
||||
if ($sortable === self::SORTABLE) {
|
||||
$this->fieldArguments[] = 'SORTABLE';
|
||||
} elseif ($sortable === self::SORTABLE_UNF) {
|
||||
$this->fieldArguments[] = 'SORTABLE';
|
||||
$this->fieldArguments[] = 'UNF';
|
||||
}
|
||||
|
||||
if ($noIndex) {
|
||||
$this->fieldArguments[] = 'NOINDEX';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->fieldArguments;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search\SchemaFields;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
/**
|
||||
* Represents field in search schema.
|
||||
*/
|
||||
interface FieldInterface extends ArrayableArgument
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search\SchemaFields;
|
||||
|
||||
class GeoField extends AbstractField
|
||||
{
|
||||
/**
|
||||
* @param string $identifier
|
||||
* @param string $alias
|
||||
* @param bool|string $sortable
|
||||
* @param bool $noIndex
|
||||
*/
|
||||
public function __construct(
|
||||
string $identifier,
|
||||
string $alias = '',
|
||||
$sortable = self::NOT_SORTABLE,
|
||||
bool $noIndex = false
|
||||
) {
|
||||
$this->setCommonOptions('GEO', $identifier, $alias, $sortable, $noIndex);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search\SchemaFields;
|
||||
|
||||
class NumericField extends AbstractField
|
||||
{
|
||||
/**
|
||||
* @param string $identifier
|
||||
* @param string $alias
|
||||
* @param bool|string $sortable
|
||||
* @param bool $noIndex
|
||||
*/
|
||||
public function __construct(
|
||||
string $identifier,
|
||||
string $alias = '',
|
||||
$sortable = self::NOT_SORTABLE,
|
||||
bool $noIndex = false
|
||||
) {
|
||||
$this->setCommonOptions('NUMERIC', $identifier, $alias, $sortable, $noIndex);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search\SchemaFields;
|
||||
|
||||
class TagField extends AbstractField
|
||||
{
|
||||
/**
|
||||
* @param string $identifier
|
||||
* @param string $alias
|
||||
* @param bool|string $sortable
|
||||
* @param bool $noIndex
|
||||
* @param string $separator
|
||||
* @param bool $caseSensitive
|
||||
*/
|
||||
public function __construct(
|
||||
string $identifier,
|
||||
string $alias = '',
|
||||
$sortable = self::NOT_SORTABLE,
|
||||
bool $noIndex = false,
|
||||
string $separator = ',',
|
||||
bool $caseSensitive = false
|
||||
) {
|
||||
$this->setCommonOptions('TAG', $identifier, $alias, $sortable, $noIndex);
|
||||
|
||||
if ($separator !== ',') {
|
||||
$this->fieldArguments[] = 'SEPARATOR';
|
||||
$this->fieldArguments[] = $separator;
|
||||
}
|
||||
|
||||
if ($caseSensitive) {
|
||||
$this->fieldArguments[] = 'CASESENSITIVE';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search\SchemaFields;
|
||||
|
||||
class TextField extends AbstractField
|
||||
{
|
||||
/**
|
||||
* @param string $identifier
|
||||
* @param string $alias
|
||||
* @param bool|string $sortable
|
||||
* @param bool $noIndex
|
||||
* @param bool $noStem
|
||||
* @param string $phonetic
|
||||
* @param int $weight
|
||||
* @param bool $withSuffixTrie
|
||||
*/
|
||||
public function __construct(
|
||||
string $identifier,
|
||||
string $alias = '',
|
||||
$sortable = self::NOT_SORTABLE,
|
||||
bool $noIndex = false,
|
||||
bool $noStem = false,
|
||||
string $phonetic = '',
|
||||
int $weight = 1,
|
||||
bool $withSuffixTrie = false
|
||||
) {
|
||||
$this->setCommonOptions('TEXT', $identifier, $alias, $sortable, $noIndex);
|
||||
|
||||
if ($noStem) {
|
||||
$this->fieldArguments[] = 'NOSTEM';
|
||||
}
|
||||
|
||||
if ($phonetic !== '') {
|
||||
$this->fieldArguments[] = 'PHONETIC';
|
||||
$this->fieldArguments[] = $phonetic;
|
||||
}
|
||||
|
||||
if ($weight !== 1) {
|
||||
$this->fieldArguments[] = 'WEIGHT';
|
||||
$this->fieldArguments[] = $weight;
|
||||
}
|
||||
|
||||
if ($withSuffixTrie) {
|
||||
$this->fieldArguments[] = 'WITHSUFFIXTRIE';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search\SchemaFields;
|
||||
|
||||
class VectorField extends AbstractField
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldArguments = [];
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
* @param string $algorithm
|
||||
* @param array $attributeNameValueDictionary
|
||||
* @param string $alias
|
||||
*/
|
||||
public function __construct(
|
||||
string $fieldName,
|
||||
string $algorithm,
|
||||
array $attributeNameValueDictionary,
|
||||
string $alias = ''
|
||||
) {
|
||||
$this->setCommonOptions('VECTOR', $fieldName, $alias);
|
||||
|
||||
array_push($this->fieldArguments, $algorithm, count($attributeNameValueDictionary));
|
||||
$this->fieldArguments = array_merge($this->fieldArguments, $attributeNameValueDictionary);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->fieldArguments;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class SearchArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $sortingEnum = [
|
||||
'asc' => 'ASC',
|
||||
'desc' => 'DESC',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the document ids and not the content.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function noContent(): self
|
||||
{
|
||||
$this->arguments[] = 'NOCONTENT';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the sorting key, right after the id and score and/or payload, if requested.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withSortKeys(): self
|
||||
{
|
||||
$this->arguments[] = 'WITHSORTKEYS';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits results to those having numeric values ranging between min and max,
|
||||
* if numeric_attribute is defined as a numeric attribute in FT.CREATE.
|
||||
* Min and max follow ZRANGE syntax, and can be -inf, +inf, and use( for exclusive ranges.
|
||||
* Multiple numeric filters for different attributes are supported in one query.
|
||||
*
|
||||
* @param array ...$filter Should contain: numeric_field, min and max. Example: ['numeric_field', 1, 10]
|
||||
* @return $this
|
||||
*/
|
||||
public function searchFilter(array ...$filter): self
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
|
||||
foreach ($arguments as $argument) {
|
||||
array_push($this->arguments, 'FILTER', ...$argument);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the results to a given radius from lon and lat. Radius is given as a number and units.
|
||||
*
|
||||
* @param array ...$filter Should contain: geo_field, lon, lat, radius, unit. Example: ['geo_field', 34.1231, 35.1231, 300, km]
|
||||
* @return $this
|
||||
*/
|
||||
public function geoFilter(array ...$filter): self
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
|
||||
foreach ($arguments as $argument) {
|
||||
array_push($this->arguments, 'GEOFILTER', ...$argument);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the result to a given set of keys specified in the list.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return $this
|
||||
*/
|
||||
public function inKeys(array $keys): self
|
||||
{
|
||||
$this->arguments[] = 'INKEYS';
|
||||
$this->arguments[] = count($keys);
|
||||
$this->arguments = array_merge($this->arguments, $keys);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the results to those appearing only in specific attributes of the document, like title or URL.
|
||||
*
|
||||
* @param array $fields
|
||||
* @return $this
|
||||
*/
|
||||
public function inFields(array $fields): self
|
||||
{
|
||||
$this->arguments[] = 'INFIELDS';
|
||||
$this->arguments[] = count($fields);
|
||||
$this->arguments = array_merge($this->arguments, $fields);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the attributes returned from the document.
|
||||
* Num is the number of attributes following the keyword.
|
||||
* If num is 0, it acts like NOCONTENT.
|
||||
* Identifier is either an attribute name (for hashes and JSON) or a JSON Path expression (for JSON).
|
||||
* Property is an optional name used in the result. If not provided, the identifier is used in the result.
|
||||
*
|
||||
* If you want to add alias property to your identifier just add "true" value in identifier enumeration,
|
||||
* next value will be considered as alias to previous one.
|
||||
*
|
||||
* Example: 'identifier', true, 'property' => 'identifier' AS 'property'
|
||||
*
|
||||
* @param int $count
|
||||
* @param string|bool ...$identifier
|
||||
* @return $this
|
||||
*/
|
||||
public function addReturn(int $count, ...$identifier): self
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
|
||||
$this->arguments[] = 'RETURN';
|
||||
|
||||
for ($i = 1, $iMax = count($arguments); $i < $iMax; $i++) {
|
||||
if (true === $arguments[$i]) {
|
||||
$arguments[$i] = 'AS';
|
||||
}
|
||||
}
|
||||
|
||||
$this->arguments = array_merge($this->arguments, $arguments);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the sections of the attribute that contain the matched text.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param int $frags
|
||||
* @param int $len
|
||||
* @param string $separator
|
||||
* @return $this
|
||||
*/
|
||||
public function summarize(array $fields = [], int $frags = 0, int $len = 0, string $separator = ''): self
|
||||
{
|
||||
$this->arguments[] = 'SUMMARIZE';
|
||||
|
||||
if (!empty($fields)) {
|
||||
$this->arguments[] = 'FIELDS';
|
||||
$this->arguments[] = count($fields);
|
||||
$this->arguments = array_merge($this->arguments, $fields);
|
||||
}
|
||||
|
||||
if ($frags !== 0) {
|
||||
$this->arguments[] = 'FRAGS';
|
||||
$this->arguments[] = $frags;
|
||||
}
|
||||
|
||||
if ($len !== 0) {
|
||||
$this->arguments[] = 'LEN';
|
||||
$this->arguments[] = $len;
|
||||
}
|
||||
|
||||
if ($separator !== '') {
|
||||
$this->arguments[] = 'SEPARATOR';
|
||||
$this->arguments[] = $separator;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats occurrences of matched text.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param string $openTag
|
||||
* @param string $closeTag
|
||||
* @return $this
|
||||
*/
|
||||
public function highlight(array $fields = [], string $openTag = '', string $closeTag = ''): self
|
||||
{
|
||||
$this->arguments[] = 'HIGHLIGHT';
|
||||
|
||||
if (!empty($fields)) {
|
||||
$this->arguments[] = 'FIELDS';
|
||||
$this->arguments[] = count($fields);
|
||||
$this->arguments = array_merge($this->arguments, $fields);
|
||||
}
|
||||
|
||||
if ($openTag !== '' && $closeTag !== '') {
|
||||
array_push($this->arguments, 'TAGS', $openTag, $closeTag);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a maximum of N intervening number of unmatched offsets between phrase terms.
|
||||
* In other words, the slop for exact phrases is 0.
|
||||
*
|
||||
* @param int $slop
|
||||
* @return $this
|
||||
*/
|
||||
public function slop(int $slop): self
|
||||
{
|
||||
$this->arguments[] = 'SLOP';
|
||||
$this->arguments[] = $slop;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the query terms in the same order in the document as in the query, regardless of the offsets between them.
|
||||
* Typically used in conjunction with SLOP.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function inOrder(): self
|
||||
{
|
||||
$this->arguments[] = 'INORDER';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses a custom query expander instead of the stemmer.
|
||||
*
|
||||
* @param string $expander
|
||||
* @return $this
|
||||
*/
|
||||
public function expander(string $expander): self
|
||||
{
|
||||
$this->arguments[] = 'EXPANDER';
|
||||
$this->arguments[] = $expander;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses a custom scoring function you define.
|
||||
*
|
||||
* @param string $scorer
|
||||
* @return $this
|
||||
*/
|
||||
public function scorer(string $scorer): self
|
||||
{
|
||||
$this->arguments[] = 'SCORER';
|
||||
$this->arguments[] = $scorer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a textual description of how the scores were calculated.
|
||||
* Using this options requires the WITHSCORES option.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function explainScore(): self
|
||||
{
|
||||
$this->arguments[] = 'EXPLAINSCORE';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders the results by the value of this attribute.
|
||||
* This applies to both text and numeric attributes.
|
||||
* Attributes needed for SORTBY should be declared as SORTABLE in the index, in order to be available with very low latency.
|
||||
* Note that this adds memory overhead.
|
||||
*
|
||||
* @param string $sortAttribute
|
||||
* @param string $orderBy
|
||||
* @return $this
|
||||
*/
|
||||
public function sortBy(string $sortAttribute, string $orderBy = 'asc'): self
|
||||
{
|
||||
$this->arguments[] = 'SORTBY';
|
||||
$this->arguments[] = $sortAttribute;
|
||||
|
||||
if (in_array(strtoupper($orderBy), $this->sortingEnum)) {
|
||||
$this->arguments[] = $this->sortingEnum[strtolower($orderBy)];
|
||||
} else {
|
||||
$enumValues = implode(', ', array_values($this->sortingEnum));
|
||||
throw new InvalidArgumentException("Wrong order direction value given. Currently supports: {$enumValues}");
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class SpellcheckArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $termsEnum = [
|
||||
'include' => 'INCLUDE',
|
||||
'exclude' => 'EXCLUDE',
|
||||
];
|
||||
|
||||
/**
|
||||
* Is maximum Levenshtein distance for spelling suggestions (default: 1, max: 4).
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function distance(int $distance): self
|
||||
{
|
||||
$this->arguments[] = 'DISTANCE';
|
||||
$this->arguments[] = $distance;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies an inclusion (INCLUDE) or exclusion (EXCLUDE) of a custom dictionary named {dict}.
|
||||
*
|
||||
* @param string $dictionary
|
||||
* @param string $modifier
|
||||
* @param string ...$terms
|
||||
* @return $this
|
||||
*/
|
||||
public function terms(string $dictionary, string $modifier = 'INCLUDE', string ...$terms): self
|
||||
{
|
||||
if (!in_array(strtoupper($modifier), $this->termsEnum)) {
|
||||
$enumValues = implode(', ', array_values($this->termsEnum));
|
||||
throw new InvalidArgumentException("Wrong modifier value given. Currently supports: {$enumValues}");
|
||||
}
|
||||
|
||||
array_push($this->arguments, 'TERMS', $this->termsEnum[strtolower($modifier)], $dictionary, ...$terms);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
class SugAddArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* Adds INCR modifier.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function incr(): self
|
||||
{
|
||||
$this->arguments[] = 'INCR';
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
class SugGetArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* Performs a fuzzy prefix search, including prefixes at Levenshtein distance of 1 from the prefix sent.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fuzzy(): self
|
||||
{
|
||||
$this->arguments[] = 'FUZZY';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the results to a maximum of num (default: 5).
|
||||
*
|
||||
* @param int $num
|
||||
* @return $this
|
||||
*/
|
||||
public function max(int $num): self
|
||||
{
|
||||
array_push($this->arguments, 'MAX', $num);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
class SynUpdateArguments extends CommonArguments
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Server;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
interface LimitInterface extends ArrayableArgument
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Server;
|
||||
|
||||
class LimitOffsetCount implements LimitInterface
|
||||
{
|
||||
private const KEYWORD = 'LIMIT';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $offset;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $count;
|
||||
|
||||
public function __construct(int $offset, int $count)
|
||||
{
|
||||
$this->offset = $offset;
|
||||
$this->count = $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [self::KEYWORD, $this->offset, $this->count];
|
||||
}
|
||||
}
|
||||
57
plugins/cache-redis/Predis/Command/Argument/Server/To.php
Normal file
57
plugins/cache-redis/Predis/Command/Argument/Server/To.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Server;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
class To implements ArrayableArgument
|
||||
{
|
||||
private const KEYWORD = 'TO';
|
||||
private const FORCE_KEYWORD = 'FORCE';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $host;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $port;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isForce;
|
||||
|
||||
public function __construct(string $host, int $port, bool $isForce = false)
|
||||
{
|
||||
$this->host = $host;
|
||||
$this->port = $port;
|
||||
$this->isForce = $isForce;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$arguments = [self::KEYWORD, $this->host, $this->port];
|
||||
|
||||
if ($this->isForce) {
|
||||
$arguments[] = self::FORCE_KEYWORD;
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
class AddArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* Is overwrite key and database configuration for DUPLICATE_POLICY,
|
||||
* the policy for handling samples with identical timestamps.
|
||||
*
|
||||
* @param string $policy
|
||||
* @return $this
|
||||
*/
|
||||
public function onDuplicate(string $policy = self::POLICY_BLOCK): self
|
||||
{
|
||||
array_push($this->arguments, 'ON_DUPLICATE', $policy);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
class AlterArguments extends CommonArguments
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
class CommonArguments implements ArrayableArgument
|
||||
{
|
||||
public const POLICY_BLOCK = 'BLOCK';
|
||||
public const POLICY_FIRST = 'FIRST';
|
||||
public const POLICY_LAST = 'LAST';
|
||||
public const POLICY_MIN = 'MIN';
|
||||
public const POLICY_MAX = 'MAX';
|
||||
public const POLICY_SUM = 'SUM';
|
||||
|
||||
public const ENCODING_UNCOMPRESSED = 'UNCOMPRESSED';
|
||||
public const ENCODING_COMPRESSED = 'COMPRESSED';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* Is maximum age for samples compared to the highest reported timestamp, in milliseconds.
|
||||
*
|
||||
* @param int $retentionPeriod
|
||||
* @return $this
|
||||
*/
|
||||
public function retentionMsecs(int $retentionPeriod): self
|
||||
{
|
||||
array_push($this->arguments, 'RETENTION', $retentionPeriod);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is initial allocation size, in bytes, for the data part of each new chunk.
|
||||
*
|
||||
* @param int $size
|
||||
* @return $this
|
||||
*/
|
||||
public function chunkSize(int $size): self
|
||||
{
|
||||
array_push($this->arguments, 'CHUNK_SIZE', $size);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is policy for handling insertion of multiple samples with identical timestamps.
|
||||
*
|
||||
* @param string $policy
|
||||
* @return $this
|
||||
*/
|
||||
public function duplicatePolicy(string $policy = self::POLICY_BLOCK): self
|
||||
{
|
||||
array_push($this->arguments, 'DUPLICATE_POLICY', $policy);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is set of label-value pairs that represent metadata labels of the key and serve as a secondary index.
|
||||
*
|
||||
* @param mixed ...$labelValuePair
|
||||
* @return $this
|
||||
*/
|
||||
public function labels(...$labelValuePair): self
|
||||
{
|
||||
array_push($this->arguments, 'LABELS', ...$labelValuePair);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the series samples encoding format.
|
||||
*
|
||||
* @param string $encoding
|
||||
* @return $this
|
||||
*/
|
||||
public function encoding(string $encoding = self::ENCODING_COMPRESSED): self
|
||||
{
|
||||
array_push($this->arguments, 'ENCODING', $encoding);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is used when a time series is a compaction.
|
||||
* With LATEST, TS.GET reports the compacted value of the latest, possibly partial, bucket.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function latest(): self
|
||||
{
|
||||
$this->arguments[] = 'LATEST';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes in the reply all label-value pairs representing metadata labels of the time series.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withLabels(): self
|
||||
{
|
||||
$this->arguments[] = 'WITHLABELS';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a subset of the label-value pairs that represent metadata labels of the time series.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function selectedLabels(string ...$labels): self
|
||||
{
|
||||
array_push($this->arguments, 'SELECTED_LABELS', ...$labels);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
class CreateArguments extends CommonArguments
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
class DecrByArguments extends IncrByArguments
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
class GetArguments extends CommonArguments
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
class IncrByArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* Is (integer) UNIX sample timestamp in milliseconds or * to set the timestamp according to the server clock.
|
||||
*
|
||||
* @param string|int $timeStamp
|
||||
* @return $this
|
||||
*/
|
||||
public function timestamp($timeStamp): self
|
||||
{
|
||||
array_push($this->arguments, 'TIMESTAMP', $timeStamp);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes data storage from compressed (default) to uncompressed.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function uncompressed(): self
|
||||
{
|
||||
$this->arguments[] = 'UNCOMPRESSED';
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
use Predis\Command\Argument\ArrayableArgument;
|
||||
|
||||
class InfoArguments implements ArrayableArgument
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $arguments = [];
|
||||
|
||||
/**
|
||||
* Is an optional flag to get a more detailed information about the chunks.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function debug(): self
|
||||
{
|
||||
$this->arguments[] = 'DEBUG';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
class MGetArguments extends CommonArguments
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
class MRangeArguments extends RangeArguments
|
||||
{
|
||||
/**
|
||||
* Filters time series based on their labels and label values.
|
||||
*
|
||||
* @param string ...$filterExpressions
|
||||
* @return $this
|
||||
*/
|
||||
public function filter(string ...$filterExpressions): self
|
||||
{
|
||||
array_push($this->arguments, 'FILTER', ...$filterExpressions);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits time series into groups, each group contains time series that share the same
|
||||
* value for the provided label name, then aggregates results in each group.
|
||||
*
|
||||
* @param string $label
|
||||
* @param string $reducer
|
||||
* @return $this
|
||||
*/
|
||||
public function groupBy(string $label, string $reducer): self
|
||||
{
|
||||
array_push($this->arguments, 'GROUPBY', $label, 'REDUCE', $reducer);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\TimeSeries;
|
||||
|
||||
class RangeArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* Filters samples by a list of specific timestamps.
|
||||
*
|
||||
* @param int ...$ts
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByTs(int ...$ts): self
|
||||
{
|
||||
array_push($this->arguments, 'FILTER_BY_TS', ...$ts);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters samples by minimum and maximum values.
|
||||
*
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByValue(int $min, int $max): self
|
||||
{
|
||||
array_push($this->arguments, 'FILTER_BY_VALUE', $min, $max);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the number of returned samples.
|
||||
*
|
||||
* @param int $count
|
||||
* @return $this
|
||||
*/
|
||||
public function count(int $count): self
|
||||
{
|
||||
array_push($this->arguments, 'COUNT', $count);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates samples into time buckets.
|
||||
*
|
||||
* @param string $aggregator
|
||||
* @param int $bucketDuration Is duration of each bucket, in milliseconds.
|
||||
* @param int $align It controls the time bucket timestamps by changing the reference timestamp on which a bucket is defined.
|
||||
* @param int $bucketTimestamp Controls how bucket timestamps are reported.
|
||||
* @param bool $empty Is a flag, which, when specified, reports aggregations also for empty buckets.
|
||||
* @return $this
|
||||
*/
|
||||
public function aggregation(string $aggregator, int $bucketDuration, int $align = 0, int $bucketTimestamp = 0, bool $empty = false): self
|
||||
{
|
||||
if ($align > 0) {
|
||||
array_push($this->arguments, 'ALIGN', $align);
|
||||
}
|
||||
|
||||
array_push($this->arguments, 'AGGREGATION', $aggregator, $bucketDuration);
|
||||
|
||||
if ($bucketTimestamp > 0) {
|
||||
array_push($this->arguments, 'BUCKETTIMESTAMP', $bucketTimestamp);
|
||||
}
|
||||
|
||||
if (true === $empty) {
|
||||
$this->arguments[] = 'EMPTY';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
126
plugins/cache-redis/Predis/Command/Command.php
Normal file
126
plugins/cache-redis/Predis/Command/Command.php
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Base class for Redis commands.
|
||||
*/
|
||||
abstract class Command implements CommandInterface
|
||||
{
|
||||
private $slot;
|
||||
private $arguments = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
unset($this->slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRawArguments(array $arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
unset($this->slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArguments()
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArgument($index)
|
||||
{
|
||||
if (isset($this->arguments[$index])) {
|
||||
return $this->arguments[$index];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSlot($slot)
|
||||
{
|
||||
$this->slot = $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSlot()
|
||||
{
|
||||
return $this->slot ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the arguments array passed to a Redis command.
|
||||
*
|
||||
* @param array $arguments Arguments for a command.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function normalizeArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 1 && isset($arguments[0]) && is_array($arguments[0])) {
|
||||
return $arguments[0];
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the arguments array passed to a variadic Redis command.
|
||||
*
|
||||
* @param array $arguments Arguments for a command.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function normalizeVariadic(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 2 && is_array($arguments[1])) {
|
||||
return array_merge([$arguments[0]], $arguments[1]);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all false values from arguments.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function filterArguments(): void
|
||||
{
|
||||
$this->arguments = array_filter($this->arguments, static function ($argument) {
|
||||
return $argument !== false && $argument !== null;
|
||||
});
|
||||
}
|
||||
}
|
||||
80
plugins/cache-redis/Predis/Command/CommandInterface.php
Normal file
80
plugins/cache-redis/Predis/Command/CommandInterface.php
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Defines an abstraction representing a Redis command.
|
||||
*/
|
||||
interface CommandInterface
|
||||
{
|
||||
/**
|
||||
* Returns the ID of the Redis command. By convention, command identifiers
|
||||
* must always be uppercase.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
/**
|
||||
* Assign the specified slot to the command for clustering distribution.
|
||||
*
|
||||
* @param int $slot Slot ID.
|
||||
*/
|
||||
public function setSlot($slot);
|
||||
|
||||
/**
|
||||
* Returns the assigned slot of the command for clustering distribution.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getSlot();
|
||||
|
||||
/**
|
||||
* Sets the arguments for the command.
|
||||
*
|
||||
* @param array $arguments List of arguments.
|
||||
*/
|
||||
public function setArguments(array $arguments);
|
||||
|
||||
/**
|
||||
* Sets the raw arguments for the command without processing them.
|
||||
*
|
||||
* @param array $arguments List of arguments.
|
||||
*/
|
||||
public function setRawArguments(array $arguments);
|
||||
|
||||
/**
|
||||
* Gets the arguments of the command.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getArguments();
|
||||
|
||||
/**
|
||||
* Gets the argument of the command at the specified index.
|
||||
*
|
||||
* @param int $index Index of the desired argument.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getArgument($index);
|
||||
|
||||
/**
|
||||
* Parses a raw response and returns a PHP object.
|
||||
*
|
||||
* @param string|array|null $data Binary string containing the whole response.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function parseResponse($data);
|
||||
}
|
||||
143
plugins/cache-redis/Predis/Command/Factory.php
Normal file
143
plugins/cache-redis/Predis/Command/Factory.php
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Predis\ClientException;
|
||||
use Predis\Command\Processor\ProcessorInterface;
|
||||
|
||||
/**
|
||||
* Base command factory class.
|
||||
*
|
||||
* This class provides all of the common functionalities required for a command
|
||||
* factory to create new instances of Redis commands objects. It also allows to
|
||||
* define or undefine command handler classes for each command ID.
|
||||
*/
|
||||
abstract class Factory implements FactoryInterface
|
||||
{
|
||||
protected $commands = [];
|
||||
protected $processor;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(string ...$commandIDs): bool
|
||||
{
|
||||
foreach ($commandIDs as $commandID) {
|
||||
if ($this->getCommandClass($commandID) === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FQCN of a class that represents the specified command ID.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $commandID Command ID
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCommandClass(string $commandID): ?string
|
||||
{
|
||||
return $this->commands[strtoupper($commandID)] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create(string $commandID, array $arguments = []): CommandInterface
|
||||
{
|
||||
if (!$commandClass = $this->getCommandClass($commandID)) {
|
||||
$commandID = strtoupper($commandID);
|
||||
|
||||
throw new ClientException("Command `$commandID` is not a registered Redis command.");
|
||||
}
|
||||
|
||||
$command = new $commandClass();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
if (isset($this->processor)) {
|
||||
$this->processor->process($command);
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a command in the factory.
|
||||
*
|
||||
* Only classes implementing Predis\Command\CommandInterface are allowed to
|
||||
* handle a command. If the command specified by its ID is already handled
|
||||
* by the factory, the underlying command class is replaced by the new one.
|
||||
*
|
||||
* @param string $commandID Command ID
|
||||
* @param string $commandClass FQCN of a class implementing Predis\Command\CommandInterface
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function define(string $commandID, string $commandClass): void
|
||||
{
|
||||
if (!is_a($commandClass, 'Predis\Command\CommandInterface', true)) {
|
||||
throw new InvalidArgumentException(
|
||||
"Class $commandClass must implement Predis\Command\CommandInterface"
|
||||
);
|
||||
}
|
||||
|
||||
$this->commands[strtoupper($commandID)] = $commandClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undefines a command in the factory.
|
||||
*
|
||||
* When the factory already has a class handler associated to the specified
|
||||
* command ID it is removed from the map of known commands. Nothing happens
|
||||
* when the command is not handled by the factory.
|
||||
*
|
||||
* @param string $commandID Command ID
|
||||
*/
|
||||
public function undefine(string $commandID): void
|
||||
{
|
||||
unset($this->commands[strtoupper($commandID)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a command processor for processing command arguments.
|
||||
*
|
||||
* Command processors are used to process and transform arguments of Redis
|
||||
* commands before their newly created instances are returned to the caller
|
||||
* of "create()".
|
||||
*
|
||||
* A NULL value can be used to effectively unset any processor if previously
|
||||
* set for the command factory.
|
||||
*
|
||||
* @param ProcessorInterface|null $processor Command processor or NULL value.
|
||||
*/
|
||||
public function setProcessor(?ProcessorInterface $processor): void
|
||||
{
|
||||
$this->processor = $processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current command processor.
|
||||
*
|
||||
* @return ProcessorInterface|null
|
||||
*/
|
||||
public function getProcessor(): ?ProcessorInterface
|
||||
{
|
||||
return $this->processor;
|
||||
}
|
||||
}
|
||||
42
plugins/cache-redis/Predis/Command/FactoryInterface.php
Normal file
42
plugins/cache-redis/Predis/Command/FactoryInterface.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Command factory interface.
|
||||
*
|
||||
* A command factory is used through the library to create instances of commands
|
||||
* classes implementing Predis\Command\CommandInterface mapped to Redis commands
|
||||
* by their command ID string (SET, GET, etc...).
|
||||
*/
|
||||
interface FactoryInterface
|
||||
{
|
||||
/**
|
||||
* Checks if the command factory supports the specified list of commands.
|
||||
*
|
||||
* @param string ...$commandIDs List of command IDs
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(string ...$commandIDs): bool;
|
||||
|
||||
/**
|
||||
* Creates a new command instance.
|
||||
*
|
||||
* @param string $commandID Command ID
|
||||
* @param array $arguments Arguments for the command
|
||||
*
|
||||
* @return CommandInterface
|
||||
*/
|
||||
public function create(string $commandID, array $arguments = []): CommandInterface;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Defines a command whose keys can be prefixed.
|
||||
*/
|
||||
interface PrefixableCommandInterface extends CommandInterface
|
||||
{
|
||||
/**
|
||||
* Prefixes all the keys found in the arguments of the command.
|
||||
*
|
||||
* @param string $prefix String used to prefix the keys.
|
||||
*/
|
||||
public function prefixKeys($prefix);
|
||||
}
|
||||
|
|
@ -0,0 +1,577 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Processor;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\PrefixableCommandInterface;
|
||||
|
||||
/**
|
||||
* Command processor capable of prefixing keys stored in the arguments of Redis
|
||||
* commands supported.
|
||||
*/
|
||||
class KeyPrefixProcessor implements ProcessorInterface
|
||||
{
|
||||
private $prefix;
|
||||
private $commands;
|
||||
|
||||
/**
|
||||
* @param string $prefix Prefix for the keys.
|
||||
*/
|
||||
public function __construct($prefix)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
|
||||
$prefixFirst = static::class . '::first';
|
||||
$prefixAll = static::class . '::all';
|
||||
$prefixInterleaved = static::class . '::interleaved';
|
||||
$prefixSkipFirst = static::class . '::skipFirst';
|
||||
$prefixSkipLast = static::class . '::skipLast';
|
||||
$prefixSort = static::class . '::sort';
|
||||
$prefixEvalKeys = static::class . '::evalKeys';
|
||||
$prefixZsetStore = static::class . '::zsetStore';
|
||||
$prefixMigrate = static::class . '::migrate';
|
||||
$prefixGeoradius = static::class . '::georadius';
|
||||
|
||||
$this->commands = [
|
||||
/* ---------------- Redis 1.2 ---------------- */
|
||||
'EXISTS' => $prefixAll,
|
||||
'DEL' => $prefixAll,
|
||||
'TYPE' => $prefixFirst,
|
||||
'KEYS' => $prefixFirst,
|
||||
'RENAME' => $prefixAll,
|
||||
'RENAMENX' => $prefixAll,
|
||||
'EXPIRE' => $prefixFirst,
|
||||
'EXPIREAT' => $prefixFirst,
|
||||
'TTL' => $prefixFirst,
|
||||
'MOVE' => $prefixFirst,
|
||||
'SORT' => $prefixSort,
|
||||
'DUMP' => $prefixFirst,
|
||||
'RESTORE' => $prefixFirst,
|
||||
'SET' => $prefixFirst,
|
||||
'SETNX' => $prefixFirst,
|
||||
'MSET' => $prefixInterleaved,
|
||||
'MSETNX' => $prefixInterleaved,
|
||||
'GET' => $prefixFirst,
|
||||
'MGET' => $prefixAll,
|
||||
'GETSET' => $prefixFirst,
|
||||
'INCR' => $prefixFirst,
|
||||
'INCRBY' => $prefixFirst,
|
||||
'DECR' => $prefixFirst,
|
||||
'DECRBY' => $prefixFirst,
|
||||
'RPUSH' => $prefixFirst,
|
||||
'LPUSH' => $prefixFirst,
|
||||
'LLEN' => $prefixFirst,
|
||||
'LRANGE' => $prefixFirst,
|
||||
'LTRIM' => $prefixFirst,
|
||||
'LINDEX' => $prefixFirst,
|
||||
'LSET' => $prefixFirst,
|
||||
'LREM' => $prefixFirst,
|
||||
'LPOP' => $prefixFirst,
|
||||
'RPOP' => $prefixFirst,
|
||||
'RPOPLPUSH' => $prefixAll,
|
||||
'SADD' => $prefixFirst,
|
||||
'SREM' => $prefixFirst,
|
||||
'SPOP' => $prefixFirst,
|
||||
'SMOVE' => $prefixSkipLast,
|
||||
'SCARD' => $prefixFirst,
|
||||
'SISMEMBER' => $prefixFirst,
|
||||
'SINTER' => $prefixAll,
|
||||
'SINTERSTORE' => $prefixAll,
|
||||
'SUNION' => $prefixAll,
|
||||
'SUNIONSTORE' => $prefixAll,
|
||||
'SDIFF' => $prefixAll,
|
||||
'SDIFFSTORE' => $prefixAll,
|
||||
'SMEMBERS' => $prefixFirst,
|
||||
'SRANDMEMBER' => $prefixFirst,
|
||||
'ZADD' => $prefixFirst,
|
||||
'ZINCRBY' => $prefixFirst,
|
||||
'ZREM' => $prefixFirst,
|
||||
'ZRANGE' => $prefixFirst,
|
||||
'ZREVRANGE' => $prefixFirst,
|
||||
'ZRANGEBYSCORE' => $prefixFirst,
|
||||
'ZCARD' => $prefixFirst,
|
||||
'ZSCORE' => $prefixFirst,
|
||||
'ZREMRANGEBYSCORE' => $prefixFirst,
|
||||
/* ---------------- Redis 2.0 ---------------- */
|
||||
'SETEX' => $prefixFirst,
|
||||
'APPEND' => $prefixFirst,
|
||||
'SUBSTR' => $prefixFirst,
|
||||
'BLPOP' => $prefixSkipLast,
|
||||
'BRPOP' => $prefixSkipLast,
|
||||
'ZUNIONSTORE' => $prefixZsetStore,
|
||||
'ZINTERSTORE' => $prefixZsetStore,
|
||||
'ZCOUNT' => $prefixFirst,
|
||||
'ZRANK' => $prefixFirst,
|
||||
'ZREVRANK' => $prefixFirst,
|
||||
'ZREMRANGEBYRANK' => $prefixFirst,
|
||||
'HSET' => $prefixFirst,
|
||||
'HSETNX' => $prefixFirst,
|
||||
'HMSET' => $prefixFirst,
|
||||
'HINCRBY' => $prefixFirst,
|
||||
'HGET' => $prefixFirst,
|
||||
'HMGET' => $prefixFirst,
|
||||
'HDEL' => $prefixFirst,
|
||||
'HEXISTS' => $prefixFirst,
|
||||
'HLEN' => $prefixFirst,
|
||||
'HKEYS' => $prefixFirst,
|
||||
'HVALS' => $prefixFirst,
|
||||
'HGETALL' => $prefixFirst,
|
||||
'SUBSCRIBE' => $prefixAll,
|
||||
'UNSUBSCRIBE' => $prefixAll,
|
||||
'PSUBSCRIBE' => $prefixAll,
|
||||
'PUNSUBSCRIBE' => $prefixAll,
|
||||
'PUBLISH' => $prefixFirst,
|
||||
/* ---------------- Redis 2.2 ---------------- */
|
||||
'PERSIST' => $prefixFirst,
|
||||
'STRLEN' => $prefixFirst,
|
||||
'SETRANGE' => $prefixFirst,
|
||||
'GETRANGE' => $prefixFirst,
|
||||
'SETBIT' => $prefixFirst,
|
||||
'GETBIT' => $prefixFirst,
|
||||
'RPUSHX' => $prefixFirst,
|
||||
'LPUSHX' => $prefixFirst,
|
||||
'LINSERT' => $prefixFirst,
|
||||
'BRPOPLPUSH' => $prefixSkipLast,
|
||||
'ZREVRANGEBYSCORE' => $prefixFirst,
|
||||
'WATCH' => $prefixAll,
|
||||
/* ---------------- Redis 2.6 ---------------- */
|
||||
'PTTL' => $prefixFirst,
|
||||
'PEXPIRE' => $prefixFirst,
|
||||
'PEXPIREAT' => $prefixFirst,
|
||||
'PSETEX' => $prefixFirst,
|
||||
'INCRBYFLOAT' => $prefixFirst,
|
||||
'BITOP' => $prefixSkipFirst,
|
||||
'BITCOUNT' => $prefixFirst,
|
||||
'HINCRBYFLOAT' => $prefixFirst,
|
||||
'EVAL' => $prefixEvalKeys,
|
||||
'EVALSHA' => $prefixEvalKeys,
|
||||
'MIGRATE' => $prefixMigrate,
|
||||
/* ---------------- Redis 2.8 ---------------- */
|
||||
'SSCAN' => $prefixFirst,
|
||||
'ZSCAN' => $prefixFirst,
|
||||
'HSCAN' => $prefixFirst,
|
||||
'PFADD' => $prefixFirst,
|
||||
'PFCOUNT' => $prefixAll,
|
||||
'PFMERGE' => $prefixAll,
|
||||
'ZLEXCOUNT' => $prefixFirst,
|
||||
'ZRANGEBYLEX' => $prefixFirst,
|
||||
'ZREMRANGEBYLEX' => $prefixFirst,
|
||||
'ZREVRANGEBYLEX' => $prefixFirst,
|
||||
'BITPOS' => $prefixFirst,
|
||||
/* ---------------- Redis 3.2 ---------------- */
|
||||
'HSTRLEN' => $prefixFirst,
|
||||
'BITFIELD' => $prefixFirst,
|
||||
'GEOADD' => $prefixFirst,
|
||||
'GEOHASH' => $prefixFirst,
|
||||
'GEOPOS' => $prefixFirst,
|
||||
'GEODIST' => $prefixFirst,
|
||||
'GEORADIUS' => $prefixGeoradius,
|
||||
'GEORADIUSBYMEMBER' => $prefixGeoradius,
|
||||
/* ---------------- Redis 5.0 ---------------- */
|
||||
'XADD' => $prefixFirst,
|
||||
'XRANGE' => $prefixFirst,
|
||||
'XREVRANGE' => $prefixFirst,
|
||||
'XDEL' => $prefixFirst,
|
||||
'XLEN' => $prefixFirst,
|
||||
'XACK' => $prefixFirst,
|
||||
'XTRIM' => $prefixFirst,
|
||||
|
||||
/* ---------------- Redis 6.2 ---------------- */
|
||||
'GETDEL' => $prefixFirst,
|
||||
|
||||
/* ---------------- Redis 7.0 ---------------- */
|
||||
'EXPIRETIME' => $prefixFirst,
|
||||
|
||||
/* RedisJSON */
|
||||
'JSON.ARRAPPEND' => $prefixFirst,
|
||||
'JSON.ARRINDEX' => $prefixFirst,
|
||||
'JSON.ARRINSERT' => $prefixFirst,
|
||||
'JSON.ARRLEN' => $prefixFirst,
|
||||
'JSON.ARRPOP' => $prefixFirst,
|
||||
'JSON.ARRTRIM' => $prefixFirst,
|
||||
'JSON.CLEAR' => $prefixFirst,
|
||||
'JSON.DEBUG MEMORY' => $prefixFirst,
|
||||
'JSON.DEL' => $prefixFirst,
|
||||
'JSON.FORGET' => $prefixFirst,
|
||||
'JSON.GET' => $prefixFirst,
|
||||
'JSON.MGET' => $prefixAll,
|
||||
'JSON.NUMINCRBY' => $prefixFirst,
|
||||
'JSON.OBJKEYS' => $prefixFirst,
|
||||
'JSON.OBJLEN' => $prefixFirst,
|
||||
'JSON.RESP' => $prefixFirst,
|
||||
'JSON.SET' => $prefixFirst,
|
||||
'JSON.STRAPPEND' => $prefixFirst,
|
||||
'JSON.STRLEN' => $prefixFirst,
|
||||
'JSON.TOGGLE' => $prefixFirst,
|
||||
'JSON.TYPE' => $prefixFirst,
|
||||
|
||||
/* RedisBloom */
|
||||
'BF.ADD' => $prefixFirst,
|
||||
'BF.EXISTS' => $prefixFirst,
|
||||
'BF.INFO' => $prefixFirst,
|
||||
'BF.INSERT' => $prefixFirst,
|
||||
'BF.LOADCHUNK' => $prefixFirst,
|
||||
'BF.MADD' => $prefixFirst,
|
||||
'BF.MEXISTS' => $prefixFirst,
|
||||
'BF.RESERVE' => $prefixFirst,
|
||||
'BF.SCANDUMP' => $prefixFirst,
|
||||
'CF.ADD' => $prefixFirst,
|
||||
'CF.ADDNX' => $prefixFirst,
|
||||
'CF.COUNT' => $prefixFirst,
|
||||
'CF.DEL' => $prefixFirst,
|
||||
'CF.EXISTS' => $prefixFirst,
|
||||
'CF.INFO' => $prefixFirst,
|
||||
'CF.INSERT' => $prefixFirst,
|
||||
'CF.INSERTNX' => $prefixFirst,
|
||||
'CF.LOADCHUNK' => $prefixFirst,
|
||||
'CF.MEXISTS' => $prefixFirst,
|
||||
'CF.RESERVE' => $prefixFirst,
|
||||
'CF.SCANDUMP' => $prefixFirst,
|
||||
'CMS.INCRBY' => $prefixFirst,
|
||||
'CMS.INFO' => $prefixFirst,
|
||||
'CMS.INITBYDIM' => $prefixFirst,
|
||||
'CMS.INITBYPROB' => $prefixFirst,
|
||||
'CMS.QUERY' => $prefixFirst,
|
||||
'TDIGEST.ADD' => $prefixFirst,
|
||||
'TDIGEST.BYRANK' => $prefixFirst,
|
||||
'TDIGEST.BYREVRANK' => $prefixFirst,
|
||||
'TDIGEST.CDF' => $prefixFirst,
|
||||
'TDIGEST.CREATE' => $prefixFirst,
|
||||
'TDIGEST.INFO' => $prefixFirst,
|
||||
'TDIGEST.MAX' => $prefixFirst,
|
||||
'TDIGEST.MIN' => $prefixFirst,
|
||||
'TDIGEST.QUANTILE' => $prefixFirst,
|
||||
'TDIGEST.RANK' => $prefixFirst,
|
||||
'TDIGEST.RESET' => $prefixFirst,
|
||||
'TDIGEST.REVRANK' => $prefixFirst,
|
||||
'TDIGEST.TRIMMED_MEAN' => $prefixFirst,
|
||||
'TOPK.ADD' => $prefixFirst,
|
||||
'TOPK.INCRBY' => $prefixFirst,
|
||||
'TOPK.INFO' => $prefixFirst,
|
||||
'TOPK.LIST' => $prefixFirst,
|
||||
'TOPK.QUERY' => $prefixFirst,
|
||||
'TOPK.RESERVE' => $prefixFirst,
|
||||
|
||||
/* RediSearch */
|
||||
'FT.AGGREGATE' => $prefixFirst,
|
||||
'FT.ALTER' => $prefixFirst,
|
||||
'FT.CREATE' => $prefixFirst,
|
||||
'FT.CURSOR DEL' => $prefixFirst,
|
||||
'FT.CURSOR READ' => $prefixFirst,
|
||||
'FT.DROPINDEX' => $prefixFirst,
|
||||
'FT.EXPLAIN' => $prefixFirst,
|
||||
'FT.INFO' => $prefixFirst,
|
||||
'FT.PROFILE' => $prefixFirst,
|
||||
'FT.SEARCH' => $prefixFirst,
|
||||
'FT.SPELLCHECK' => $prefixFirst,
|
||||
'FT.SYNDUMP' => $prefixFirst,
|
||||
'FT.SYNUPDATE' => $prefixFirst,
|
||||
'FT.TAGVALS' => $prefixFirst,
|
||||
|
||||
/* Redis TimeSeries */
|
||||
'TS.ADD' => $prefixFirst,
|
||||
'TS.ALTER' => $prefixFirst,
|
||||
'TS.CREATE' => $prefixFirst,
|
||||
'TS.DECRBY' => $prefixFirst,
|
||||
'TS.DEL' => $prefixFirst,
|
||||
'TS.GET' => $prefixFirst,
|
||||
'TS.INCRBY' => $prefixFirst,
|
||||
'TS.INFO' => $prefixFirst,
|
||||
'TS.MGET' => $prefixFirst,
|
||||
'TS.MRANGE' => $prefixFirst,
|
||||
'TS.MREVRANGE' => $prefixFirst,
|
||||
'TS.QUERYINDEX' => $prefixFirst,
|
||||
'TS.RANGE' => $prefixFirst,
|
||||
'TS.REVRANGE' => $prefixFirst,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a prefix that is applied to all the keys.
|
||||
*
|
||||
* @param string $prefix Prefix for the keys.
|
||||
*/
|
||||
public function setPrefix($prefix)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current prefix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrefix()
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(CommandInterface $command)
|
||||
{
|
||||
if ($command instanceof PrefixableCommandInterface) {
|
||||
$command->prefixKeys($this->prefix);
|
||||
} elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
|
||||
$this->commands[$commandID]($command, $this->prefix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an handler for the specified command ID.
|
||||
*
|
||||
* The callback signature must have 2 parameters of the following types:
|
||||
*
|
||||
* - Predis\Command\CommandInterface (command instance)
|
||||
* - String (prefix)
|
||||
*
|
||||
* When the callback argument is omitted or NULL, the previously
|
||||
* associated handler for the specified command ID is removed.
|
||||
*
|
||||
* @param string $commandID The ID of the command to be handled.
|
||||
* @param mixed $callback A valid callable object or NULL.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setCommandHandler($commandID, $callback = null)
|
||||
{
|
||||
$commandID = strtoupper($commandID);
|
||||
|
||||
if (!isset($callback)) {
|
||||
unset($this->commands[$commandID]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_callable($callback)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Callback must be a valid callable object or NULL'
|
||||
);
|
||||
}
|
||||
|
||||
$this->commands[$commandID] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getPrefix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix only the first argument.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function first(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
$arguments[0] = "$prefix{$arguments[0]}";
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to all the arguments.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function all(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
foreach ($arguments as &$key) {
|
||||
$key = "$prefix$key";
|
||||
}
|
||||
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix only to even arguments in the list.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function interleaved(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
$length = count($arguments);
|
||||
|
||||
for ($i = 0; $i < $length; $i += 2) {
|
||||
$arguments[$i] = "$prefix{$arguments[$i]}";
|
||||
}
|
||||
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to all the arguments but the first one.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function skipFirst(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
$length = count($arguments);
|
||||
|
||||
for ($i = 1; $i < $length; ++$i) {
|
||||
$arguments[$i] = "$prefix{$arguments[$i]}";
|
||||
}
|
||||
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to all the arguments but the last one.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function skipLast(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
$length = count($arguments);
|
||||
|
||||
for ($i = 0; $i < $length - 1; ++$i) {
|
||||
$arguments[$i] = "$prefix{$arguments[$i]}";
|
||||
}
|
||||
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to the keys of a SORT command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function sort(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
$arguments[0] = "$prefix{$arguments[0]}";
|
||||
|
||||
if (($count = count($arguments)) > 1) {
|
||||
for ($i = 1; $i < $count; ++$i) {
|
||||
switch (strtoupper($arguments[$i])) {
|
||||
case 'BY':
|
||||
case 'STORE':
|
||||
$arguments[$i] = "$prefix{$arguments[++$i]}";
|
||||
break;
|
||||
|
||||
case 'GET':
|
||||
$value = $arguments[++$i];
|
||||
if ($value !== '#') {
|
||||
$arguments[$i] = "$prefix$value";
|
||||
}
|
||||
break;
|
||||
|
||||
case 'LIMIT':
|
||||
$i += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to the keys of an EVAL-based command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function evalKeys(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
for ($i = 2; $i < $arguments[1] + 2; ++$i) {
|
||||
$arguments[$i] = "$prefix{$arguments[$i]}";
|
||||
}
|
||||
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to the keys of Z[INTERSECTION|UNION]STORE.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function zsetStore(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
$arguments[0] = "$prefix{$arguments[0]}";
|
||||
$length = ((int) $arguments[1]) + 2;
|
||||
|
||||
for ($i = 2; $i < $length; ++$i) {
|
||||
$arguments[$i] = "$prefix{$arguments[$i]}";
|
||||
}
|
||||
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to the key of a MIGRATE command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function migrate(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
$arguments[2] = "$prefix{$arguments[2]}";
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to the key of a GEORADIUS command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function georadius(CommandInterface $command, $prefix)
|
||||
{
|
||||
if ($arguments = $command->getArguments()) {
|
||||
$arguments[0] = "$prefix{$arguments[0]}";
|
||||
$startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
|
||||
|
||||
if (($count = count($arguments)) > $startIndex) {
|
||||
for ($i = $startIndex; $i < $count; ++$i) {
|
||||
switch (strtoupper($arguments[$i])) {
|
||||
case 'STORE':
|
||||
case 'STOREDIST':
|
||||
$arguments[$i] = "$prefix{$arguments[++$i]}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
142
plugins/cache-redis/Predis/Command/Processor/ProcessorChain.php
Normal file
142
plugins/cache-redis/Predis/Command/Processor/ProcessorChain.php
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Processor;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use InvalidArgumentException;
|
||||
use Predis\Command\CommandInterface;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Default implementation of a command processors chain.
|
||||
*/
|
||||
class ProcessorChain implements ArrayAccess, ProcessorInterface
|
||||
{
|
||||
private $processors = [];
|
||||
|
||||
/**
|
||||
* @param array $processors List of instances of ProcessorInterface.
|
||||
*/
|
||||
public function __construct($processors = [])
|
||||
{
|
||||
foreach ($processors as $processor) {
|
||||
$this->add($processor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add(ProcessorInterface $processor)
|
||||
{
|
||||
$this->processors[] = $processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove(ProcessorInterface $processor)
|
||||
{
|
||||
if (false !== $index = array_search($processor, $this->processors, true)) {
|
||||
unset($this[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(CommandInterface $command)
|
||||
{
|
||||
for ($i = 0; $i < $count = count($this->processors); ++$i) {
|
||||
$this->processors[$i]->process($command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProcessors()
|
||||
{
|
||||
return $this->processors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the list of command processor in the chain.
|
||||
*
|
||||
* @return Traversable<int, ProcessorInterface>
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->processors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of command processors in the chain.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->processors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetExists($index)
|
||||
{
|
||||
return isset($this->processors[$index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
* @return ProcessorInterface
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($index)
|
||||
{
|
||||
return $this->processors[$index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
* @param ProcessorInterface $processor
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetSet($index, $processor)
|
||||
{
|
||||
if (!$processor instanceof ProcessorInterface) {
|
||||
throw new InvalidArgumentException(
|
||||
'Processor chain accepts only instances of `Predis\Command\Processor\ProcessorInterface`'
|
||||
);
|
||||
}
|
||||
|
||||
$this->processors[$index] = $processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetUnset($index)
|
||||
{
|
||||
unset($this->processors[$index]);
|
||||
$this->processors = array_values($this->processors);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Processor;
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
|
||||
/**
|
||||
* A command processor processes Redis commands before they are sent to Redis.
|
||||
*/
|
||||
interface ProcessorInterface
|
||||
{
|
||||
/**
|
||||
* Processes the given Redis command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*/
|
||||
public function process(CommandInterface $command);
|
||||
}
|
||||
123
plugins/cache-redis/Predis/Command/RawCommand.php
Normal file
123
plugins/cache-redis/Predis/Command/RawCommand.php
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Class representing a generic Redis command.
|
||||
*
|
||||
* Arguments and responses for these commands are not normalized and they follow
|
||||
* what is defined by the Redis documentation.
|
||||
*
|
||||
* Raw commands can be useful when implementing higher level abstractions on top
|
||||
* of Predis\Client or managing internals like Redis Sentinel or Cluster as they
|
||||
* are not potentially subject to hijacking from third party libraries when they
|
||||
* override command handlers for standard Redis commands.
|
||||
*/
|
||||
final class RawCommand implements CommandInterface
|
||||
{
|
||||
private $slot;
|
||||
private $commandID;
|
||||
private $arguments;
|
||||
|
||||
/**
|
||||
* @param string $commandID Command ID
|
||||
* @param array $arguments Command arguments
|
||||
*/
|
||||
public function __construct($commandID, array $arguments = [])
|
||||
{
|
||||
$this->commandID = strtoupper($commandID);
|
||||
$this->setArguments($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new raw command using a variadic method.
|
||||
*
|
||||
* @param string $commandID Redis command ID
|
||||
* @param string ...$args Arguments list for the command
|
||||
*
|
||||
* @return CommandInterface
|
||||
*/
|
||||
public static function create($commandID, ...$args)
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
|
||||
return new static(array_shift($arguments), $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->commandID;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
unset($this->slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRawArguments(array $arguments)
|
||||
{
|
||||
$this->setArguments($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArguments()
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArgument($index)
|
||||
{
|
||||
if (isset($this->arguments[$index])) {
|
||||
return $this->arguments[$index];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSlot($slot)
|
||||
{
|
||||
$this->slot = $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSlot()
|
||||
{
|
||||
return $this->slot ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
43
plugins/cache-redis/Predis/Command/RawFactory.php
Normal file
43
plugins/cache-redis/Predis/Command/RawFactory.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Command factory creating raw command instances out of command IDs.
|
||||
*
|
||||
* Any command ID will produce a command instance even for unknown commands that
|
||||
* are not implemented by Redis (the server will return a "-ERR unknown command"
|
||||
* error responses).
|
||||
*
|
||||
* When using this factory the client does not process arguments before sending
|
||||
* commands to Redis and server responses are not further processed before being
|
||||
* returned to the caller.
|
||||
*/
|
||||
class RawFactory implements FactoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(string ...$commandIDs): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create(string $commandID, array $arguments = []): CommandInterface
|
||||
{
|
||||
return new RawCommand($commandID, $arguments);
|
||||
}
|
||||
}
|
||||
53
plugins/cache-redis/Predis/Command/Redis/ACL.php
Normal file
53
plugins/cache-redis/Predis/Command/Redis/ACL.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @see https://redis.io/commands/?name=ACL
|
||||
*
|
||||
* Container command corresponds to any ACL *.
|
||||
* Represents any ACL command with subcommand as first argument.
|
||||
*/
|
||||
class ACL extends RedisCommand
|
||||
{
|
||||
public function getId()
|
||||
{
|
||||
return 'ACL';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
if (!is_array($data)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ($data === array_values($data)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// flatten Relay (RESP3) maps
|
||||
$return = [];
|
||||
|
||||
array_walk($data, function ($value, $key) use (&$return) {
|
||||
$return[] = $key;
|
||||
$return[] = $value;
|
||||
});
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
29
plugins/cache-redis/Predis/Command/Redis/APPEND.php
Normal file
29
plugins/cache-redis/Predis/Command/Redis/APPEND.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @see http://redis.io/commands/append
|
||||
*/
|
||||
class APPEND extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'APPEND';
|
||||
}
|
||||
}
|
||||
29
plugins/cache-redis/Predis/Command/Redis/AUTH.php
Normal file
29
plugins/cache-redis/Predis/Command/Redis/AUTH.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @see http://redis.io/commands/auth
|
||||
*/
|
||||
class AUTH extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'AUTH';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis\AbstractCommand;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
use Predis\Command\Traits\Keys;
|
||||
|
||||
abstract class BZPOPBase extends RedisCommand
|
||||
{
|
||||
use Keys {
|
||||
Keys::setArguments as setKeys;
|
||||
}
|
||||
|
||||
protected static $keysArgumentPositionOffset = 0;
|
||||
|
||||
abstract public function getId(): string;
|
||||
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$this->setKeys($arguments, false);
|
||||
}
|
||||
|
||||
public function parseResponse($data)
|
||||
{
|
||||
$key = array_shift($data);
|
||||
|
||||
if (null === $key) {
|
||||
return [$key];
|
||||
}
|
||||
|
||||
return array_combine([$key], [[$data[0] => $data[1]]]);
|
||||
}
|
||||
}
|
||||
37
plugins/cache-redis/Predis/Command/Redis/BGREWRITEAOF.php
Normal file
37
plugins/cache-redis/Predis/Command/Redis/BGREWRITEAOF.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @see http://redis.io/commands/bgrewriteaof
|
||||
*/
|
||||
class BGREWRITEAOF extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'BGREWRITEAOF';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return $data == 'Background append only file rewriting started';
|
||||
}
|
||||
}
|
||||
37
plugins/cache-redis/Predis/Command/Redis/BGSAVE.php
Normal file
37
plugins/cache-redis/Predis/Command/Redis/BGSAVE.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @see http://redis.io/commands/bgsave
|
||||
*/
|
||||
class BGSAVE extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'BGSAVE';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return $data === 'Background saving started' ? true : $data;
|
||||
}
|
||||
}
|
||||
34
plugins/cache-redis/Predis/Command/Redis/BITCOUNT.php
Normal file
34
plugins/cache-redis/Predis/Command/Redis/BITCOUNT.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
use Predis\Command\Traits\BitByte;
|
||||
|
||||
/**
|
||||
* @see http://redis.io/commands/bitcount
|
||||
*
|
||||
* Count the number of set bits (population counting) in a string.
|
||||
*/
|
||||
class BITCOUNT extends RedisCommand
|
||||
{
|
||||
use BitByte;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'BITCOUNT';
|
||||
}
|
||||
}
|
||||
29
plugins/cache-redis/Predis/Command/Redis/BITFIELD.php
Normal file
29
plugins/cache-redis/Predis/Command/Redis/BITFIELD.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @see http://redis.io/commands/bitfield
|
||||
*/
|
||||
class BITFIELD extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'BITFIELD';
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue