Auth-Fundament: JWT + Redis (Phase 1, non-breaking)
Vorbereitung für pod-unabhängige Sessions (k8s-Ziel). Ändert am Live-System NICHTS: AUTH_BACKEND='session' bleibt Default, die neuen Libs sind noch nirgends verdrahtet. - composer eingeführt: firebase/php-jwt ^6.11 + predis/predis ^2.4 (pure PHP, keine Redis-Extension nötig → läuft auf XAMPP wie im Prod-Image). vendor/ committed (kein Composer auf dem Server). - Jwt.php: HS256-Access-Token (issue/verify), decode() IMMER mit explizitem Algorithmus (mitigiert Algorithmus-Confusion). 8/8 Tests grün. - SessionStore.php: predis-gestützter Session/Refresh-Store (sess:<sid>, Sliding-TTL, revoke=DEL) — die widerrufbare Session hinter dem kurzen JWT. - app.php: vendor/autoload (guarded), Konstanten AUTH_BACKEND (Default session), REDIS_URL, JWT_TTL, JWT_REFRESH_TTL. JWT_SECRET bewusst ohne Default. - docker-compose.dev.yml: Redis (+ redis-commander UI) für lokale Dev-Parität. - .env.example um Auth-Block ergänzt. Nächste Phasen: $_SESSION-Zugriffe (293) durch Session:: kanalisieren → Backend-Umschalter in Session.php → doppelgleisiger Cutover → Reporting auf student_id-FK ziehen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -14,3 +14,18 @@ DB_PORT=3306
|
|||||||
|
|
||||||
# ElevenLabs Sound Effects API — nur für generate-sounds.py (Dev-Zeit)
|
# ElevenLabs Sound Effects API — nur für generate-sounds.py (Dev-Zeit)
|
||||||
# ELEVENLABS_API_KEY=...
|
# ELEVENLABS_API_KEY=...
|
||||||
|
|
||||||
|
# ── Auth-Backend (JWT + Redis) ──────────────────────────────────────────
|
||||||
|
# 'session' = Legacy PHP-Datei-Sessions (Default, ändert nichts).
|
||||||
|
# 'jwt' = JWT-Access-Token (HttpOnly-Cookie) + Redis-Session/Refresh.
|
||||||
|
AUTH_BACKEND=session
|
||||||
|
|
||||||
|
# Signaturschlüssel für JWT (HS256). PFLICHT sobald AUTH_BACKEND=jwt.
|
||||||
|
# Erzeugen mit: openssl rand -hex 32
|
||||||
|
# Pro Umgebung UNTERSCHIEDLICH (dev ≠ prod). NIEMALS committen.
|
||||||
|
# JWT_SECRET=
|
||||||
|
|
||||||
|
# Redis (Session-/Refresh-Store). Lokal: docker-compose.dev.yml → 127.0.0.1:6379
|
||||||
|
REDIS_URL=tcp://127.0.0.1:6379
|
||||||
|
# JWT_TTL=900 # Access-Token-Lebensdauer in Sekunden (Default 15 min)
|
||||||
|
# JWT_REFRESH_TTL=2592000 # Session/Refresh in Sekunden (Default 30 Tage)
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "geograsim/app",
|
||||||
|
"description": "GeoGraSim — Auth (JWT + Redis) Abhängigkeiten",
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0",
|
||||||
|
"firebase/php-jwt": "^6.10",
|
||||||
|
"predis/predis": "^2.2"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"platform": { "php": "8.0.2" },
|
||||||
|
"audit": {
|
||||||
|
"ignore": {
|
||||||
|
"GHSA-2x45-7fc3-mxwq": "Algorithmus-Confusion-Klasse — mitigiert durch IMMER explizite alg-Angabe bei JWT::decode() (Key(secret,'HS256')). State-of-the-art-Nutzung."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+149
@@ -0,0 +1,149 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "2d5bd664b477984e9dd447a511f9360d",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "firebase/php-jwt",
|
||||||
|
"version": "v6.11.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/googleapis/php-jwt.git",
|
||||||
|
"reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/googleapis/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
|
||||||
|
"reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
|
"phpspec/prophecy-phpunit": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"psr/cache": "^2.0||^3.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
|
"psr/http-factory": "^1.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-sodium": "Support EdDSA (Ed25519) signatures",
|
||||||
|
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Firebase\\JWT\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Neuman Vong",
|
||||||
|
"email": "neuman+pear@twilio.com",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Anant Narayanan",
|
||||||
|
"email": "anant@php.net",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
|
||||||
|
"homepage": "https://github.com/firebase/php-jwt",
|
||||||
|
"keywords": [
|
||||||
|
"jwt",
|
||||||
|
"php"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/googleapis/php-jwt/issues",
|
||||||
|
"source": "https://github.com/googleapis/php-jwt/tree/v6.11.1"
|
||||||
|
},
|
||||||
|
"time": "2025-04-09T20:32:01+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "predis/predis",
|
||||||
|
"version": "v2.4.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/predis/predis.git",
|
||||||
|
"reference": "07105e050622ed80bd60808367ced9e379f31530"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/predis/predis/zipball/07105e050622ed80bd60808367ced9e379f31530",
|
||||||
|
"reference": "07105e050622ed80bd60808367ced9e379f31530",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.3",
|
||||||
|
"phpstan/phpstan": "^1.9",
|
||||||
|
"phpunit/phpcov": "^6.0 || ^8.0",
|
||||||
|
"phpunit/phpunit": "^8.0 || ^9.4"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Predis\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Till Krüss",
|
||||||
|
"homepage": "https://till.im",
|
||||||
|
"role": "Maintainer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A flexible and feature-complete Redis/Valkey client for PHP.",
|
||||||
|
"homepage": "http://github.com/predis/predis",
|
||||||
|
"keywords": [
|
||||||
|
"nosql",
|
||||||
|
"predis",
|
||||||
|
"redis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/predis/predis/issues",
|
||||||
|
"source": "https://github.com/predis/predis/tree/v2.4.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/sponsors/tillkruss",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-11-12T18:00:11+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": {},
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"php": ">=8.0"
|
||||||
|
},
|
||||||
|
"platform-dev": {},
|
||||||
|
"platform-overrides": {
|
||||||
|
"php": "8.0.2"
|
||||||
|
},
|
||||||
|
"plugin-api-version": "2.9.0"
|
||||||
|
}
|
||||||
@@ -38,6 +38,13 @@ if (strpos($host, 'geograsim.at') !== false) {
|
|||||||
}
|
}
|
||||||
define('APP_ROOT', dirname(__DIR__, 2));
|
define('APP_ROOT', dirname(__DIR__, 2));
|
||||||
|
|
||||||
|
// Composer-Autoloader (firebase/php-jwt, predis). Guard: erst ab dem
|
||||||
|
// JWT/Redis-Umbau vorhanden; fehlt er, läuft die App im Legacy-Session-Modus
|
||||||
|
// unverändert weiter.
|
||||||
|
if (is_file(APP_ROOT . '/vendor/autoload.php')) {
|
||||||
|
require_once APP_ROOT . '/vendor/autoload.php';
|
||||||
|
}
|
||||||
|
|
||||||
// Plattform-Limits — eine einzelne Sim-Session darf nie länger als 120 min
|
// Plattform-Limits — eine einzelne Sim-Session darf nie länger als 120 min
|
||||||
// dauern. Schützt DB vor absurden Werten (Tab über Nacht offen, Bug im
|
// dauern. Schützt DB vor absurden Werten (Tab über Nacht offen, Bug im
|
||||||
// Frontend-Zeit-Tracking, etc.). Backend-Cap ist defensive Garantie:
|
// Frontend-Zeit-Tracking, etc.). Backend-Cap ist defensive Garantie:
|
||||||
@@ -73,6 +80,17 @@ if (!defined('DB_USER')) define('DB_USER', 'root');
|
|||||||
if (!defined('DB_PASS')) define('DB_PASS', '');
|
if (!defined('DB_PASS')) define('DB_PASS', '');
|
||||||
if (!defined('DB_PORT')) define('DB_PORT', '3306');
|
if (!defined('DB_PORT')) define('DB_PORT', '3306');
|
||||||
|
|
||||||
|
// ── Auth-Backend (JWT + Redis Migration) ────────────────────────────────
|
||||||
|
// 'session' = Legacy (native PHP-Session-Dateien, wie bisher) — DEFAULT,
|
||||||
|
// damit sich am Live-System NICHTS ändert, bis der Cutover steht.
|
||||||
|
// 'jwt' = JWT-Access-Token (HttpOnly-Cookie) + Redis-Session/Refresh.
|
||||||
|
if (!defined('AUTH_BACKEND')) define('AUTH_BACKEND', 'session');
|
||||||
|
if (!defined('REDIS_URL')) define('REDIS_URL', 'tcp://127.0.0.1:6379');
|
||||||
|
if (!defined('JWT_TTL')) define('JWT_TTL', 900); // Access-Token: 15 min
|
||||||
|
if (!defined('JWT_REFRESH_TTL')) define('JWT_REFRESH_TTL', 2592000); // Refresh/Session: 30 Tage
|
||||||
|
// JWT_SECRET hat BEWUSST keinen Default — muss in .env gesetzt sein, sobald
|
||||||
|
// AUTH_BACKEND='jwt'. Jwt.php wirft sonst eine klare Exception.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cockpit-URL je nach Login-Status:
|
* Cockpit-URL je nach Login-Status:
|
||||||
* - Lehrkraft eingeloggt → /teacher
|
* - Lehrkraft eingeloggt → /teacher
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* GeoGraSim — JWT Access-Token (HS256)
|
||||||
|
* ------------------------------------
|
||||||
|
* Dünner Wrapper über firebase/php-jwt. Stellt kurzlebige Access-Tokens aus
|
||||||
|
* und prüft sie. WICHTIG: decode() bekommt IMMER den expliziten Algorithmus
|
||||||
|
* (Key(secret, 'HS256')) — das mitigiert die Algorithmus-Confusion-Klasse
|
||||||
|
* (alg:none / RS↔HS-Verwechslung).
|
||||||
|
*
|
||||||
|
* Reine Bibliothek — wird erst im Cutover von Session.php genutzt. Solange
|
||||||
|
* AUTH_BACKEND='session' gilt, ruft nichts diese Klasse.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Firebase\JWT\JWT as FirebaseJWT;
|
||||||
|
use Firebase\JWT\Key;
|
||||||
|
|
||||||
|
class Jwt
|
||||||
|
{
|
||||||
|
const ALG = 'HS256';
|
||||||
|
|
||||||
|
private static function secret(): string
|
||||||
|
{
|
||||||
|
if (!defined('JWT_SECRET') || JWT_SECRET === '') {
|
||||||
|
throw new RuntimeException('JWT_SECRET fehlt — in .env setzen, sobald AUTH_BACKEND=jwt.');
|
||||||
|
}
|
||||||
|
return JWT_SECRET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access-Token ausstellen.
|
||||||
|
* @param array $claims z. B. ['sub'=>42, 'typ'=>'teacher', 'sid'=>'<redis-sid>', 'roles'=>[]]
|
||||||
|
*/
|
||||||
|
public static function issue(array $claims, ?int $ttl = null): string
|
||||||
|
{
|
||||||
|
$now = time();
|
||||||
|
$payload = array_merge([
|
||||||
|
'iss' => defined('BASE_URL') ? BASE_URL : 'geograsim',
|
||||||
|
'iat' => $now,
|
||||||
|
'nbf' => $now,
|
||||||
|
'exp' => $now + ($ttl ?? (defined('JWT_TTL') ? JWT_TTL : 900)),
|
||||||
|
], $claims);
|
||||||
|
return FirebaseJWT::encode($payload, self::secret(), self::ALG);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access-Token prüfen. Gibt die Claims als Array zurück oder null
|
||||||
|
* (bei ungültiger Signatur, abgelaufen, noch nicht gültig, kaputt).
|
||||||
|
*/
|
||||||
|
public static function verify(string $token): ?array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$decoded = FirebaseJWT::decode($token, new Key(self::secret(), self::ALG));
|
||||||
|
return (array) $decoded;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return null; // ExpiredException, SignatureInvalidException, …
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* GeoGraSim — Redis-Session/Refresh-Store (predis)
|
||||||
|
* ------------------------------------------------
|
||||||
|
* Hält die WIDERRUFBARE Session hinter dem kurzlebigen JWT. Der Access-Token
|
||||||
|
* trägt nur die Session-ID (`sid`); die eigentliche Session lebt hier in Redis:
|
||||||
|
*
|
||||||
|
* sess:<sid> → JSON { typ, sub, class_id?, roles, created } (TTL, Sliding)
|
||||||
|
*
|
||||||
|
* Widerruf (Logout, Bann, „alle Geräte abmelden", Passwort-Wechsel) = DEL.
|
||||||
|
* Da der Access-Token kurz lebt (JWT_TTL), wirkt der Widerruf binnen Minuten.
|
||||||
|
*
|
||||||
|
* Reine Bibliothek — wird erst im Cutover genutzt. predis ist pure PHP,
|
||||||
|
* braucht KEINE Redis-Extension (läuft auf XAMPP wie im Prod-Image).
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Predis\Client as PredisClient;
|
||||||
|
|
||||||
|
class SessionStore
|
||||||
|
{
|
||||||
|
private static ?PredisClient $client = null;
|
||||||
|
|
||||||
|
public static function client(): PredisClient
|
||||||
|
{
|
||||||
|
if (self::$client === null) {
|
||||||
|
$url = defined('REDIS_URL') ? REDIS_URL : 'tcp://127.0.0.1:6379';
|
||||||
|
self::$client = new PredisClient($url);
|
||||||
|
}
|
||||||
|
return self::$client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function key(string $sid): string
|
||||||
|
{
|
||||||
|
return 'sess:' . $sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function ttl(): int
|
||||||
|
{
|
||||||
|
return defined('JWT_REFRESH_TTL') ? JWT_REFRESH_TTL : 2592000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Opake 256-bit Session-ID. */
|
||||||
|
public static function newSid(): string
|
||||||
|
{
|
||||||
|
return bin2hex(random_bytes(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Neue Session anlegen → sid. */
|
||||||
|
public static function create(array $data, ?int $ttl = null): string
|
||||||
|
{
|
||||||
|
$sid = self::newSid();
|
||||||
|
$data['created'] = time();
|
||||||
|
self::client()->setex(self::key($sid), $ttl ?? self::ttl(), json_encode($data));
|
||||||
|
return $sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Session lesen (null wenn widerrufen/abgelaufen). Sliding-Expiry per default. */
|
||||||
|
public static function get(string $sid, bool $touch = true): ?array
|
||||||
|
{
|
||||||
|
$raw = self::client()->get(self::key($sid));
|
||||||
|
if ($raw === null) return null;
|
||||||
|
if ($touch) self::client()->expire(self::key($sid), self::ttl());
|
||||||
|
$data = json_decode($raw, true);
|
||||||
|
return is_array($data) ? $data : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Widerruf (Logout / Bann). */
|
||||||
|
public static function revoke(string $sid): void
|
||||||
|
{
|
||||||
|
self::client()->del([self::key($sid)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Verbindungstest — für Health-Check / Setup-Verifikation. */
|
||||||
|
public static function ping(): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return (string) self::client()->ping() !== '';
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, $err);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException($err);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInit2d5bd664b477984e9dd447a511f9360d::getLoader();
|
||||||
Vendored
+579
@@ -0,0 +1,579 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see https://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see https://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
/** @var \Closure(string):void */
|
||||||
|
private static $includeFile;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $vendorDir;
|
||||||
|
|
||||||
|
// PSR-4
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, int>>
|
||||||
|
*/
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var array<string, list<string>>
|
||||||
|
*/
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
/**
|
||||||
|
* List of PSR-0 prefixes
|
||||||
|
*
|
||||||
|
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||||
|
*
|
||||||
|
* @var array<string, array<string, list<string>>>
|
||||||
|
*/
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $useIncludePath = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, bool>
|
||||||
|
*/
|
||||||
|
private $missingClasses = array();
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, self>
|
||||||
|
*/
|
||||||
|
private static $registeredLoaders = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $vendorDir
|
||||||
|
*/
|
||||||
|
public function __construct($vendorDir = null)
|
||||||
|
{
|
||||||
|
$this->vendorDir = $vendorDir;
|
||||||
|
self::initializeIncludeClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string> Array of classname => path
|
||||||
|
*/
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $classMap Class to filename map
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 base directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
|
||||||
|
if (null === $this->vendorDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prepend) {
|
||||||
|
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||||
|
} else {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
|
||||||
|
if (null !== $this->vendorDir) {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return true|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
$includeFile = self::$includeFile;
|
||||||
|
$includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||||
|
*
|
||||||
|
* @return array<string, self>
|
||||||
|
*/
|
||||||
|
public static function getRegisteredLoaders()
|
||||||
|
{
|
||||||
|
return self::$registeredLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $ext
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath . '\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
if (file_exists($file = $dir . $pathEnd)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function initializeIncludeClosure()
|
||||||
|
{
|
||||||
|
if (self::$includeFile !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
self::$includeFile = \Closure::bind(static function($file) {
|
||||||
|
include $file;
|
||||||
|
}, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
+396
@@ -0,0 +1,396 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
use Composer\Semver\VersionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is copied in every Composer installed project and available to all
|
||||||
|
*
|
||||||
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
|
*
|
||||||
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class InstalledVersions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static $selfDir = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed[]|null
|
||||||
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
|
*/
|
||||||
|
private static $installed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private static $installedIsLocalDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
private static $canGetVendors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackages()
|
||||||
|
{
|
||||||
|
$packages = array();
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
$packages[] = array_keys($installed['versions']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === \count($packages)) {
|
||||||
|
return $packages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names with a specific type e.g. 'library'
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackagesByType($type)
|
||||||
|
{
|
||||||
|
$packagesByType = array();
|
||||||
|
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
foreach ($installed['versions'] as $name => $package) {
|
||||||
|
if (isset($package['type']) && $package['type'] === $type) {
|
||||||
|
$packagesByType[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packagesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package is installed
|
||||||
|
*
|
||||||
|
* This also returns true if the package name is provided or replaced by another package
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @param bool $includeDevRequirements
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (isset($installed['versions'][$packageName])) {
|
||||||
|
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package satisfies a version constraint
|
||||||
|
*
|
||||||
|
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||||
|
*
|
||||||
|
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||||
|
*
|
||||||
|
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||||
|
* @param string $packageName
|
||||||
|
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||||
|
{
|
||||||
|
$constraint = $parser->parseConstraints((string) $constraint);
|
||||||
|
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||||
|
|
||||||
|
return $provided->matches($constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||||
|
*
|
||||||
|
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||||
|
* whether a given version of a package is installed, and not just whether it exists
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string Version constraint usable with composer/semver
|
||||||
|
*/
|
||||||
|
public static function getVersionRanges($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = array();
|
||||||
|
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' || ', $ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getPrettyVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||||
|
*/
|
||||||
|
public static function getReference($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||||
|
*/
|
||||||
|
public static function getInstallPath($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||||
|
*/
|
||||||
|
public static function getRootPackage()
|
||||||
|
{
|
||||||
|
$installed = self::getInstalled();
|
||||||
|
|
||||||
|
return $installed[0]['root'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw installed.php data for custom implementations
|
||||||
|
*
|
||||||
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||||
|
*/
|
||||||
|
public static function getRawData()
|
||||||
|
{
|
||||||
|
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
self::$installed = include __DIR__ . '/installed.php';
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
public static function getAllRawData()
|
||||||
|
{
|
||||||
|
return self::getInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you reload the static array from another file
|
||||||
|
*
|
||||||
|
* This is only useful for complex integrations in which a project needs to use
|
||||||
|
* this class but then also needs to execute another project's autoloader in process,
|
||||||
|
* and wants to ensure both projects have access to their version of installed.php.
|
||||||
|
*
|
||||||
|
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||||
|
* the data it needs from this class, then call reload() with
|
||||||
|
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||||
|
* the project in which it runs can then also use this class safely, without
|
||||||
|
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||||
|
*
|
||||||
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||||
|
*/
|
||||||
|
public static function reload($data)
|
||||||
|
{
|
||||||
|
self::$installed = $data;
|
||||||
|
self::$installedByVendor = array();
|
||||||
|
|
||||||
|
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||||
|
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||||
|
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||||
|
// all installed packages for example
|
||||||
|
self::$installedIsLocalDir = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getSelfDir()
|
||||||
|
{
|
||||||
|
if (self::$selfDir === null) {
|
||||||
|
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$selfDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static function getInstalled()
|
||||||
|
{
|
||||||
|
if (null === self::$canGetVendors) {
|
||||||
|
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed = array();
|
||||||
|
$copiedLocalDir = false;
|
||||||
|
|
||||||
|
if (self::$canGetVendors) {
|
||||||
|
$selfDir = self::getSelfDir();
|
||||||
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
|
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||||
|
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir];
|
||||||
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require $vendorDir.'/composer/installed.php';
|
||||||
|
self::$installedByVendor[$vendorDir] = $required;
|
||||||
|
$installed[] = $required;
|
||||||
|
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
self::$installed = $required;
|
||||||
|
self::$installedIsLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
$copiedLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require __DIR__ . '/installed.php';
|
||||||
|
self::$installed = $required;
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||||
|
$installed[] = self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
+568
@@ -0,0 +1,568 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||||
|
'Firebase\\JWT\\BeforeValidException' => $vendorDir . '/firebase/php-jwt/src/BeforeValidException.php',
|
||||||
|
'Firebase\\JWT\\CachedKeySet' => $vendorDir . '/firebase/php-jwt/src/CachedKeySet.php',
|
||||||
|
'Firebase\\JWT\\ExpiredException' => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php',
|
||||||
|
'Firebase\\JWT\\JWK' => $vendorDir . '/firebase/php-jwt/src/JWK.php',
|
||||||
|
'Firebase\\JWT\\JWT' => $vendorDir . '/firebase/php-jwt/src/JWT.php',
|
||||||
|
'Firebase\\JWT\\JWTExceptionWithPayloadInterface' => $vendorDir . '/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php',
|
||||||
|
'Firebase\\JWT\\Key' => $vendorDir . '/firebase/php-jwt/src/Key.php',
|
||||||
|
'Firebase\\JWT\\SignatureInvalidException' => $vendorDir . '/firebase/php-jwt/src/SignatureInvalidException.php',
|
||||||
|
'Predis\\Autoloader' => $vendorDir . '/predis/predis/src/Autoloader.php',
|
||||||
|
'Predis\\Client' => $vendorDir . '/predis/predis/src/Client.php',
|
||||||
|
'Predis\\ClientConfiguration' => $vendorDir . '/predis/predis/src/ClientConfiguration.php',
|
||||||
|
'Predis\\ClientContextInterface' => $vendorDir . '/predis/predis/src/ClientContextInterface.php',
|
||||||
|
'Predis\\ClientException' => $vendorDir . '/predis/predis/src/ClientException.php',
|
||||||
|
'Predis\\ClientInterface' => $vendorDir . '/predis/predis/src/ClientInterface.php',
|
||||||
|
'Predis\\Cluster\\ClusterStrategy' => $vendorDir . '/predis/predis/src/Cluster/ClusterStrategy.php',
|
||||||
|
'Predis\\Cluster\\Distributor\\DistributorInterface' => $vendorDir . '/predis/predis/src/Cluster/Distributor/DistributorInterface.php',
|
||||||
|
'Predis\\Cluster\\Distributor\\EmptyRingException' => $vendorDir . '/predis/predis/src/Cluster/Distributor/EmptyRingException.php',
|
||||||
|
'Predis\\Cluster\\Distributor\\HashRing' => $vendorDir . '/predis/predis/src/Cluster/Distributor/HashRing.php',
|
||||||
|
'Predis\\Cluster\\Distributor\\KetamaRing' => $vendorDir . '/predis/predis/src/Cluster/Distributor/KetamaRing.php',
|
||||||
|
'Predis\\Cluster\\Hash\\CRC16' => $vendorDir . '/predis/predis/src/Cluster/Hash/CRC16.php',
|
||||||
|
'Predis\\Cluster\\Hash\\HashGeneratorInterface' => $vendorDir . '/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php',
|
||||||
|
'Predis\\Cluster\\Hash\\PhpiredisCRC16' => $vendorDir . '/predis/predis/src/Cluster/Hash/PhpiredisCRC16.php',
|
||||||
|
'Predis\\Cluster\\NullSlotRange' => $vendorDir . '/predis/predis/src/Cluster/NullSlotRange.php',
|
||||||
|
'Predis\\Cluster\\PredisStrategy' => $vendorDir . '/predis/predis/src/Cluster/PredisStrategy.php',
|
||||||
|
'Predis\\Cluster\\RedisStrategy' => $vendorDir . '/predis/predis/src/Cluster/RedisStrategy.php',
|
||||||
|
'Predis\\Cluster\\SimpleSlotMap' => $vendorDir . '/predis/predis/src/Cluster/SimpleSlotMap.php',
|
||||||
|
'Predis\\Cluster\\SlotMap' => $vendorDir . '/predis/predis/src/Cluster/SlotMap.php',
|
||||||
|
'Predis\\Cluster\\SlotRange' => $vendorDir . '/predis/predis/src/Cluster/SlotRange.php',
|
||||||
|
'Predis\\Cluster\\StrategyInterface' => $vendorDir . '/predis/predis/src/Cluster/StrategyInterface.php',
|
||||||
|
'Predis\\Collection\\Iterator\\CursorBasedIterator' => $vendorDir . '/predis/predis/src/Collection/Iterator/CursorBasedIterator.php',
|
||||||
|
'Predis\\Collection\\Iterator\\HashKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/HashKey.php',
|
||||||
|
'Predis\\Collection\\Iterator\\Keyspace' => $vendorDir . '/predis/predis/src/Collection/Iterator/Keyspace.php',
|
||||||
|
'Predis\\Collection\\Iterator\\ListKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/ListKey.php',
|
||||||
|
'Predis\\Collection\\Iterator\\SetKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/SetKey.php',
|
||||||
|
'Predis\\Collection\\Iterator\\SortedSetKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/SortedSetKey.php',
|
||||||
|
'Predis\\Command\\Argument\\ArrayableArgument' => $vendorDir . '/predis/predis/src/Command/Argument/ArrayableArgument.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\AbstractBy' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/AbstractBy.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\ByBox' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/ByBox.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\ByInterface' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/ByInterface.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\ByRadius' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/ByRadius.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\FromInterface' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/FromInterface.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\FromLonLat' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/FromLonLat.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\FromMember' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/FromMember.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\AggregateArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/AggregateArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\AlterArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/AlterArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\CommonArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/CommonArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\CreateArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/CreateArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\CursorArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/CursorArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\DropArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/DropArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\ExplainArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/ExplainArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\ProfileArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/ProfileArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\AbstractField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/AbstractField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\FieldInterface' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/FieldInterface.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\GeoField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/GeoField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\GeoShapeField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/GeoShapeField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\NumericField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/NumericField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\TagField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/TagField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\TextField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/TextField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\VectorField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/VectorField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SearchArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SearchArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SpellcheckArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SpellcheckArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SugAddArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SugAddArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SugGetArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SugGetArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SynUpdateArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SynUpdateArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Server\\LimitInterface' => $vendorDir . '/predis/predis/src/Command/Argument/Server/LimitInterface.php',
|
||||||
|
'Predis\\Command\\Argument\\Server\\LimitOffsetCount' => $vendorDir . '/predis/predis/src/Command/Argument/Server/LimitOffsetCount.php',
|
||||||
|
'Predis\\Command\\Argument\\Server\\To' => $vendorDir . '/predis/predis/src/Command/Argument/Server/To.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\AddArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/AddArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\AlterArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/AlterArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\CommonArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/CommonArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\CreateArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/CreateArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\DecrByArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/DecrByArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\GetArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/GetArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\IncrByArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/IncrByArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\InfoArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/InfoArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\MGetArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/MGetArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\MRangeArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/MRangeArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\RangeArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/RangeArguments.php',
|
||||||
|
'Predis\\Command\\Command' => $vendorDir . '/predis/predis/src/Command/Command.php',
|
||||||
|
'Predis\\Command\\CommandInterface' => $vendorDir . '/predis/predis/src/Command/CommandInterface.php',
|
||||||
|
'Predis\\Command\\Factory' => $vendorDir . '/predis/predis/src/Command/Factory.php',
|
||||||
|
'Predis\\Command\\FactoryInterface' => $vendorDir . '/predis/predis/src/Command/FactoryInterface.php',
|
||||||
|
'Predis\\Command\\PrefixableCommandInterface' => $vendorDir . '/predis/predis/src/Command/PrefixableCommandInterface.php',
|
||||||
|
'Predis\\Command\\Processor\\KeyPrefixProcessor' => $vendorDir . '/predis/predis/src/Command/Processor/KeyPrefixProcessor.php',
|
||||||
|
'Predis\\Command\\Processor\\ProcessorChain' => $vendorDir . '/predis/predis/src/Command/Processor/ProcessorChain.php',
|
||||||
|
'Predis\\Command\\Processor\\ProcessorInterface' => $vendorDir . '/predis/predis/src/Command/Processor/ProcessorInterface.php',
|
||||||
|
'Predis\\Command\\RawCommand' => $vendorDir . '/predis/predis/src/Command/RawCommand.php',
|
||||||
|
'Predis\\Command\\RawFactory' => $vendorDir . '/predis/predis/src/Command/RawFactory.php',
|
||||||
|
'Predis\\Command\\RedisFactory' => $vendorDir . '/predis/predis/src/Command/RedisFactory.php',
|
||||||
|
'Predis\\Command\\Redis\\ACL' => $vendorDir . '/predis/predis/src/Command/Redis/ACL.php',
|
||||||
|
'Predis\\Command\\Redis\\APPEND' => $vendorDir . '/predis/predis/src/Command/Redis/APPEND.php',
|
||||||
|
'Predis\\Command\\Redis\\AUTH' => $vendorDir . '/predis/predis/src/Command/Redis/AUTH.php',
|
||||||
|
'Predis\\Command\\Redis\\AbstractCommand\\BZPOPBase' => $vendorDir . '/predis/predis/src/Command/Redis/AbstractCommand/BZPOPBase.php',
|
||||||
|
'Predis\\Command\\Redis\\BGREWRITEAOF' => $vendorDir . '/predis/predis/src/Command/Redis/BGREWRITEAOF.php',
|
||||||
|
'Predis\\Command\\Redis\\BGSAVE' => $vendorDir . '/predis/predis/src/Command/Redis/BGSAVE.php',
|
||||||
|
'Predis\\Command\\Redis\\BITCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/BITCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\BITFIELD' => $vendorDir . '/predis/predis/src/Command/Redis/BITFIELD.php',
|
||||||
|
'Predis\\Command\\Redis\\BITFIELD_RO' => $vendorDir . '/predis/predis/src/Command/Redis/BITFIELD_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\BITOP' => $vendorDir . '/predis/predis/src/Command/Redis/BITOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BITPOS' => $vendorDir . '/predis/predis/src/Command/Redis/BITPOS.php',
|
||||||
|
'Predis\\Command\\Redis\\BLMOVE' => $vendorDir . '/predis/predis/src/Command/Redis/BLMOVE.php',
|
||||||
|
'Predis\\Command\\Redis\\BLMPOP' => $vendorDir . '/predis/predis/src/Command/Redis/BLMPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BLPOP' => $vendorDir . '/predis/predis/src/Command/Redis/BLPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BRPOP' => $vendorDir . '/predis/predis/src/Command/Redis/BRPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BRPOPLPUSH' => $vendorDir . '/predis/predis/src/Command/Redis/BRPOPLPUSH.php',
|
||||||
|
'Predis\\Command\\Redis\\BZMPOP' => $vendorDir . '/predis/predis/src/Command/Redis/BZMPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BZPOPMAX' => $vendorDir . '/predis/predis/src/Command/Redis/BZPOPMAX.php',
|
||||||
|
'Predis\\Command\\Redis\\BZPOPMIN' => $vendorDir . '/predis/predis/src/Command/Redis/BZPOPMIN.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFADD' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFADD.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFINFO' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFINSERT' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFINSERT.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFLOADCHUNK' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFLOADCHUNK.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFMADD' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFMADD.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFMEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFMEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFRESERVE' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFRESERVE.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFSCANDUMP' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFSCANDUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\CLIENT' => $vendorDir . '/predis/predis/src/Command/Redis/CLIENT.php',
|
||||||
|
'Predis\\Command\\Redis\\CLUSTER' => $vendorDir . '/predis/predis/src/Command/Redis/CLUSTER.php',
|
||||||
|
'Predis\\Command\\Redis\\COMMAND' => $vendorDir . '/predis/predis/src/Command/Redis/COMMAND.php',
|
||||||
|
'Predis\\Command\\Redis\\CONFIG' => $vendorDir . '/predis/predis/src/Command/Redis/CONFIG.php',
|
||||||
|
'Predis\\Command\\Redis\\COPY' => $vendorDir . '/predis/predis/src/Command/Redis/COPY.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\ACL' => $vendorDir . '/predis/predis/src/Command/Redis/Container/ACL.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\AbstractContainer' => $vendorDir . '/predis/predis/src/Command/Redis/Container/AbstractContainer.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\CLUSTER' => $vendorDir . '/predis/predis/src/Command/Redis/Container/CLUSTER.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\ContainerFactory' => $vendorDir . '/predis/predis/src/Command/Redis/Container/ContainerFactory.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\ContainerInterface' => $vendorDir . '/predis/predis/src/Command/Redis/Container/ContainerInterface.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\FunctionContainer' => $vendorDir . '/predis/predis/src/Command/Redis/Container/FunctionContainer.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\Json\\JSONDEBUG' => $vendorDir . '/predis/predis/src/Command/Redis/Container/Json/JSONDEBUG.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\Search\\FTCONFIG' => $vendorDir . '/predis/predis/src/Command/Redis/Container/Search/FTCONFIG.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\Search\\FTCURSOR' => $vendorDir . '/predis/predis/src/Command/Redis/Container/Search/FTCURSOR.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSINFO' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSINITBYDIM' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYDIM.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSINITBYPROB' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYPROB.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSMERGE' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSMERGE.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSQUERY' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSQUERY.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFADD' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFADD.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFADDNX' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFADDNX.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFDEL' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFINFO' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFINSERT' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFINSERT.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFINSERTNX' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFINSERTNX.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFLOADCHUNK' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFLOADCHUNK.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFMEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFMEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFRESERVE' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFRESERVE.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFSCANDUMP' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFSCANDUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\DBSIZE' => $vendorDir . '/predis/predis/src/Command/Redis/DBSIZE.php',
|
||||||
|
'Predis\\Command\\Redis\\DECR' => $vendorDir . '/predis/predis/src/Command/Redis/DECR.php',
|
||||||
|
'Predis\\Command\\Redis\\DECRBY' => $vendorDir . '/predis/predis/src/Command/Redis/DECRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\DEL' => $vendorDir . '/predis/predis/src/Command/Redis/DEL.php',
|
||||||
|
'Predis\\Command\\Redis\\DISCARD' => $vendorDir . '/predis/predis/src/Command/Redis/DISCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\DUMP' => $vendorDir . '/predis/predis/src/Command/Redis/DUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\ECHO_' => $vendorDir . '/predis/predis/src/Command/Redis/ECHO_.php',
|
||||||
|
'Predis\\Command\\Redis\\EVALSHA' => $vendorDir . '/predis/predis/src/Command/Redis/EVALSHA.php',
|
||||||
|
'Predis\\Command\\Redis\\EVALSHA_RO' => $vendorDir . '/predis/predis/src/Command/Redis/EVALSHA_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\EVAL_' => $vendorDir . '/predis/predis/src/Command/Redis/EVAL_.php',
|
||||||
|
'Predis\\Command\\Redis\\EVAL_RO' => $vendorDir . '/predis/predis/src/Command/Redis/EVAL_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\EXEC' => $vendorDir . '/predis/predis/src/Command/Redis/EXEC.php',
|
||||||
|
'Predis\\Command\\Redis\\EXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/EXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\EXPIRE' => $vendorDir . '/predis/predis/src/Command/Redis/EXPIRE.php',
|
||||||
|
'Predis\\Command\\Redis\\EXPIREAT' => $vendorDir . '/predis/predis/src/Command/Redis/EXPIREAT.php',
|
||||||
|
'Predis\\Command\\Redis\\EXPIRETIME' => $vendorDir . '/predis/predis/src/Command/Redis/EXPIRETIME.php',
|
||||||
|
'Predis\\Command\\Redis\\FAILOVER' => $vendorDir . '/predis/predis/src/Command/Redis/FAILOVER.php',
|
||||||
|
'Predis\\Command\\Redis\\FCALL' => $vendorDir . '/predis/predis/src/Command/Redis/FCALL.php',
|
||||||
|
'Predis\\Command\\Redis\\FCALL_RO' => $vendorDir . '/predis/predis/src/Command/Redis/FCALL_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\FLUSHALL' => $vendorDir . '/predis/predis/src/Command/Redis/FLUSHALL.php',
|
||||||
|
'Predis\\Command\\Redis\\FLUSHDB' => $vendorDir . '/predis/predis/src/Command/Redis/FLUSHDB.php',
|
||||||
|
'Predis\\Command\\Redis\\FUNCTIONS' => $vendorDir . '/predis/predis/src/Command/Redis/FUNCTIONS.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOADD' => $vendorDir . '/predis/predis/src/Command/Redis/GEOADD.php',
|
||||||
|
'Predis\\Command\\Redis\\GEODIST' => $vendorDir . '/predis/predis/src/Command/Redis/GEODIST.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOHASH' => $vendorDir . '/predis/predis/src/Command/Redis/GEOHASH.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOPOS' => $vendorDir . '/predis/predis/src/Command/Redis/GEOPOS.php',
|
||||||
|
'Predis\\Command\\Redis\\GEORADIUS' => $vendorDir . '/predis/predis/src/Command/Redis/GEORADIUS.php',
|
||||||
|
'Predis\\Command\\Redis\\GEORADIUSBYMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/GEORADIUSBYMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOSEARCH' => $vendorDir . '/predis/predis/src/Command/Redis/GEOSEARCH.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOSEARCHSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/GEOSEARCHSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\GET' => $vendorDir . '/predis/predis/src/Command/Redis/GET.php',
|
||||||
|
'Predis\\Command\\Redis\\GETBIT' => $vendorDir . '/predis/predis/src/Command/Redis/GETBIT.php',
|
||||||
|
'Predis\\Command\\Redis\\GETDEL' => $vendorDir . '/predis/predis/src/Command/Redis/GETDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\GETEX' => $vendorDir . '/predis/predis/src/Command/Redis/GETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\GETRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/GETRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\GETSET' => $vendorDir . '/predis/predis/src/Command/Redis/GETSET.php',
|
||||||
|
'Predis\\Command\\Redis\\HDEL' => $vendorDir . '/predis/predis/src/Command/Redis/HDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\HEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/HEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\HEXPIRE' => $vendorDir . '/predis/predis/src/Command/Redis/HEXPIRE.php',
|
||||||
|
'Predis\\Command\\Redis\\HEXPIREAT' => $vendorDir . '/predis/predis/src/Command/Redis/HEXPIREAT.php',
|
||||||
|
'Predis\\Command\\Redis\\HEXPIRETIME' => $vendorDir . '/predis/predis/src/Command/Redis/HEXPIRETIME.php',
|
||||||
|
'Predis\\Command\\Redis\\HGET' => $vendorDir . '/predis/predis/src/Command/Redis/HGET.php',
|
||||||
|
'Predis\\Command\\Redis\\HGETALL' => $vendorDir . '/predis/predis/src/Command/Redis/HGETALL.php',
|
||||||
|
'Predis\\Command\\Redis\\HGETDEL' => $vendorDir . '/predis/predis/src/Command/Redis/HGETDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\HGETEX' => $vendorDir . '/predis/predis/src/Command/Redis/HGETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\HINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/HINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\HINCRBYFLOAT' => $vendorDir . '/predis/predis/src/Command/Redis/HINCRBYFLOAT.php',
|
||||||
|
'Predis\\Command\\Redis\\HKEYS' => $vendorDir . '/predis/predis/src/Command/Redis/HKEYS.php',
|
||||||
|
'Predis\\Command\\Redis\\HLEN' => $vendorDir . '/predis/predis/src/Command/Redis/HLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\HMGET' => $vendorDir . '/predis/predis/src/Command/Redis/HMGET.php',
|
||||||
|
'Predis\\Command\\Redis\\HMSET' => $vendorDir . '/predis/predis/src/Command/Redis/HMSET.php',
|
||||||
|
'Predis\\Command\\Redis\\HPERSIST' => $vendorDir . '/predis/predis/src/Command/Redis/HPERSIST.php',
|
||||||
|
'Predis\\Command\\Redis\\HPEXPIRE' => $vendorDir . '/predis/predis/src/Command/Redis/HPEXPIRE.php',
|
||||||
|
'Predis\\Command\\Redis\\HPEXPIREAT' => $vendorDir . '/predis/predis/src/Command/Redis/HPEXPIREAT.php',
|
||||||
|
'Predis\\Command\\Redis\\HPEXPIRETIME' => $vendorDir . '/predis/predis/src/Command/Redis/HPEXPIRETIME.php',
|
||||||
|
'Predis\\Command\\Redis\\HPTTL' => $vendorDir . '/predis/predis/src/Command/Redis/HPTTL.php',
|
||||||
|
'Predis\\Command\\Redis\\HRANDFIELD' => $vendorDir . '/predis/predis/src/Command/Redis/HRANDFIELD.php',
|
||||||
|
'Predis\\Command\\Redis\\HSCAN' => $vendorDir . '/predis/predis/src/Command/Redis/HSCAN.php',
|
||||||
|
'Predis\\Command\\Redis\\HSET' => $vendorDir . '/predis/predis/src/Command/Redis/HSET.php',
|
||||||
|
'Predis\\Command\\Redis\\HSETEX' => $vendorDir . '/predis/predis/src/Command/Redis/HSETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\HSETNX' => $vendorDir . '/predis/predis/src/Command/Redis/HSETNX.php',
|
||||||
|
'Predis\\Command\\Redis\\HSTRLEN' => $vendorDir . '/predis/predis/src/Command/Redis/HSTRLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\HTTL' => $vendorDir . '/predis/predis/src/Command/Redis/HTTL.php',
|
||||||
|
'Predis\\Command\\Redis\\HVALS' => $vendorDir . '/predis/predis/src/Command/Redis/HVALS.php',
|
||||||
|
'Predis\\Command\\Redis\\INCR' => $vendorDir . '/predis/predis/src/Command/Redis/INCR.php',
|
||||||
|
'Predis\\Command\\Redis\\INCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/INCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\INCRBYFLOAT' => $vendorDir . '/predis/predis/src/Command/Redis/INCRBYFLOAT.php',
|
||||||
|
'Predis\\Command\\Redis\\INFO' => $vendorDir . '/predis/predis/src/Command/Redis/INFO.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRAPPEND' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRAPPEND.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRINDEX' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRINDEX.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRINSERT' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRINSERT.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRLEN' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRPOP' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRTRIM' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRTRIM.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONCLEAR' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONCLEAR.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONDEBUG' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONDEBUG.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONDEL' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONFORGET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONFORGET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONGET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONGET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONMERGE' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONMERGE.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONMGET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONMGET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONMSET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONMSET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONNUMINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONNUMINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONOBJKEYS' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONOBJKEYS.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONOBJLEN' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONOBJLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONRESP' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONRESP.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONSET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONSET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONSTRAPPEND' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONSTRAPPEND.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONSTRLEN' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONSTRLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONTOGGLE' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONTOGGLE.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONTYPE' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONTYPE.php',
|
||||||
|
'Predis\\Command\\Redis\\KEYS' => $vendorDir . '/predis/predis/src/Command/Redis/KEYS.php',
|
||||||
|
'Predis\\Command\\Redis\\LASTSAVE' => $vendorDir . '/predis/predis/src/Command/Redis/LASTSAVE.php',
|
||||||
|
'Predis\\Command\\Redis\\LCS' => $vendorDir . '/predis/predis/src/Command/Redis/LCS.php',
|
||||||
|
'Predis\\Command\\Redis\\LINDEX' => $vendorDir . '/predis/predis/src/Command/Redis/LINDEX.php',
|
||||||
|
'Predis\\Command\\Redis\\LINSERT' => $vendorDir . '/predis/predis/src/Command/Redis/LINSERT.php',
|
||||||
|
'Predis\\Command\\Redis\\LLEN' => $vendorDir . '/predis/predis/src/Command/Redis/LLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\LMOVE' => $vendorDir . '/predis/predis/src/Command/Redis/LMOVE.php',
|
||||||
|
'Predis\\Command\\Redis\\LMPOP' => $vendorDir . '/predis/predis/src/Command/Redis/LMPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\LPOP' => $vendorDir . '/predis/predis/src/Command/Redis/LPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\LPUSH' => $vendorDir . '/predis/predis/src/Command/Redis/LPUSH.php',
|
||||||
|
'Predis\\Command\\Redis\\LPUSHX' => $vendorDir . '/predis/predis/src/Command/Redis/LPUSHX.php',
|
||||||
|
'Predis\\Command\\Redis\\LRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/LRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\LREM' => $vendorDir . '/predis/predis/src/Command/Redis/LREM.php',
|
||||||
|
'Predis\\Command\\Redis\\LSET' => $vendorDir . '/predis/predis/src/Command/Redis/LSET.php',
|
||||||
|
'Predis\\Command\\Redis\\LTRIM' => $vendorDir . '/predis/predis/src/Command/Redis/LTRIM.php',
|
||||||
|
'Predis\\Command\\Redis\\MGET' => $vendorDir . '/predis/predis/src/Command/Redis/MGET.php',
|
||||||
|
'Predis\\Command\\Redis\\MIGRATE' => $vendorDir . '/predis/predis/src/Command/Redis/MIGRATE.php',
|
||||||
|
'Predis\\Command\\Redis\\MONITOR' => $vendorDir . '/predis/predis/src/Command/Redis/MONITOR.php',
|
||||||
|
'Predis\\Command\\Redis\\MOVE' => $vendorDir . '/predis/predis/src/Command/Redis/MOVE.php',
|
||||||
|
'Predis\\Command\\Redis\\MSET' => $vendorDir . '/predis/predis/src/Command/Redis/MSET.php',
|
||||||
|
'Predis\\Command\\Redis\\MSETNX' => $vendorDir . '/predis/predis/src/Command/Redis/MSETNX.php',
|
||||||
|
'Predis\\Command\\Redis\\MULTI' => $vendorDir . '/predis/predis/src/Command/Redis/MULTI.php',
|
||||||
|
'Predis\\Command\\Redis\\OBJECT_' => $vendorDir . '/predis/predis/src/Command/Redis/OBJECT_.php',
|
||||||
|
'Predis\\Command\\Redis\\PERSIST' => $vendorDir . '/predis/predis/src/Command/Redis/PERSIST.php',
|
||||||
|
'Predis\\Command\\Redis\\PEXPIRE' => $vendorDir . '/predis/predis/src/Command/Redis/PEXPIRE.php',
|
||||||
|
'Predis\\Command\\Redis\\PEXPIREAT' => $vendorDir . '/predis/predis/src/Command/Redis/PEXPIREAT.php',
|
||||||
|
'Predis\\Command\\Redis\\PEXPIRETIME' => $vendorDir . '/predis/predis/src/Command/Redis/PEXPIRETIME.php',
|
||||||
|
'Predis\\Command\\Redis\\PFADD' => $vendorDir . '/predis/predis/src/Command/Redis/PFADD.php',
|
||||||
|
'Predis\\Command\\Redis\\PFCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/PFCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\PFMERGE' => $vendorDir . '/predis/predis/src/Command/Redis/PFMERGE.php',
|
||||||
|
'Predis\\Command\\Redis\\PING' => $vendorDir . '/predis/predis/src/Command/Redis/PING.php',
|
||||||
|
'Predis\\Command\\Redis\\PSETEX' => $vendorDir . '/predis/predis/src/Command/Redis/PSETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\PSUBSCRIBE' => $vendorDir . '/predis/predis/src/Command/Redis/PSUBSCRIBE.php',
|
||||||
|
'Predis\\Command\\Redis\\PTTL' => $vendorDir . '/predis/predis/src/Command/Redis/PTTL.php',
|
||||||
|
'Predis\\Command\\Redis\\PUBLISH' => $vendorDir . '/predis/predis/src/Command/Redis/PUBLISH.php',
|
||||||
|
'Predis\\Command\\Redis\\PUBSUB' => $vendorDir . '/predis/predis/src/Command/Redis/PUBSUB.php',
|
||||||
|
'Predis\\Command\\Redis\\PUNSUBSCRIBE' => $vendorDir . '/predis/predis/src/Command/Redis/PUNSUBSCRIBE.php',
|
||||||
|
'Predis\\Command\\Redis\\QUIT' => $vendorDir . '/predis/predis/src/Command/Redis/QUIT.php',
|
||||||
|
'Predis\\Command\\Redis\\RANDOMKEY' => $vendorDir . '/predis/predis/src/Command/Redis/RANDOMKEY.php',
|
||||||
|
'Predis\\Command\\Redis\\RENAME' => $vendorDir . '/predis/predis/src/Command/Redis/RENAME.php',
|
||||||
|
'Predis\\Command\\Redis\\RENAMENX' => $vendorDir . '/predis/predis/src/Command/Redis/RENAMENX.php',
|
||||||
|
'Predis\\Command\\Redis\\RESTORE' => $vendorDir . '/predis/predis/src/Command/Redis/RESTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\RPOP' => $vendorDir . '/predis/predis/src/Command/Redis/RPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\RPOPLPUSH' => $vendorDir . '/predis/predis/src/Command/Redis/RPOPLPUSH.php',
|
||||||
|
'Predis\\Command\\Redis\\RPUSH' => $vendorDir . '/predis/predis/src/Command/Redis/RPUSH.php',
|
||||||
|
'Predis\\Command\\Redis\\RPUSHX' => $vendorDir . '/predis/predis/src/Command/Redis/RPUSHX.php',
|
||||||
|
'Predis\\Command\\Redis\\SADD' => $vendorDir . '/predis/predis/src/Command/Redis/SADD.php',
|
||||||
|
'Predis\\Command\\Redis\\SAVE' => $vendorDir . '/predis/predis/src/Command/Redis/SAVE.php',
|
||||||
|
'Predis\\Command\\Redis\\SCAN' => $vendorDir . '/predis/predis/src/Command/Redis/SCAN.php',
|
||||||
|
'Predis\\Command\\Redis\\SCARD' => $vendorDir . '/predis/predis/src/Command/Redis/SCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\SCRIPT' => $vendorDir . '/predis/predis/src/Command/Redis/SCRIPT.php',
|
||||||
|
'Predis\\Command\\Redis\\SDIFF' => $vendorDir . '/predis/predis/src/Command/Redis/SDIFF.php',
|
||||||
|
'Predis\\Command\\Redis\\SDIFFSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/SDIFFSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\SELECT' => $vendorDir . '/predis/predis/src/Command/Redis/SELECT.php',
|
||||||
|
'Predis\\Command\\Redis\\SENTINEL' => $vendorDir . '/predis/predis/src/Command/Redis/SENTINEL.php',
|
||||||
|
'Predis\\Command\\Redis\\SET' => $vendorDir . '/predis/predis/src/Command/Redis/SET.php',
|
||||||
|
'Predis\\Command\\Redis\\SETBIT' => $vendorDir . '/predis/predis/src/Command/Redis/SETBIT.php',
|
||||||
|
'Predis\\Command\\Redis\\SETEX' => $vendorDir . '/predis/predis/src/Command/Redis/SETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\SETNX' => $vendorDir . '/predis/predis/src/Command/Redis/SETNX.php',
|
||||||
|
'Predis\\Command\\Redis\\SETRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/SETRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\SHUTDOWN' => $vendorDir . '/predis/predis/src/Command/Redis/SHUTDOWN.php',
|
||||||
|
'Predis\\Command\\Redis\\SINTER' => $vendorDir . '/predis/predis/src/Command/Redis/SINTER.php',
|
||||||
|
'Predis\\Command\\Redis\\SINTERCARD' => $vendorDir . '/predis/predis/src/Command/Redis/SINTERCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\SINTERSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/SINTERSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\SISMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/SISMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\SLAVEOF' => $vendorDir . '/predis/predis/src/Command/Redis/SLAVEOF.php',
|
||||||
|
'Predis\\Command\\Redis\\SLOWLOG' => $vendorDir . '/predis/predis/src/Command/Redis/SLOWLOG.php',
|
||||||
|
'Predis\\Command\\Redis\\SMEMBERS' => $vendorDir . '/predis/predis/src/Command/Redis/SMEMBERS.php',
|
||||||
|
'Predis\\Command\\Redis\\SMISMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/SMISMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\SMOVE' => $vendorDir . '/predis/predis/src/Command/Redis/SMOVE.php',
|
||||||
|
'Predis\\Command\\Redis\\SORT' => $vendorDir . '/predis/predis/src/Command/Redis/SORT.php',
|
||||||
|
'Predis\\Command\\Redis\\SORT_RO' => $vendorDir . '/predis/predis/src/Command/Redis/SORT_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\SPOP' => $vendorDir . '/predis/predis/src/Command/Redis/SPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\SRANDMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/SRANDMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\SREM' => $vendorDir . '/predis/predis/src/Command/Redis/SREM.php',
|
||||||
|
'Predis\\Command\\Redis\\SSCAN' => $vendorDir . '/predis/predis/src/Command/Redis/SSCAN.php',
|
||||||
|
'Predis\\Command\\Redis\\STRLEN' => $vendorDir . '/predis/predis/src/Command/Redis/STRLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\SUBSCRIBE' => $vendorDir . '/predis/predis/src/Command/Redis/SUBSCRIBE.php',
|
||||||
|
'Predis\\Command\\Redis\\SUBSTR' => $vendorDir . '/predis/predis/src/Command/Redis/SUBSTR.php',
|
||||||
|
'Predis\\Command\\Redis\\SUNION' => $vendorDir . '/predis/predis/src/Command/Redis/SUNION.php',
|
||||||
|
'Predis\\Command\\Redis\\SUNIONSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/SUNIONSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTAGGREGATE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTAGGREGATE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTALIASADD' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTALIASADD.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTALIASDEL' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTALIASDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTALIASUPDATE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTALIASUPDATE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTALTER' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTALTER.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTCONFIG' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTCONFIG.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTCREATE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTCREATE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTCURSOR' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTCURSOR.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTDICTADD' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTDICTADD.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTDICTDEL' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTDICTDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTDICTDUMP' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTDICTDUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTDROPINDEX' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTDROPINDEX.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTEXPLAIN' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTEXPLAIN.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTINFO' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTPROFILE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTPROFILE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSEARCH' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSEARCH.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSPELLCHECK' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSPELLCHECK.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSUGADD' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSUGADD.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSUGDEL' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSUGDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSUGGET' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSUGGET.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSUGLEN' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSUGLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSYNDUMP' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSYNDUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSYNUPDATE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSYNUPDATE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTTAGVALS' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTTAGVALS.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FT_LIST' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FT_LIST.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTADD' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTADD.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTBYRANK' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTBYRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTBYREVRANK' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTBYREVRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTCDF' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTCDF.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTCREATE' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTCREATE.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTINFO' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTMAX' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMAX.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTMERGE' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMERGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTMIN' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMIN.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTQUANTILE' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTQUANTILE.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTRANK' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTRESET' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTRESET.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTREVRANK' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTREVRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTTRIMMED_MEAN' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTTRIMMED_MEAN.php',
|
||||||
|
'Predis\\Command\\Redis\\TIME' => $vendorDir . '/predis/predis/src/Command/Redis/TIME.php',
|
||||||
|
'Predis\\Command\\Redis\\TOUCH' => $vendorDir . '/predis/predis/src/Command/Redis/TOUCH.php',
|
||||||
|
'Predis\\Command\\Redis\\TTL' => $vendorDir . '/predis/predis/src/Command/Redis/TTL.php',
|
||||||
|
'Predis\\Command\\Redis\\TYPE' => $vendorDir . '/predis/predis/src/Command/Redis/TYPE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSADD' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSADD.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSALTER' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSALTER.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSCREATE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSCREATE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSCREATERULE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSCREATERULE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSDECRBY' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSDECRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSDEL' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSDELETERULE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSDELETERULE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSGET' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSGET.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSINFO' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSMADD' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSMADD.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSMGET' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSMGET.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSMRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSMRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSMREVRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSMREVRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSQUERYINDEX' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSQUERYINDEX.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSREVRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSREVRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKADD' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKADD.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKINFO' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKLIST' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKLIST.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKQUERY' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKQUERY.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKRESERVE' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKRESERVE.php',
|
||||||
|
'Predis\\Command\\Redis\\UNSUBSCRIBE' => $vendorDir . '/predis/predis/src/Command/Redis/UNSUBSCRIBE.php',
|
||||||
|
'Predis\\Command\\Redis\\UNWATCH' => $vendorDir . '/predis/predis/src/Command/Redis/UNWATCH.php',
|
||||||
|
'Predis\\Command\\Redis\\WAITAOF' => $vendorDir . '/predis/predis/src/Command/Redis/WAITAOF.php',
|
||||||
|
'Predis\\Command\\Redis\\WATCH' => $vendorDir . '/predis/predis/src/Command/Redis/WATCH.php',
|
||||||
|
'Predis\\Command\\Redis\\XADD' => $vendorDir . '/predis/predis/src/Command/Redis/XADD.php',
|
||||||
|
'Predis\\Command\\Redis\\XDEL' => $vendorDir . '/predis/predis/src/Command/Redis/XDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\XLEN' => $vendorDir . '/predis/predis/src/Command/Redis/XLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\XRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/XRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\XREAD' => $vendorDir . '/predis/predis/src/Command/Redis/XREAD.php',
|
||||||
|
'Predis\\Command\\Redis\\XREVRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/XREVRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\XTRIM' => $vendorDir . '/predis/predis/src/Command/Redis/XTRIM.php',
|
||||||
|
'Predis\\Command\\Redis\\ZADD' => $vendorDir . '/predis/predis/src/Command/Redis/ZADD.php',
|
||||||
|
'Predis\\Command\\Redis\\ZCARD' => $vendorDir . '/predis/predis/src/Command/Redis/ZCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\ZCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/ZCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\ZDIFF' => $vendorDir . '/predis/predis/src/Command/Redis/ZDIFF.php',
|
||||||
|
'Predis\\Command\\Redis\\ZDIFFSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZDIFFSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/ZINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\ZINTER' => $vendorDir . '/predis/predis/src/Command/Redis/ZINTER.php',
|
||||||
|
'Predis\\Command\\Redis\\ZINTERCARD' => $vendorDir . '/predis/predis/src/Command/Redis/ZINTERCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\ZINTERSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZINTERSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZLEXCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/ZLEXCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\ZMPOP' => $vendorDir . '/predis/predis/src/Command/Redis/ZMPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\ZMSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZMSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZPOPMAX' => $vendorDir . '/predis/predis/src/Command/Redis/ZPOPMAX.php',
|
||||||
|
'Predis\\Command\\Redis\\ZPOPMIN' => $vendorDir . '/predis/predis/src/Command/Redis/ZPOPMIN.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANDMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANDMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANGEBYLEX' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANGEBYLEX.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANGEBYSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANGEBYSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANGESTORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANGESTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANK' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREM' => $vendorDir . '/predis/predis/src/Command/Redis/ZREM.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREMRANGEBYLEX' => $vendorDir . '/predis/predis/src/Command/Redis/ZREMRANGEBYLEX.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREMRANGEBYRANK' => $vendorDir . '/predis/predis/src/Command/Redis/ZREMRANGEBYRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREMRANGEBYSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZREMRANGEBYSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREVRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/ZREVRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREVRANGEBYLEX' => $vendorDir . '/predis/predis/src/Command/Redis/ZREVRANGEBYLEX.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREVRANGEBYSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZREVRANGEBYSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREVRANK' => $vendorDir . '/predis/predis/src/Command/Redis/ZREVRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\ZSCAN' => $vendorDir . '/predis/predis/src/Command/Redis/ZSCAN.php',
|
||||||
|
'Predis\\Command\\Redis\\ZSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZUNION' => $vendorDir . '/predis/predis/src/Command/Redis/ZUNION.php',
|
||||||
|
'Predis\\Command\\Redis\\ZUNIONSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZUNIONSTORE.php',
|
||||||
|
'Predis\\Command\\ScriptCommand' => $vendorDir . '/predis/predis/src/Command/ScriptCommand.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\DeleteStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DeleteStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\DumpStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DumpStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\FlushStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/FlushStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\KillStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/KillStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\ListStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/ListStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\LoadStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/LoadStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\RestoreStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/RestoreStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\StatsStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/StatsStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\StrategyResolverInterface' => $vendorDir . '/predis/predis/src/Command/Strategy/StrategyResolverInterface.php',
|
||||||
|
'Predis\\Command\\Strategy\\SubcommandStrategyInterface' => $vendorDir . '/predis/predis/src/Command/Strategy/SubcommandStrategyInterface.php',
|
||||||
|
'Predis\\Command\\Strategy\\SubcommandStrategyResolver' => $vendorDir . '/predis/predis/src/Command/Strategy/SubcommandStrategyResolver.php',
|
||||||
|
'Predis\\Command\\Traits\\Aggregate' => $vendorDir . '/predis/predis/src/Command/Traits/Aggregate.php',
|
||||||
|
'Predis\\Command\\Traits\\BitByte' => $vendorDir . '/predis/predis/src/Command/Traits/BitByte.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\BucketSize' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/BucketSize.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\Capacity' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/Capacity.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\Error' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/Error.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\Expansion' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/Expansion.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\Items' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/Items.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\MaxIterations' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/MaxIterations.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\NoCreate' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/NoCreate.php',
|
||||||
|
'Predis\\Command\\Traits\\By\\ByArgument' => $vendorDir . '/predis/predis/src/Command/Traits/By/ByArgument.php',
|
||||||
|
'Predis\\Command\\Traits\\By\\ByLexByScore' => $vendorDir . '/predis/predis/src/Command/Traits/By/ByLexByScore.php',
|
||||||
|
'Predis\\Command\\Traits\\By\\GeoBy' => $vendorDir . '/predis/predis/src/Command/Traits/By/GeoBy.php',
|
||||||
|
'Predis\\Command\\Traits\\Count' => $vendorDir . '/predis/predis/src/Command/Traits/Count.php',
|
||||||
|
'Predis\\Command\\Traits\\DB' => $vendorDir . '/predis/predis/src/Command/Traits/DB.php',
|
||||||
|
'Predis\\Command\\Traits\\Expire\\ExpireOptions' => $vendorDir . '/predis/predis/src/Command/Traits/Expire/ExpireOptions.php',
|
||||||
|
'Predis\\Command\\Traits\\From\\GeoFrom' => $vendorDir . '/predis/predis/src/Command/Traits/From/GeoFrom.php',
|
||||||
|
'Predis\\Command\\Traits\\Get\\Get' => $vendorDir . '/predis/predis/src/Command/Traits/Get/Get.php',
|
||||||
|
'Predis\\Command\\Traits\\Json\\Indent' => $vendorDir . '/predis/predis/src/Command/Traits/Json/Indent.php',
|
||||||
|
'Predis\\Command\\Traits\\Json\\Newline' => $vendorDir . '/predis/predis/src/Command/Traits/Json/Newline.php',
|
||||||
|
'Predis\\Command\\Traits\\Json\\NxXxArgument' => $vendorDir . '/predis/predis/src/Command/Traits/Json/NxXxArgument.php',
|
||||||
|
'Predis\\Command\\Traits\\Json\\Space' => $vendorDir . '/predis/predis/src/Command/Traits/Json/Space.php',
|
||||||
|
'Predis\\Command\\Traits\\Keys' => $vendorDir . '/predis/predis/src/Command/Traits/Keys.php',
|
||||||
|
'Predis\\Command\\Traits\\LeftRight' => $vendorDir . '/predis/predis/src/Command/Traits/LeftRight.php',
|
||||||
|
'Predis\\Command\\Traits\\Limit\\Limit' => $vendorDir . '/predis/predis/src/Command/Traits/Limit/Limit.php',
|
||||||
|
'Predis\\Command\\Traits\\Limit\\LimitObject' => $vendorDir . '/predis/predis/src/Command/Traits/Limit/LimitObject.php',
|
||||||
|
'Predis\\Command\\Traits\\MinMaxModifier' => $vendorDir . '/predis/predis/src/Command/Traits/MinMaxModifier.php',
|
||||||
|
'Predis\\Command\\Traits\\Replace' => $vendorDir . '/predis/predis/src/Command/Traits/Replace.php',
|
||||||
|
'Predis\\Command\\Traits\\Rev' => $vendorDir . '/predis/predis/src/Command/Traits/Rev.php',
|
||||||
|
'Predis\\Command\\Traits\\Sorting' => $vendorDir . '/predis/predis/src/Command/Traits/Sorting.php',
|
||||||
|
'Predis\\Command\\Traits\\Storedist' => $vendorDir . '/predis/predis/src/Command/Traits/Storedist.php',
|
||||||
|
'Predis\\Command\\Traits\\Timeout' => $vendorDir . '/predis/predis/src/Command/Traits/Timeout.php',
|
||||||
|
'Predis\\Command\\Traits\\To\\ServerTo' => $vendorDir . '/predis/predis/src/Command/Traits/To/ServerTo.php',
|
||||||
|
'Predis\\Command\\Traits\\Weights' => $vendorDir . '/predis/predis/src/Command/Traits/Weights.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithCoord' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithCoord.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithDist' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithDist.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithHash' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithHash.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithScores' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithScores.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithValues' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithValues.php',
|
||||||
|
'Predis\\CommunicationException' => $vendorDir . '/predis/predis/src/CommunicationException.php',
|
||||||
|
'Predis\\Configuration\\OptionInterface' => $vendorDir . '/predis/predis/src/Configuration/OptionInterface.php',
|
||||||
|
'Predis\\Configuration\\Option\\Aggregate' => $vendorDir . '/predis/predis/src/Configuration/Option/Aggregate.php',
|
||||||
|
'Predis\\Configuration\\Option\\CRC16' => $vendorDir . '/predis/predis/src/Configuration/Option/CRC16.php',
|
||||||
|
'Predis\\Configuration\\Option\\Cluster' => $vendorDir . '/predis/predis/src/Configuration/Option/Cluster.php',
|
||||||
|
'Predis\\Configuration\\Option\\Commands' => $vendorDir . '/predis/predis/src/Configuration/Option/Commands.php',
|
||||||
|
'Predis\\Configuration\\Option\\Connections' => $vendorDir . '/predis/predis/src/Configuration/Option/Connections.php',
|
||||||
|
'Predis\\Configuration\\Option\\Exceptions' => $vendorDir . '/predis/predis/src/Configuration/Option/Exceptions.php',
|
||||||
|
'Predis\\Configuration\\Option\\Prefix' => $vendorDir . '/predis/predis/src/Configuration/Option/Prefix.php',
|
||||||
|
'Predis\\Configuration\\Option\\Replication' => $vendorDir . '/predis/predis/src/Configuration/Option/Replication.php',
|
||||||
|
'Predis\\Configuration\\Options' => $vendorDir . '/predis/predis/src/Configuration/Options.php',
|
||||||
|
'Predis\\Configuration\\OptionsInterface' => $vendorDir . '/predis/predis/src/Configuration/OptionsInterface.php',
|
||||||
|
'Predis\\Connection\\AbstractConnection' => $vendorDir . '/predis/predis/src/Connection/AbstractConnection.php',
|
||||||
|
'Predis\\Connection\\AggregateConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/AggregateConnectionInterface.php',
|
||||||
|
'Predis\\Connection\\Cluster\\ClusterInterface' => $vendorDir . '/predis/predis/src/Connection/Cluster/ClusterInterface.php',
|
||||||
|
'Predis\\Connection\\Cluster\\PredisCluster' => $vendorDir . '/predis/predis/src/Connection/Cluster/PredisCluster.php',
|
||||||
|
'Predis\\Connection\\Cluster\\RedisCluster' => $vendorDir . '/predis/predis/src/Connection/Cluster/RedisCluster.php',
|
||||||
|
'Predis\\Connection\\CompositeConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/CompositeConnectionInterface.php',
|
||||||
|
'Predis\\Connection\\CompositeStreamConnection' => $vendorDir . '/predis/predis/src/Connection/CompositeStreamConnection.php',
|
||||||
|
'Predis\\Connection\\ConnectionException' => $vendorDir . '/predis/predis/src/Connection/ConnectionException.php',
|
||||||
|
'Predis\\Connection\\ConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/ConnectionInterface.php',
|
||||||
|
'Predis\\Connection\\Factory' => $vendorDir . '/predis/predis/src/Connection/Factory.php',
|
||||||
|
'Predis\\Connection\\FactoryInterface' => $vendorDir . '/predis/predis/src/Connection/FactoryInterface.php',
|
||||||
|
'Predis\\Connection\\NodeConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/NodeConnectionInterface.php',
|
||||||
|
'Predis\\Connection\\Parameters' => $vendorDir . '/predis/predis/src/Connection/Parameters.php',
|
||||||
|
'Predis\\Connection\\ParametersInterface' => $vendorDir . '/predis/predis/src/Connection/ParametersInterface.php',
|
||||||
|
'Predis\\Connection\\PhpiredisSocketConnection' => $vendorDir . '/predis/predis/src/Connection/PhpiredisSocketConnection.php',
|
||||||
|
'Predis\\Connection\\PhpiredisStreamConnection' => $vendorDir . '/predis/predis/src/Connection/PhpiredisStreamConnection.php',
|
||||||
|
'Predis\\Connection\\RelayConnection' => $vendorDir . '/predis/predis/src/Connection/RelayConnection.php',
|
||||||
|
'Predis\\Connection\\RelayMethods' => $vendorDir . '/predis/predis/src/Connection/RelayMethods.php',
|
||||||
|
'Predis\\Connection\\Replication\\MasterSlaveReplication' => $vendorDir . '/predis/predis/src/Connection/Replication/MasterSlaveReplication.php',
|
||||||
|
'Predis\\Connection\\Replication\\ReplicationInterface' => $vendorDir . '/predis/predis/src/Connection/Replication/ReplicationInterface.php',
|
||||||
|
'Predis\\Connection\\Replication\\SentinelReplication' => $vendorDir . '/predis/predis/src/Connection/Replication/SentinelReplication.php',
|
||||||
|
'Predis\\Connection\\StreamConnection' => $vendorDir . '/predis/predis/src/Connection/StreamConnection.php',
|
||||||
|
'Predis\\Connection\\WebdisConnection' => $vendorDir . '/predis/predis/src/Connection/WebdisConnection.php',
|
||||||
|
'Predis\\Monitor\\Consumer' => $vendorDir . '/predis/predis/src/Monitor/Consumer.php',
|
||||||
|
'Predis\\NotSupportedException' => $vendorDir . '/predis/predis/src/NotSupportedException.php',
|
||||||
|
'Predis\\Pipeline\\Atomic' => $vendorDir . '/predis/predis/src/Pipeline/Atomic.php',
|
||||||
|
'Predis\\Pipeline\\ConnectionErrorProof' => $vendorDir . '/predis/predis/src/Pipeline/ConnectionErrorProof.php',
|
||||||
|
'Predis\\Pipeline\\FireAndForget' => $vendorDir . '/predis/predis/src/Pipeline/FireAndForget.php',
|
||||||
|
'Predis\\Pipeline\\Pipeline' => $vendorDir . '/predis/predis/src/Pipeline/Pipeline.php',
|
||||||
|
'Predis\\Pipeline\\RelayAtomic' => $vendorDir . '/predis/predis/src/Pipeline/RelayAtomic.php',
|
||||||
|
'Predis\\Pipeline\\RelayPipeline' => $vendorDir . '/predis/predis/src/Pipeline/RelayPipeline.php',
|
||||||
|
'Predis\\PredisException' => $vendorDir . '/predis/predis/src/PredisException.php',
|
||||||
|
'Predis\\Protocol\\ProtocolException' => $vendorDir . '/predis/predis/src/Protocol/ProtocolException.php',
|
||||||
|
'Predis\\Protocol\\ProtocolProcessorInterface' => $vendorDir . '/predis/predis/src/Protocol/ProtocolProcessorInterface.php',
|
||||||
|
'Predis\\Protocol\\RequestSerializerInterface' => $vendorDir . '/predis/predis/src/Protocol/RequestSerializerInterface.php',
|
||||||
|
'Predis\\Protocol\\ResponseReaderInterface' => $vendorDir . '/predis/predis/src/Protocol/ResponseReaderInterface.php',
|
||||||
|
'Predis\\Protocol\\Text\\CompositeProtocolProcessor' => $vendorDir . '/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\BulkResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/BulkResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\ErrorResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\IntegerResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\MultiBulkResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\ResponseHandlerInterface' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\StatusResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/StatusResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\StreamableMultiBulkResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\ProtocolProcessor' => $vendorDir . '/predis/predis/src/Protocol/Text/ProtocolProcessor.php',
|
||||||
|
'Predis\\Protocol\\Text\\RequestSerializer' => $vendorDir . '/predis/predis/src/Protocol/Text/RequestSerializer.php',
|
||||||
|
'Predis\\Protocol\\Text\\ResponseReader' => $vendorDir . '/predis/predis/src/Protocol/Text/ResponseReader.php',
|
||||||
|
'Predis\\PubSub\\AbstractConsumer' => $vendorDir . '/predis/predis/src/PubSub/AbstractConsumer.php',
|
||||||
|
'Predis\\PubSub\\Consumer' => $vendorDir . '/predis/predis/src/PubSub/Consumer.php',
|
||||||
|
'Predis\\PubSub\\DispatcherLoop' => $vendorDir . '/predis/predis/src/PubSub/DispatcherLoop.php',
|
||||||
|
'Predis\\PubSub\\RelayConsumer' => $vendorDir . '/predis/predis/src/PubSub/RelayConsumer.php',
|
||||||
|
'Predis\\Replication\\MissingMasterException' => $vendorDir . '/predis/predis/src/Replication/MissingMasterException.php',
|
||||||
|
'Predis\\Replication\\ReplicationStrategy' => $vendorDir . '/predis/predis/src/Replication/ReplicationStrategy.php',
|
||||||
|
'Predis\\Replication\\RoleException' => $vendorDir . '/predis/predis/src/Replication/RoleException.php',
|
||||||
|
'Predis\\Response\\Error' => $vendorDir . '/predis/predis/src/Response/Error.php',
|
||||||
|
'Predis\\Response\\ErrorInterface' => $vendorDir . '/predis/predis/src/Response/ErrorInterface.php',
|
||||||
|
'Predis\\Response\\Iterator\\MultiBulk' => $vendorDir . '/predis/predis/src/Response/Iterator/MultiBulk.php',
|
||||||
|
'Predis\\Response\\Iterator\\MultiBulkIterator' => $vendorDir . '/predis/predis/src/Response/Iterator/MultiBulkIterator.php',
|
||||||
|
'Predis\\Response\\Iterator\\MultiBulkTuple' => $vendorDir . '/predis/predis/src/Response/Iterator/MultiBulkTuple.php',
|
||||||
|
'Predis\\Response\\ResponseInterface' => $vendorDir . '/predis/predis/src/Response/ResponseInterface.php',
|
||||||
|
'Predis\\Response\\ServerException' => $vendorDir . '/predis/predis/src/Response/ServerException.php',
|
||||||
|
'Predis\\Response\\Status' => $vendorDir . '/predis/predis/src/Response/Status.php',
|
||||||
|
'Predis\\Session\\Handler' => $vendorDir . '/predis/predis/src/Session/Handler.php',
|
||||||
|
'Predis\\Transaction\\AbortedMultiExecException' => $vendorDir . '/predis/predis/src/Transaction/AbortedMultiExecException.php',
|
||||||
|
'Predis\\Transaction\\MultiExec' => $vendorDir . '/predis/predis/src/Transaction/MultiExec.php',
|
||||||
|
'Predis\\Transaction\\MultiExecState' => $vendorDir . '/predis/predis/src/Transaction/MultiExecState.php',
|
||||||
|
);
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Predis\\' => array($vendorDir . '/predis/predis/src'),
|
||||||
|
'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
|
||||||
|
);
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInit2d5bd664b477984e9dd447a511f9360d
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Composer\Autoload\ClassLoader
|
||||||
|
*/
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInit2d5bd664b477984e9dd447a511f9360d', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInit2d5bd664b477984e9dd447a511f9360d', 'loadClassLoader'));
|
||||||
|
|
||||||
|
require __DIR__ . '/autoload_static.php';
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInit2d5bd664b477984e9dd447a511f9360d::getInitializer($loader));
|
||||||
|
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
||||||
+602
@@ -0,0 +1,602 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_static.php @generated by Composer
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
class ComposerStaticInit2d5bd664b477984e9dd447a511f9360d
|
||||||
|
{
|
||||||
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
'P' =>
|
||||||
|
array (
|
||||||
|
'Predis\\' => 7,
|
||||||
|
),
|
||||||
|
'F' =>
|
||||||
|
array (
|
||||||
|
'Firebase\\JWT\\' => 13,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixDirsPsr4 = array (
|
||||||
|
'Predis\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/predis/predis/src',
|
||||||
|
),
|
||||||
|
'Firebase\\JWT\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $classMap = array (
|
||||||
|
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||||
|
'Firebase\\JWT\\BeforeValidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/BeforeValidException.php',
|
||||||
|
'Firebase\\JWT\\CachedKeySet' => __DIR__ . '/..' . '/firebase/php-jwt/src/CachedKeySet.php',
|
||||||
|
'Firebase\\JWT\\ExpiredException' => __DIR__ . '/..' . '/firebase/php-jwt/src/ExpiredException.php',
|
||||||
|
'Firebase\\JWT\\JWK' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWK.php',
|
||||||
|
'Firebase\\JWT\\JWT' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWT.php',
|
||||||
|
'Firebase\\JWT\\JWTExceptionWithPayloadInterface' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php',
|
||||||
|
'Firebase\\JWT\\Key' => __DIR__ . '/..' . '/firebase/php-jwt/src/Key.php',
|
||||||
|
'Firebase\\JWT\\SignatureInvalidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/SignatureInvalidException.php',
|
||||||
|
'Predis\\Autoloader' => __DIR__ . '/..' . '/predis/predis/src/Autoloader.php',
|
||||||
|
'Predis\\Client' => __DIR__ . '/..' . '/predis/predis/src/Client.php',
|
||||||
|
'Predis\\ClientConfiguration' => __DIR__ . '/..' . '/predis/predis/src/ClientConfiguration.php',
|
||||||
|
'Predis\\ClientContextInterface' => __DIR__ . '/..' . '/predis/predis/src/ClientContextInterface.php',
|
||||||
|
'Predis\\ClientException' => __DIR__ . '/..' . '/predis/predis/src/ClientException.php',
|
||||||
|
'Predis\\ClientInterface' => __DIR__ . '/..' . '/predis/predis/src/ClientInterface.php',
|
||||||
|
'Predis\\Cluster\\ClusterStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/ClusterStrategy.php',
|
||||||
|
'Predis\\Cluster\\Distributor\\DistributorInterface' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/DistributorInterface.php',
|
||||||
|
'Predis\\Cluster\\Distributor\\EmptyRingException' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/EmptyRingException.php',
|
||||||
|
'Predis\\Cluster\\Distributor\\HashRing' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/HashRing.php',
|
||||||
|
'Predis\\Cluster\\Distributor\\KetamaRing' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/KetamaRing.php',
|
||||||
|
'Predis\\Cluster\\Hash\\CRC16' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Hash/CRC16.php',
|
||||||
|
'Predis\\Cluster\\Hash\\HashGeneratorInterface' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php',
|
||||||
|
'Predis\\Cluster\\Hash\\PhpiredisCRC16' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Hash/PhpiredisCRC16.php',
|
||||||
|
'Predis\\Cluster\\NullSlotRange' => __DIR__ . '/..' . '/predis/predis/src/Cluster/NullSlotRange.php',
|
||||||
|
'Predis\\Cluster\\PredisStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/PredisStrategy.php',
|
||||||
|
'Predis\\Cluster\\RedisStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/RedisStrategy.php',
|
||||||
|
'Predis\\Cluster\\SimpleSlotMap' => __DIR__ . '/..' . '/predis/predis/src/Cluster/SimpleSlotMap.php',
|
||||||
|
'Predis\\Cluster\\SlotMap' => __DIR__ . '/..' . '/predis/predis/src/Cluster/SlotMap.php',
|
||||||
|
'Predis\\Cluster\\SlotRange' => __DIR__ . '/..' . '/predis/predis/src/Cluster/SlotRange.php',
|
||||||
|
'Predis\\Cluster\\StrategyInterface' => __DIR__ . '/..' . '/predis/predis/src/Cluster/StrategyInterface.php',
|
||||||
|
'Predis\\Collection\\Iterator\\CursorBasedIterator' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/CursorBasedIterator.php',
|
||||||
|
'Predis\\Collection\\Iterator\\HashKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/HashKey.php',
|
||||||
|
'Predis\\Collection\\Iterator\\Keyspace' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/Keyspace.php',
|
||||||
|
'Predis\\Collection\\Iterator\\ListKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/ListKey.php',
|
||||||
|
'Predis\\Collection\\Iterator\\SetKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/SetKey.php',
|
||||||
|
'Predis\\Collection\\Iterator\\SortedSetKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/SortedSetKey.php',
|
||||||
|
'Predis\\Command\\Argument\\ArrayableArgument' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/ArrayableArgument.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\AbstractBy' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/AbstractBy.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\ByBox' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/ByBox.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\ByInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/ByInterface.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\ByRadius' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/ByRadius.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\FromInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/FromInterface.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\FromLonLat' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/FromLonLat.php',
|
||||||
|
'Predis\\Command\\Argument\\Geospatial\\FromMember' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/FromMember.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\AggregateArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/AggregateArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\AlterArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/AlterArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\CommonArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/CommonArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\CreateArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/CreateArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\CursorArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/CursorArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\DropArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/DropArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\ExplainArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/ExplainArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\ProfileArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/ProfileArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\AbstractField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/AbstractField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\FieldInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/FieldInterface.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\GeoField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/GeoField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\GeoShapeField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/GeoShapeField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\NumericField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/NumericField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\TagField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/TagField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\TextField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/TextField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SchemaFields\\VectorField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/VectorField.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SearchArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SearchArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SpellcheckArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SpellcheckArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SugAddArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SugAddArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SugGetArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SugGetArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Search\\SynUpdateArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SynUpdateArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\Server\\LimitInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Server/LimitInterface.php',
|
||||||
|
'Predis\\Command\\Argument\\Server\\LimitOffsetCount' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Server/LimitOffsetCount.php',
|
||||||
|
'Predis\\Command\\Argument\\Server\\To' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Server/To.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\AddArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/AddArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\AlterArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/AlterArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\CommonArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/CommonArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\CreateArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/CreateArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\DecrByArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/DecrByArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\GetArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/GetArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\IncrByArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/IncrByArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\InfoArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/InfoArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\MGetArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/MGetArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\MRangeArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/MRangeArguments.php',
|
||||||
|
'Predis\\Command\\Argument\\TimeSeries\\RangeArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/RangeArguments.php',
|
||||||
|
'Predis\\Command\\Command' => __DIR__ . '/..' . '/predis/predis/src/Command/Command.php',
|
||||||
|
'Predis\\Command\\CommandInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/CommandInterface.php',
|
||||||
|
'Predis\\Command\\Factory' => __DIR__ . '/..' . '/predis/predis/src/Command/Factory.php',
|
||||||
|
'Predis\\Command\\FactoryInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/FactoryInterface.php',
|
||||||
|
'Predis\\Command\\PrefixableCommandInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/PrefixableCommandInterface.php',
|
||||||
|
'Predis\\Command\\Processor\\KeyPrefixProcessor' => __DIR__ . '/..' . '/predis/predis/src/Command/Processor/KeyPrefixProcessor.php',
|
||||||
|
'Predis\\Command\\Processor\\ProcessorChain' => __DIR__ . '/..' . '/predis/predis/src/Command/Processor/ProcessorChain.php',
|
||||||
|
'Predis\\Command\\Processor\\ProcessorInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Processor/ProcessorInterface.php',
|
||||||
|
'Predis\\Command\\RawCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/RawCommand.php',
|
||||||
|
'Predis\\Command\\RawFactory' => __DIR__ . '/..' . '/predis/predis/src/Command/RawFactory.php',
|
||||||
|
'Predis\\Command\\RedisFactory' => __DIR__ . '/..' . '/predis/predis/src/Command/RedisFactory.php',
|
||||||
|
'Predis\\Command\\Redis\\ACL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ACL.php',
|
||||||
|
'Predis\\Command\\Redis\\APPEND' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/APPEND.php',
|
||||||
|
'Predis\\Command\\Redis\\AUTH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/AUTH.php',
|
||||||
|
'Predis\\Command\\Redis\\AbstractCommand\\BZPOPBase' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/AbstractCommand/BZPOPBase.php',
|
||||||
|
'Predis\\Command\\Redis\\BGREWRITEAOF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BGREWRITEAOF.php',
|
||||||
|
'Predis\\Command\\Redis\\BGSAVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BGSAVE.php',
|
||||||
|
'Predis\\Command\\Redis\\BITCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BITCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\BITFIELD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BITFIELD.php',
|
||||||
|
'Predis\\Command\\Redis\\BITFIELD_RO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BITFIELD_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\BITOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BITOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BITPOS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BITPOS.php',
|
||||||
|
'Predis\\Command\\Redis\\BLMOVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BLMOVE.php',
|
||||||
|
'Predis\\Command\\Redis\\BLMPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BLMPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BLPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BLPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BRPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BRPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BRPOPLPUSH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BRPOPLPUSH.php',
|
||||||
|
'Predis\\Command\\Redis\\BZMPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BZMPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\BZPOPMAX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BZPOPMAX.php',
|
||||||
|
'Predis\\Command\\Redis\\BZPOPMIN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BZPOPMIN.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFADD.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFINSERT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFINSERT.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFLOADCHUNK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFLOADCHUNK.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFMADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFMADD.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFMEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFMEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFRESERVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFRESERVE.php',
|
||||||
|
'Predis\\Command\\Redis\\BloomFilter\\BFSCANDUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFSCANDUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\CLIENT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CLIENT.php',
|
||||||
|
'Predis\\Command\\Redis\\CLUSTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CLUSTER.php',
|
||||||
|
'Predis\\Command\\Redis\\COMMAND' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/COMMAND.php',
|
||||||
|
'Predis\\Command\\Redis\\CONFIG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CONFIG.php',
|
||||||
|
'Predis\\Command\\Redis\\COPY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/COPY.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\ACL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/ACL.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\AbstractContainer' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/AbstractContainer.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\CLUSTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/CLUSTER.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\ContainerFactory' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/ContainerFactory.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\ContainerInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/ContainerInterface.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\FunctionContainer' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/FunctionContainer.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\Json\\JSONDEBUG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/Json/JSONDEBUG.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\Search\\FTCONFIG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/Search/FTCONFIG.php',
|
||||||
|
'Predis\\Command\\Redis\\Container\\Search\\FTCURSOR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/Search/FTCURSOR.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSINITBYDIM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYDIM.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSINITBYPROB' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYPROB.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSMERGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSMERGE.php',
|
||||||
|
'Predis\\Command\\Redis\\CountMinSketch\\CMSQUERY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSQUERY.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFADD.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFADDNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFADDNX.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFINSERT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFINSERT.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFINSERTNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFINSERTNX.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFLOADCHUNK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFLOADCHUNK.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFMEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFMEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFRESERVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFRESERVE.php',
|
||||||
|
'Predis\\Command\\Redis\\CuckooFilter\\CFSCANDUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFSCANDUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\DBSIZE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DBSIZE.php',
|
||||||
|
'Predis\\Command\\Redis\\DECR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DECR.php',
|
||||||
|
'Predis\\Command\\Redis\\DECRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DECRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\DEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DEL.php',
|
||||||
|
'Predis\\Command\\Redis\\DISCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DISCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\DUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\ECHO_' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ECHO_.php',
|
||||||
|
'Predis\\Command\\Redis\\EVALSHA' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EVALSHA.php',
|
||||||
|
'Predis\\Command\\Redis\\EVALSHA_RO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EVALSHA_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\EVAL_' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EVAL_.php',
|
||||||
|
'Predis\\Command\\Redis\\EVAL_RO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EVAL_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\EXEC' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXEC.php',
|
||||||
|
'Predis\\Command\\Redis\\EXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\EXPIRE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXPIRE.php',
|
||||||
|
'Predis\\Command\\Redis\\EXPIREAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXPIREAT.php',
|
||||||
|
'Predis\\Command\\Redis\\EXPIRETIME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXPIRETIME.php',
|
||||||
|
'Predis\\Command\\Redis\\FAILOVER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FAILOVER.php',
|
||||||
|
'Predis\\Command\\Redis\\FCALL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FCALL.php',
|
||||||
|
'Predis\\Command\\Redis\\FCALL_RO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FCALL_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\FLUSHALL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FLUSHALL.php',
|
||||||
|
'Predis\\Command\\Redis\\FLUSHDB' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FLUSHDB.php',
|
||||||
|
'Predis\\Command\\Redis\\FUNCTIONS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FUNCTIONS.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOADD.php',
|
||||||
|
'Predis\\Command\\Redis\\GEODIST' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEODIST.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOHASH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOHASH.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOPOS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOPOS.php',
|
||||||
|
'Predis\\Command\\Redis\\GEORADIUS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEORADIUS.php',
|
||||||
|
'Predis\\Command\\Redis\\GEORADIUSBYMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEORADIUSBYMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOSEARCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOSEARCH.php',
|
||||||
|
'Predis\\Command\\Redis\\GEOSEARCHSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOSEARCHSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\GET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GET.php',
|
||||||
|
'Predis\\Command\\Redis\\GETBIT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETBIT.php',
|
||||||
|
'Predis\\Command\\Redis\\GETDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\GETEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\GETRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\GETSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETSET.php',
|
||||||
|
'Predis\\Command\\Redis\\HDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\HEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HEXISTS.php',
|
||||||
|
'Predis\\Command\\Redis\\HEXPIRE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HEXPIRE.php',
|
||||||
|
'Predis\\Command\\Redis\\HEXPIREAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HEXPIREAT.php',
|
||||||
|
'Predis\\Command\\Redis\\HEXPIRETIME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HEXPIRETIME.php',
|
||||||
|
'Predis\\Command\\Redis\\HGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HGET.php',
|
||||||
|
'Predis\\Command\\Redis\\HGETALL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HGETALL.php',
|
||||||
|
'Predis\\Command\\Redis\\HGETDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HGETDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\HGETEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HGETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\HINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\HINCRBYFLOAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HINCRBYFLOAT.php',
|
||||||
|
'Predis\\Command\\Redis\\HKEYS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HKEYS.php',
|
||||||
|
'Predis\\Command\\Redis\\HLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\HMGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HMGET.php',
|
||||||
|
'Predis\\Command\\Redis\\HMSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HMSET.php',
|
||||||
|
'Predis\\Command\\Redis\\HPERSIST' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HPERSIST.php',
|
||||||
|
'Predis\\Command\\Redis\\HPEXPIRE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HPEXPIRE.php',
|
||||||
|
'Predis\\Command\\Redis\\HPEXPIREAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HPEXPIREAT.php',
|
||||||
|
'Predis\\Command\\Redis\\HPEXPIRETIME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HPEXPIRETIME.php',
|
||||||
|
'Predis\\Command\\Redis\\HPTTL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HPTTL.php',
|
||||||
|
'Predis\\Command\\Redis\\HRANDFIELD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HRANDFIELD.php',
|
||||||
|
'Predis\\Command\\Redis\\HSCAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HSCAN.php',
|
||||||
|
'Predis\\Command\\Redis\\HSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HSET.php',
|
||||||
|
'Predis\\Command\\Redis\\HSETEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HSETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\HSETNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HSETNX.php',
|
||||||
|
'Predis\\Command\\Redis\\HSTRLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HSTRLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\HTTL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HTTL.php',
|
||||||
|
'Predis\\Command\\Redis\\HVALS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HVALS.php',
|
||||||
|
'Predis\\Command\\Redis\\INCR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/INCR.php',
|
||||||
|
'Predis\\Command\\Redis\\INCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/INCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\INCRBYFLOAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/INCRBYFLOAT.php',
|
||||||
|
'Predis\\Command\\Redis\\INFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/INFO.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRAPPEND' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRAPPEND.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRINDEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRINDEX.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRINSERT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRINSERT.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONARRTRIM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRTRIM.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONCLEAR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONCLEAR.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONDEBUG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONDEBUG.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONFORGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONFORGET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONGET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONMERGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONMERGE.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONMGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONMGET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONMSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONMSET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONNUMINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONNUMINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONOBJKEYS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONOBJKEYS.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONOBJLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONOBJLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONRESP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONRESP.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONSET.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONSTRAPPEND' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONSTRAPPEND.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONSTRLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONSTRLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONTOGGLE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONTOGGLE.php',
|
||||||
|
'Predis\\Command\\Redis\\Json\\JSONTYPE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONTYPE.php',
|
||||||
|
'Predis\\Command\\Redis\\KEYS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/KEYS.php',
|
||||||
|
'Predis\\Command\\Redis\\LASTSAVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LASTSAVE.php',
|
||||||
|
'Predis\\Command\\Redis\\LCS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LCS.php',
|
||||||
|
'Predis\\Command\\Redis\\LINDEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LINDEX.php',
|
||||||
|
'Predis\\Command\\Redis\\LINSERT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LINSERT.php',
|
||||||
|
'Predis\\Command\\Redis\\LLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\LMOVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LMOVE.php',
|
||||||
|
'Predis\\Command\\Redis\\LMPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LMPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\LPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\LPUSH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LPUSH.php',
|
||||||
|
'Predis\\Command\\Redis\\LPUSHX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LPUSHX.php',
|
||||||
|
'Predis\\Command\\Redis\\LRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\LREM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LREM.php',
|
||||||
|
'Predis\\Command\\Redis\\LSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LSET.php',
|
||||||
|
'Predis\\Command\\Redis\\LTRIM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LTRIM.php',
|
||||||
|
'Predis\\Command\\Redis\\MGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MGET.php',
|
||||||
|
'Predis\\Command\\Redis\\MIGRATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MIGRATE.php',
|
||||||
|
'Predis\\Command\\Redis\\MONITOR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MONITOR.php',
|
||||||
|
'Predis\\Command\\Redis\\MOVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MOVE.php',
|
||||||
|
'Predis\\Command\\Redis\\MSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MSET.php',
|
||||||
|
'Predis\\Command\\Redis\\MSETNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MSETNX.php',
|
||||||
|
'Predis\\Command\\Redis\\MULTI' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MULTI.php',
|
||||||
|
'Predis\\Command\\Redis\\OBJECT_' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/OBJECT_.php',
|
||||||
|
'Predis\\Command\\Redis\\PERSIST' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PERSIST.php',
|
||||||
|
'Predis\\Command\\Redis\\PEXPIRE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PEXPIRE.php',
|
||||||
|
'Predis\\Command\\Redis\\PEXPIREAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PEXPIREAT.php',
|
||||||
|
'Predis\\Command\\Redis\\PEXPIRETIME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PEXPIRETIME.php',
|
||||||
|
'Predis\\Command\\Redis\\PFADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PFADD.php',
|
||||||
|
'Predis\\Command\\Redis\\PFCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PFCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\PFMERGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PFMERGE.php',
|
||||||
|
'Predis\\Command\\Redis\\PING' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PING.php',
|
||||||
|
'Predis\\Command\\Redis\\PSETEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PSETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\PSUBSCRIBE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PSUBSCRIBE.php',
|
||||||
|
'Predis\\Command\\Redis\\PTTL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PTTL.php',
|
||||||
|
'Predis\\Command\\Redis\\PUBLISH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PUBLISH.php',
|
||||||
|
'Predis\\Command\\Redis\\PUBSUB' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PUBSUB.php',
|
||||||
|
'Predis\\Command\\Redis\\PUNSUBSCRIBE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PUNSUBSCRIBE.php',
|
||||||
|
'Predis\\Command\\Redis\\QUIT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/QUIT.php',
|
||||||
|
'Predis\\Command\\Redis\\RANDOMKEY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RANDOMKEY.php',
|
||||||
|
'Predis\\Command\\Redis\\RENAME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RENAME.php',
|
||||||
|
'Predis\\Command\\Redis\\RENAMENX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RENAMENX.php',
|
||||||
|
'Predis\\Command\\Redis\\RESTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RESTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\RPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\RPOPLPUSH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RPOPLPUSH.php',
|
||||||
|
'Predis\\Command\\Redis\\RPUSH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RPUSH.php',
|
||||||
|
'Predis\\Command\\Redis\\RPUSHX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RPUSHX.php',
|
||||||
|
'Predis\\Command\\Redis\\SADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SADD.php',
|
||||||
|
'Predis\\Command\\Redis\\SAVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SAVE.php',
|
||||||
|
'Predis\\Command\\Redis\\SCAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SCAN.php',
|
||||||
|
'Predis\\Command\\Redis\\SCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\SCRIPT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SCRIPT.php',
|
||||||
|
'Predis\\Command\\Redis\\SDIFF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SDIFF.php',
|
||||||
|
'Predis\\Command\\Redis\\SDIFFSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SDIFFSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\SELECT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SELECT.php',
|
||||||
|
'Predis\\Command\\Redis\\SENTINEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SENTINEL.php',
|
||||||
|
'Predis\\Command\\Redis\\SET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SET.php',
|
||||||
|
'Predis\\Command\\Redis\\SETBIT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SETBIT.php',
|
||||||
|
'Predis\\Command\\Redis\\SETEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SETEX.php',
|
||||||
|
'Predis\\Command\\Redis\\SETNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SETNX.php',
|
||||||
|
'Predis\\Command\\Redis\\SETRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SETRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\SHUTDOWN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SHUTDOWN.php',
|
||||||
|
'Predis\\Command\\Redis\\SINTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SINTER.php',
|
||||||
|
'Predis\\Command\\Redis\\SINTERCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SINTERCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\SINTERSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SINTERSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\SISMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SISMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\SLAVEOF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SLAVEOF.php',
|
||||||
|
'Predis\\Command\\Redis\\SLOWLOG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SLOWLOG.php',
|
||||||
|
'Predis\\Command\\Redis\\SMEMBERS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SMEMBERS.php',
|
||||||
|
'Predis\\Command\\Redis\\SMISMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SMISMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\SMOVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SMOVE.php',
|
||||||
|
'Predis\\Command\\Redis\\SORT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SORT.php',
|
||||||
|
'Predis\\Command\\Redis\\SORT_RO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SORT_RO.php',
|
||||||
|
'Predis\\Command\\Redis\\SPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\SRANDMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SRANDMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\SREM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SREM.php',
|
||||||
|
'Predis\\Command\\Redis\\SSCAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SSCAN.php',
|
||||||
|
'Predis\\Command\\Redis\\STRLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/STRLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\SUBSCRIBE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SUBSCRIBE.php',
|
||||||
|
'Predis\\Command\\Redis\\SUBSTR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SUBSTR.php',
|
||||||
|
'Predis\\Command\\Redis\\SUNION' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SUNION.php',
|
||||||
|
'Predis\\Command\\Redis\\SUNIONSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SUNIONSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTAGGREGATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTAGGREGATE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTALIASADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTALIASADD.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTALIASDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTALIASDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTALIASUPDATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTALIASUPDATE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTALTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTALTER.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTCONFIG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTCONFIG.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTCREATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTCREATE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTCURSOR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTCURSOR.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTDICTADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTDICTADD.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTDICTDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTDICTDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTDICTDUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTDICTDUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTDROPINDEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTDROPINDEX.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTEXPLAIN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTEXPLAIN.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTPROFILE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTPROFILE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSEARCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSEARCH.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSPELLCHECK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSPELLCHECK.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSUGADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSUGADD.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSUGDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSUGDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSUGGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSUGGET.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSUGLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSUGLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSYNDUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSYNDUMP.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTSYNUPDATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSYNUPDATE.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FTTAGVALS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTTAGVALS.php',
|
||||||
|
'Predis\\Command\\Redis\\Search\\FT_LIST' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FT_LIST.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTADD.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTBYRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTBYRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTBYREVRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTBYREVRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTCDF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTCDF.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTCREATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTCREATE.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTMAX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMAX.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTMERGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMERGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTMIN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMIN.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTQUANTILE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTQUANTILE.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTRESET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTRESET.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTREVRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTREVRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\TDigest\\TDIGESTTRIMMED_MEAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTTRIMMED_MEAN.php',
|
||||||
|
'Predis\\Command\\Redis\\TIME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TIME.php',
|
||||||
|
'Predis\\Command\\Redis\\TOUCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TOUCH.php',
|
||||||
|
'Predis\\Command\\Redis\\TTL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TTL.php',
|
||||||
|
'Predis\\Command\\Redis\\TYPE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TYPE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSADD.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSALTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSALTER.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSCREATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSCREATE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSCREATERULE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSCREATERULE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSDECRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSDECRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSDELETERULE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSDELETERULE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSGET.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSMADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSMADD.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSMGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSMGET.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSMRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSMRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSMREVRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSMREVRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSQUERYINDEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSQUERYINDEX.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TimeSeries\\TSREVRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSREVRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKADD.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKINFO.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKLIST' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKLIST.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKQUERY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKQUERY.php',
|
||||||
|
'Predis\\Command\\Redis\\TopK\\TOPKRESERVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKRESERVE.php',
|
||||||
|
'Predis\\Command\\Redis\\UNSUBSCRIBE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/UNSUBSCRIBE.php',
|
||||||
|
'Predis\\Command\\Redis\\UNWATCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/UNWATCH.php',
|
||||||
|
'Predis\\Command\\Redis\\WAITAOF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/WAITAOF.php',
|
||||||
|
'Predis\\Command\\Redis\\WATCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/WATCH.php',
|
||||||
|
'Predis\\Command\\Redis\\XADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XADD.php',
|
||||||
|
'Predis\\Command\\Redis\\XDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XDEL.php',
|
||||||
|
'Predis\\Command\\Redis\\XLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XLEN.php',
|
||||||
|
'Predis\\Command\\Redis\\XRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\XREAD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XREAD.php',
|
||||||
|
'Predis\\Command\\Redis\\XREVRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XREVRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\XTRIM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XTRIM.php',
|
||||||
|
'Predis\\Command\\Redis\\ZADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZADD.php',
|
||||||
|
'Predis\\Command\\Redis\\ZCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\ZCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\ZDIFF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZDIFF.php',
|
||||||
|
'Predis\\Command\\Redis\\ZDIFFSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZDIFFSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZINCRBY.php',
|
||||||
|
'Predis\\Command\\Redis\\ZINTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZINTER.php',
|
||||||
|
'Predis\\Command\\Redis\\ZINTERCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZINTERCARD.php',
|
||||||
|
'Predis\\Command\\Redis\\ZINTERSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZINTERSTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZLEXCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZLEXCOUNT.php',
|
||||||
|
'Predis\\Command\\Redis\\ZMPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZMPOP.php',
|
||||||
|
'Predis\\Command\\Redis\\ZMSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZMSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZPOPMAX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZPOPMAX.php',
|
||||||
|
'Predis\\Command\\Redis\\ZPOPMIN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZPOPMIN.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANDMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANDMEMBER.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANGEBYLEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANGEBYLEX.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANGEBYSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANGEBYSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANGESTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANGESTORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREM.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREMRANGEBYLEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREMRANGEBYLEX.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREMRANGEBYRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREMRANGEBYRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREMRANGEBYSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREMRANGEBYSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREVRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREVRANGE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREVRANGEBYLEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREVRANGEBYLEX.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREVRANGEBYSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREVRANGEBYSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZREVRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREVRANK.php',
|
||||||
|
'Predis\\Command\\Redis\\ZSCAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZSCAN.php',
|
||||||
|
'Predis\\Command\\Redis\\ZSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZSCORE.php',
|
||||||
|
'Predis\\Command\\Redis\\ZUNION' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZUNION.php',
|
||||||
|
'Predis\\Command\\Redis\\ZUNIONSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZUNIONSTORE.php',
|
||||||
|
'Predis\\Command\\ScriptCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/ScriptCommand.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\DeleteStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DeleteStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\DumpStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DumpStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\FlushStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/FlushStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\KillStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/KillStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\ListStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/ListStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\LoadStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/LoadStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\RestoreStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/RestoreStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\StatsStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/StatsStrategy.php',
|
||||||
|
'Predis\\Command\\Strategy\\StrategyResolverInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/StrategyResolverInterface.php',
|
||||||
|
'Predis\\Command\\Strategy\\SubcommandStrategyInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/SubcommandStrategyInterface.php',
|
||||||
|
'Predis\\Command\\Strategy\\SubcommandStrategyResolver' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/SubcommandStrategyResolver.php',
|
||||||
|
'Predis\\Command\\Traits\\Aggregate' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Aggregate.php',
|
||||||
|
'Predis\\Command\\Traits\\BitByte' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BitByte.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\BucketSize' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/BucketSize.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\Capacity' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/Capacity.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\Error' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/Error.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\Expansion' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/Expansion.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\Items' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/Items.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\MaxIterations' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/MaxIterations.php',
|
||||||
|
'Predis\\Command\\Traits\\BloomFilters\\NoCreate' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/NoCreate.php',
|
||||||
|
'Predis\\Command\\Traits\\By\\ByArgument' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/By/ByArgument.php',
|
||||||
|
'Predis\\Command\\Traits\\By\\ByLexByScore' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/By/ByLexByScore.php',
|
||||||
|
'Predis\\Command\\Traits\\By\\GeoBy' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/By/GeoBy.php',
|
||||||
|
'Predis\\Command\\Traits\\Count' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Count.php',
|
||||||
|
'Predis\\Command\\Traits\\DB' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/DB.php',
|
||||||
|
'Predis\\Command\\Traits\\Expire\\ExpireOptions' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Expire/ExpireOptions.php',
|
||||||
|
'Predis\\Command\\Traits\\From\\GeoFrom' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/From/GeoFrom.php',
|
||||||
|
'Predis\\Command\\Traits\\Get\\Get' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Get/Get.php',
|
||||||
|
'Predis\\Command\\Traits\\Json\\Indent' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Json/Indent.php',
|
||||||
|
'Predis\\Command\\Traits\\Json\\Newline' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Json/Newline.php',
|
||||||
|
'Predis\\Command\\Traits\\Json\\NxXxArgument' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Json/NxXxArgument.php',
|
||||||
|
'Predis\\Command\\Traits\\Json\\Space' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Json/Space.php',
|
||||||
|
'Predis\\Command\\Traits\\Keys' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Keys.php',
|
||||||
|
'Predis\\Command\\Traits\\LeftRight' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/LeftRight.php',
|
||||||
|
'Predis\\Command\\Traits\\Limit\\Limit' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Limit/Limit.php',
|
||||||
|
'Predis\\Command\\Traits\\Limit\\LimitObject' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Limit/LimitObject.php',
|
||||||
|
'Predis\\Command\\Traits\\MinMaxModifier' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/MinMaxModifier.php',
|
||||||
|
'Predis\\Command\\Traits\\Replace' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Replace.php',
|
||||||
|
'Predis\\Command\\Traits\\Rev' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Rev.php',
|
||||||
|
'Predis\\Command\\Traits\\Sorting' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Sorting.php',
|
||||||
|
'Predis\\Command\\Traits\\Storedist' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Storedist.php',
|
||||||
|
'Predis\\Command\\Traits\\Timeout' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Timeout.php',
|
||||||
|
'Predis\\Command\\Traits\\To\\ServerTo' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/To/ServerTo.php',
|
||||||
|
'Predis\\Command\\Traits\\Weights' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Weights.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithCoord' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithCoord.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithDist' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithDist.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithHash' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithHash.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithScores' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithScores.php',
|
||||||
|
'Predis\\Command\\Traits\\With\\WithValues' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithValues.php',
|
||||||
|
'Predis\\CommunicationException' => __DIR__ . '/..' . '/predis/predis/src/CommunicationException.php',
|
||||||
|
'Predis\\Configuration\\OptionInterface' => __DIR__ . '/..' . '/predis/predis/src/Configuration/OptionInterface.php',
|
||||||
|
'Predis\\Configuration\\Option\\Aggregate' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Aggregate.php',
|
||||||
|
'Predis\\Configuration\\Option\\CRC16' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/CRC16.php',
|
||||||
|
'Predis\\Configuration\\Option\\Cluster' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Cluster.php',
|
||||||
|
'Predis\\Configuration\\Option\\Commands' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Commands.php',
|
||||||
|
'Predis\\Configuration\\Option\\Connections' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Connections.php',
|
||||||
|
'Predis\\Configuration\\Option\\Exceptions' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Exceptions.php',
|
||||||
|
'Predis\\Configuration\\Option\\Prefix' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Prefix.php',
|
||||||
|
'Predis\\Configuration\\Option\\Replication' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Replication.php',
|
||||||
|
'Predis\\Configuration\\Options' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Options.php',
|
||||||
|
'Predis\\Configuration\\OptionsInterface' => __DIR__ . '/..' . '/predis/predis/src/Configuration/OptionsInterface.php',
|
||||||
|
'Predis\\Connection\\AbstractConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/AbstractConnection.php',
|
||||||
|
'Predis\\Connection\\AggregateConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/AggregateConnectionInterface.php',
|
||||||
|
'Predis\\Connection\\Cluster\\ClusterInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/Cluster/ClusterInterface.php',
|
||||||
|
'Predis\\Connection\\Cluster\\PredisCluster' => __DIR__ . '/..' . '/predis/predis/src/Connection/Cluster/PredisCluster.php',
|
||||||
|
'Predis\\Connection\\Cluster\\RedisCluster' => __DIR__ . '/..' . '/predis/predis/src/Connection/Cluster/RedisCluster.php',
|
||||||
|
'Predis\\Connection\\CompositeConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/CompositeConnectionInterface.php',
|
||||||
|
'Predis\\Connection\\CompositeStreamConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/CompositeStreamConnection.php',
|
||||||
|
'Predis\\Connection\\ConnectionException' => __DIR__ . '/..' . '/predis/predis/src/Connection/ConnectionException.php',
|
||||||
|
'Predis\\Connection\\ConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/ConnectionInterface.php',
|
||||||
|
'Predis\\Connection\\Factory' => __DIR__ . '/..' . '/predis/predis/src/Connection/Factory.php',
|
||||||
|
'Predis\\Connection\\FactoryInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/FactoryInterface.php',
|
||||||
|
'Predis\\Connection\\NodeConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/NodeConnectionInterface.php',
|
||||||
|
'Predis\\Connection\\Parameters' => __DIR__ . '/..' . '/predis/predis/src/Connection/Parameters.php',
|
||||||
|
'Predis\\Connection\\ParametersInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/ParametersInterface.php',
|
||||||
|
'Predis\\Connection\\PhpiredisSocketConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/PhpiredisSocketConnection.php',
|
||||||
|
'Predis\\Connection\\PhpiredisStreamConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/PhpiredisStreamConnection.php',
|
||||||
|
'Predis\\Connection\\RelayConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/RelayConnection.php',
|
||||||
|
'Predis\\Connection\\RelayMethods' => __DIR__ . '/..' . '/predis/predis/src/Connection/RelayMethods.php',
|
||||||
|
'Predis\\Connection\\Replication\\MasterSlaveReplication' => __DIR__ . '/..' . '/predis/predis/src/Connection/Replication/MasterSlaveReplication.php',
|
||||||
|
'Predis\\Connection\\Replication\\ReplicationInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/Replication/ReplicationInterface.php',
|
||||||
|
'Predis\\Connection\\Replication\\SentinelReplication' => __DIR__ . '/..' . '/predis/predis/src/Connection/Replication/SentinelReplication.php',
|
||||||
|
'Predis\\Connection\\StreamConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/StreamConnection.php',
|
||||||
|
'Predis\\Connection\\WebdisConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/WebdisConnection.php',
|
||||||
|
'Predis\\Monitor\\Consumer' => __DIR__ . '/..' . '/predis/predis/src/Monitor/Consumer.php',
|
||||||
|
'Predis\\NotSupportedException' => __DIR__ . '/..' . '/predis/predis/src/NotSupportedException.php',
|
||||||
|
'Predis\\Pipeline\\Atomic' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/Atomic.php',
|
||||||
|
'Predis\\Pipeline\\ConnectionErrorProof' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/ConnectionErrorProof.php',
|
||||||
|
'Predis\\Pipeline\\FireAndForget' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/FireAndForget.php',
|
||||||
|
'Predis\\Pipeline\\Pipeline' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/Pipeline.php',
|
||||||
|
'Predis\\Pipeline\\RelayAtomic' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/RelayAtomic.php',
|
||||||
|
'Predis\\Pipeline\\RelayPipeline' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/RelayPipeline.php',
|
||||||
|
'Predis\\PredisException' => __DIR__ . '/..' . '/predis/predis/src/PredisException.php',
|
||||||
|
'Predis\\Protocol\\ProtocolException' => __DIR__ . '/..' . '/predis/predis/src/Protocol/ProtocolException.php',
|
||||||
|
'Predis\\Protocol\\ProtocolProcessorInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/ProtocolProcessorInterface.php',
|
||||||
|
'Predis\\Protocol\\RequestSerializerInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/RequestSerializerInterface.php',
|
||||||
|
'Predis\\Protocol\\ResponseReaderInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/ResponseReaderInterface.php',
|
||||||
|
'Predis\\Protocol\\Text\\CompositeProtocolProcessor' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\BulkResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/BulkResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\ErrorResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\IntegerResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\MultiBulkResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\ResponseHandlerInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\StatusResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/StatusResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\Handler\\StreamableMultiBulkResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php',
|
||||||
|
'Predis\\Protocol\\Text\\ProtocolProcessor' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/ProtocolProcessor.php',
|
||||||
|
'Predis\\Protocol\\Text\\RequestSerializer' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/RequestSerializer.php',
|
||||||
|
'Predis\\Protocol\\Text\\ResponseReader' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/ResponseReader.php',
|
||||||
|
'Predis\\PubSub\\AbstractConsumer' => __DIR__ . '/..' . '/predis/predis/src/PubSub/AbstractConsumer.php',
|
||||||
|
'Predis\\PubSub\\Consumer' => __DIR__ . '/..' . '/predis/predis/src/PubSub/Consumer.php',
|
||||||
|
'Predis\\PubSub\\DispatcherLoop' => __DIR__ . '/..' . '/predis/predis/src/PubSub/DispatcherLoop.php',
|
||||||
|
'Predis\\PubSub\\RelayConsumer' => __DIR__ . '/..' . '/predis/predis/src/PubSub/RelayConsumer.php',
|
||||||
|
'Predis\\Replication\\MissingMasterException' => __DIR__ . '/..' . '/predis/predis/src/Replication/MissingMasterException.php',
|
||||||
|
'Predis\\Replication\\ReplicationStrategy' => __DIR__ . '/..' . '/predis/predis/src/Replication/ReplicationStrategy.php',
|
||||||
|
'Predis\\Replication\\RoleException' => __DIR__ . '/..' . '/predis/predis/src/Replication/RoleException.php',
|
||||||
|
'Predis\\Response\\Error' => __DIR__ . '/..' . '/predis/predis/src/Response/Error.php',
|
||||||
|
'Predis\\Response\\ErrorInterface' => __DIR__ . '/..' . '/predis/predis/src/Response/ErrorInterface.php',
|
||||||
|
'Predis\\Response\\Iterator\\MultiBulk' => __DIR__ . '/..' . '/predis/predis/src/Response/Iterator/MultiBulk.php',
|
||||||
|
'Predis\\Response\\Iterator\\MultiBulkIterator' => __DIR__ . '/..' . '/predis/predis/src/Response/Iterator/MultiBulkIterator.php',
|
||||||
|
'Predis\\Response\\Iterator\\MultiBulkTuple' => __DIR__ . '/..' . '/predis/predis/src/Response/Iterator/MultiBulkTuple.php',
|
||||||
|
'Predis\\Response\\ResponseInterface' => __DIR__ . '/..' . '/predis/predis/src/Response/ResponseInterface.php',
|
||||||
|
'Predis\\Response\\ServerException' => __DIR__ . '/..' . '/predis/predis/src/Response/ServerException.php',
|
||||||
|
'Predis\\Response\\Status' => __DIR__ . '/..' . '/predis/predis/src/Response/Status.php',
|
||||||
|
'Predis\\Session\\Handler' => __DIR__ . '/..' . '/predis/predis/src/Session/Handler.php',
|
||||||
|
'Predis\\Transaction\\AbortedMultiExecException' => __DIR__ . '/..' . '/predis/predis/src/Transaction/AbortedMultiExecException.php',
|
||||||
|
'Predis\\Transaction\\MultiExec' => __DIR__ . '/..' . '/predis/predis/src/Transaction/MultiExec.php',
|
||||||
|
'Predis\\Transaction\\MultiExecState' => __DIR__ . '/..' . '/predis/predis/src/Transaction/MultiExecState.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixLengthsPsr4 = ComposerStaticInit2d5bd664b477984e9dd447a511f9360d::$prefixLengthsPsr4;
|
||||||
|
$loader->prefixDirsPsr4 = ComposerStaticInit2d5bd664b477984e9dd447a511f9360d::$prefixDirsPsr4;
|
||||||
|
$loader->classMap = ComposerStaticInit2d5bd664b477984e9dd447a511f9360d::$classMap;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+137
@@ -0,0 +1,137 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "firebase/php-jwt",
|
||||||
|
"version": "v6.11.1",
|
||||||
|
"version_normalized": "6.11.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/googleapis/php-jwt.git",
|
||||||
|
"reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/googleapis/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
|
||||||
|
"reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
|
"phpspec/prophecy-phpunit": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"psr/cache": "^2.0||^3.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
|
"psr/http-factory": "^1.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-sodium": "Support EdDSA (Ed25519) signatures",
|
||||||
|
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
||||||
|
},
|
||||||
|
"time": "2025-04-09T20:32:01+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Firebase\\JWT\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Neuman Vong",
|
||||||
|
"email": "neuman+pear@twilio.com",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Anant Narayanan",
|
||||||
|
"email": "anant@php.net",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
|
||||||
|
"homepage": "https://github.com/firebase/php-jwt",
|
||||||
|
"keywords": [
|
||||||
|
"jwt",
|
||||||
|
"php"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/googleapis/php-jwt/issues",
|
||||||
|
"source": "https://github.com/googleapis/php-jwt/tree/v6.11.1"
|
||||||
|
},
|
||||||
|
"install-path": "../firebase/php-jwt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "predis/predis",
|
||||||
|
"version": "v2.4.1",
|
||||||
|
"version_normalized": "2.4.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/predis/predis.git",
|
||||||
|
"reference": "07105e050622ed80bd60808367ced9e379f31530"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/predis/predis/zipball/07105e050622ed80bd60808367ced9e379f31530",
|
||||||
|
"reference": "07105e050622ed80bd60808367ced9e379f31530",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.3",
|
||||||
|
"phpstan/phpstan": "^1.9",
|
||||||
|
"phpunit/phpcov": "^6.0 || ^8.0",
|
||||||
|
"phpunit/phpunit": "^8.0 || ^9.4"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
|
||||||
|
},
|
||||||
|
"time": "2025-11-12T18:00:11+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Predis\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Till Krüss",
|
||||||
|
"homepage": "https://till.im",
|
||||||
|
"role": "Maintainer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A flexible and feature-complete Redis/Valkey client for PHP.",
|
||||||
|
"homepage": "http://github.com/predis/predis",
|
||||||
|
"keywords": [
|
||||||
|
"nosql",
|
||||||
|
"predis",
|
||||||
|
"redis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/predis/predis/issues",
|
||||||
|
"source": "https://github.com/predis/predis/tree/v2.4.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/sponsors/tillkruss",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"install-path": "../predis/predis"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"dev-package-names": []
|
||||||
|
}
|
||||||
Vendored
+41
@@ -0,0 +1,41 @@
|
|||||||
|
<?php return array(
|
||||||
|
'root' => array(
|
||||||
|
'name' => 'geograsim/app',
|
||||||
|
'pretty_version' => 'dev-main',
|
||||||
|
'version' => 'dev-main',
|
||||||
|
'reference' => 'db4b75c5e6a1eab4bb851e749e2bceb7b01a4def',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev' => true,
|
||||||
|
),
|
||||||
|
'versions' => array(
|
||||||
|
'firebase/php-jwt' => array(
|
||||||
|
'pretty_version' => 'v6.11.1',
|
||||||
|
'version' => '6.11.1.0',
|
||||||
|
'reference' => 'd1e91ecf8c598d073d0995afa8cd5c75c6e19e66',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../firebase/php-jwt',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'geograsim/app' => array(
|
||||||
|
'pretty_version' => 'dev-main',
|
||||||
|
'version' => 'dev-main',
|
||||||
|
'reference' => 'db4b75c5e6a1eab4bb851e749e2bceb7b01a4def',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'predis/predis' => array(
|
||||||
|
'pretty_version' => 'v2.4.1',
|
||||||
|
'version' => '2.4.1.0',
|
||||||
|
'reference' => '07105e050622ed80bd60808367ced9e379f31530',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../predis/predis',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// platform_check.php @generated by Composer
|
||||||
|
|
||||||
|
$issues = array();
|
||||||
|
|
||||||
|
if (!(PHP_VERSION_ID >= 80000)) {
|
||||||
|
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.0". You are running ' . PHP_VERSION . '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($issues) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new \RuntimeException(
|
||||||
|
'Composer detected issues in your platform: ' . implode(' ', $issues)
|
||||||
|
);
|
||||||
|
}
|
||||||
+205
@@ -0,0 +1,205 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [6.11.1](https://github.com/firebase/php-jwt/compare/v6.11.0...v6.11.1) (2025-04-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* update error text for consistency ([#528](https://github.com/firebase/php-jwt/issues/528)) ([c11113a](https://github.com/firebase/php-jwt/commit/c11113afa13265e016a669e75494b9203b8a7775))
|
||||||
|
|
||||||
|
## [6.11.0](https://github.com/firebase/php-jwt/compare/v6.10.2...v6.11.0) (2025-01-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* support octet typed JWK ([#587](https://github.com/firebase/php-jwt/issues/587)) ([7cb8a26](https://github.com/firebase/php-jwt/commit/7cb8a265fa81edf2fa6ef8098f5bc5ae573c33ad))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* refactor constructor Key to use PHP 8.0 syntax ([#577](https://github.com/firebase/php-jwt/issues/577)) ([29fa2ce](https://github.com/firebase/php-jwt/commit/29fa2ce9e0582cd397711eec1e80c05ce20fabca))
|
||||||
|
|
||||||
|
## [6.10.2](https://github.com/firebase/php-jwt/compare/v6.10.1...v6.10.2) (2024-11-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Mitigate PHP8.4 deprecation warnings ([#570](https://github.com/firebase/php-jwt/issues/570)) ([76808fa](https://github.com/firebase/php-jwt/commit/76808fa227f3811aa5cdb3bf81233714b799a5b5))
|
||||||
|
* support php 8.4 ([#583](https://github.com/firebase/php-jwt/issues/583)) ([e3d68b0](https://github.com/firebase/php-jwt/commit/e3d68b044421339443c74199edd020e03fb1887e))
|
||||||
|
|
||||||
|
## [6.10.1](https://github.com/firebase/php-jwt/compare/v6.10.0...v6.10.1) (2024-05-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ensure ratelimit expiry is set every time ([#556](https://github.com/firebase/php-jwt/issues/556)) ([09cb208](https://github.com/firebase/php-jwt/commit/09cb2081c2c3bc0f61e2f2a5fbea5741f7498648))
|
||||||
|
* ratelimit cache expiration ([#550](https://github.com/firebase/php-jwt/issues/550)) ([dda7250](https://github.com/firebase/php-jwt/commit/dda725033585ece30ff8cae8937320d7e9f18bae))
|
||||||
|
|
||||||
|
## [6.10.0](https://github.com/firebase/php-jwt/compare/v6.9.0...v6.10.0) (2023-11-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* allow typ header override ([#546](https://github.com/firebase/php-jwt/issues/546)) ([79cb30b](https://github.com/firebase/php-jwt/commit/79cb30b729a22931b2fbd6b53f20629a83031ba9))
|
||||||
|
|
||||||
|
## [6.9.0](https://github.com/firebase/php-jwt/compare/v6.8.1...v6.9.0) (2023-10-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add payload to jwt exception ([#521](https://github.com/firebase/php-jwt/issues/521)) ([175edf9](https://github.com/firebase/php-jwt/commit/175edf958bb61922ec135b2333acf5622f2238a2))
|
||||||
|
|
||||||
|
## [6.8.1](https://github.com/firebase/php-jwt/compare/v6.8.0...v6.8.1) (2023-07-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* accept float claims but round down to ignore them ([#492](https://github.com/firebase/php-jwt/issues/492)) ([3936842](https://github.com/firebase/php-jwt/commit/39368423beeaacb3002afa7dcb75baebf204fe7e))
|
||||||
|
* different BeforeValidException messages for nbf and iat ([#526](https://github.com/firebase/php-jwt/issues/526)) ([0a53cf2](https://github.com/firebase/php-jwt/commit/0a53cf2986e45c2bcbf1a269f313ebf56a154ee4))
|
||||||
|
|
||||||
|
## [6.8.0](https://github.com/firebase/php-jwt/compare/v6.7.0...v6.8.0) (2023-06-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add support for P-384 curve ([#515](https://github.com/firebase/php-jwt/issues/515)) ([5de4323](https://github.com/firebase/php-jwt/commit/5de4323f4baf4d70bca8663bd87682a69c656c3d))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* handle invalid http responses ([#508](https://github.com/firebase/php-jwt/issues/508)) ([91c39c7](https://github.com/firebase/php-jwt/commit/91c39c72b22fc3e1191e574089552c1f2041c718))
|
||||||
|
|
||||||
|
## [6.7.0](https://github.com/firebase/php-jwt/compare/v6.6.0...v6.7.0) (2023-06-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ed25519 support to JWK (public keys) ([#452](https://github.com/firebase/php-jwt/issues/452)) ([e53979a](https://github.com/firebase/php-jwt/commit/e53979abae927de916a75b9d239cfda8ce32be2a))
|
||||||
|
|
||||||
|
## [6.6.0](https://github.com/firebase/php-jwt/compare/v6.5.0...v6.6.0) (2023-06-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* allow get headers when decoding token ([#442](https://github.com/firebase/php-jwt/issues/442)) ([fb85f47](https://github.com/firebase/php-jwt/commit/fb85f47cfaeffdd94faf8defdf07164abcdad6c3))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* only check iat if nbf is not used ([#493](https://github.com/firebase/php-jwt/issues/493)) ([398ccd2](https://github.com/firebase/php-jwt/commit/398ccd25ea12fa84b9e4f1085d5ff448c21ec797))
|
||||||
|
|
||||||
|
## [6.5.0](https://github.com/firebase/php-jwt/compare/v6.4.0...v6.5.0) (2023-05-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow KID of '0' ([#505](https://github.com/firebase/php-jwt/issues/505)) ([9dc46a9](https://github.com/firebase/php-jwt/commit/9dc46a9c3e5801294249cfd2554c5363c9f9326a))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous Chores
|
||||||
|
|
||||||
|
* drop support for PHP 7.3 ([#495](https://github.com/firebase/php-jwt/issues/495))
|
||||||
|
|
||||||
|
## [6.4.0](https://github.com/firebase/php-jwt/compare/v6.3.2...v6.4.0) (2023-02-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add support for W3C ES256K ([#462](https://github.com/firebase/php-jwt/issues/462)) ([213924f](https://github.com/firebase/php-jwt/commit/213924f51936291fbbca99158b11bd4ae56c2c95))
|
||||||
|
* improve caching by only decoding jwks when necessary ([#486](https://github.com/firebase/php-jwt/issues/486)) ([78d3ed1](https://github.com/firebase/php-jwt/commit/78d3ed1073553f7d0bbffa6c2010009a0d483d5c))
|
||||||
|
|
||||||
|
## [6.3.2](https://github.com/firebase/php-jwt/compare/v6.3.1...v6.3.2) (2022-11-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check kid before using as array index ([bad1b04](https://github.com/firebase/php-jwt/commit/bad1b040d0c736bbf86814c6b5ae614f517cf7bd))
|
||||||
|
|
||||||
|
## [6.3.1](https://github.com/firebase/php-jwt/compare/v6.3.0...v6.3.1) (2022-11-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* casing of GET for PSR compat ([#451](https://github.com/firebase/php-jwt/issues/451)) ([60b52b7](https://github.com/firebase/php-jwt/commit/60b52b71978790eafcf3b95cfbd83db0439e8d22))
|
||||||
|
* string interpolation format for php 8.2 ([#446](https://github.com/firebase/php-jwt/issues/446)) ([2e07d8a](https://github.com/firebase/php-jwt/commit/2e07d8a1524d12b69b110ad649f17461d068b8f2))
|
||||||
|
|
||||||
|
## 6.3.0 / 2022-07-15
|
||||||
|
|
||||||
|
- Added ES256 support to JWK parsing ([#399](https://github.com/firebase/php-jwt/pull/399))
|
||||||
|
- Fixed potential caching error in `CachedKeySet` by caching jwks as strings ([#435](https://github.com/firebase/php-jwt/pull/435))
|
||||||
|
|
||||||
|
## 6.2.0 / 2022-05-14
|
||||||
|
|
||||||
|
- Added `CachedKeySet` ([#397](https://github.com/firebase/php-jwt/pull/397))
|
||||||
|
- Added `$defaultAlg` parameter to `JWT::parseKey` and `JWT::parseKeySet` ([#426](https://github.com/firebase/php-jwt/pull/426)).
|
||||||
|
|
||||||
|
## 6.1.0 / 2022-03-23
|
||||||
|
|
||||||
|
- Drop support for PHP 5.3, 5.4, 5.5, 5.6, and 7.0
|
||||||
|
- Add parameter typing and return types where possible
|
||||||
|
|
||||||
|
## 6.0.0 / 2022-01-24
|
||||||
|
|
||||||
|
- **Backwards-Compatibility Breaking Changes**: See the [Release Notes](https://github.com/firebase/php-jwt/releases/tag/v6.0.0) for more information.
|
||||||
|
- New Key object to prevent key/algorithm type confusion (#365)
|
||||||
|
- Add JWK support (#273)
|
||||||
|
- Add ES256 support (#256)
|
||||||
|
- Add ES384 support (#324)
|
||||||
|
- Add Ed25519 support (#343)
|
||||||
|
|
||||||
|
## 5.0.0 / 2017-06-26
|
||||||
|
- Support RS384 and RS512.
|
||||||
|
See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!
|
||||||
|
- Add an example for RS256 openssl.
|
||||||
|
See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)!
|
||||||
|
- Detect invalid Base64 encoding in signature.
|
||||||
|
See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)!
|
||||||
|
- Update `JWT::verify` to handle OpenSSL errors.
|
||||||
|
See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)!
|
||||||
|
- Add `array` type hinting to `decode` method
|
||||||
|
See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)!
|
||||||
|
- Add all JSON error types.
|
||||||
|
See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)!
|
||||||
|
- Bugfix 'kid' not in given key list.
|
||||||
|
See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)!
|
||||||
|
- Miscellaneous cleanup, documentation and test fixes.
|
||||||
|
See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115),
|
||||||
|
[#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and
|
||||||
|
[#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman),
|
||||||
|
[@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)!
|
||||||
|
|
||||||
|
## 4.0.0 / 2016-07-17
|
||||||
|
- Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)!
|
||||||
|
- Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)!
|
||||||
|
- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)!
|
||||||
|
- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)!
|
||||||
|
|
||||||
|
## 3.0.0 / 2015-07-22
|
||||||
|
- Minimum PHP version updated from `5.2.0` to `5.3.0`.
|
||||||
|
- Add `\Firebase\JWT` namespace. See
|
||||||
|
[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to
|
||||||
|
[@Dashron](https://github.com/Dashron)!
|
||||||
|
- Require a non-empty key to decode and verify a JWT. See
|
||||||
|
[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to
|
||||||
|
[@sjones608](https://github.com/sjones608)!
|
||||||
|
- Cleaner documentation blocks in the code. See
|
||||||
|
[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to
|
||||||
|
[@johanderuijter](https://github.com/johanderuijter)!
|
||||||
|
|
||||||
|
## 2.2.0 / 2015-06-22
|
||||||
|
- Add support for adding custom, optional JWT headers to `JWT::encode()`. See
|
||||||
|
[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to
|
||||||
|
[@mcocaro](https://github.com/mcocaro)!
|
||||||
|
|
||||||
|
## 2.1.0 / 2015-05-20
|
||||||
|
- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew
|
||||||
|
between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)!
|
||||||
|
- Add support for passing an object implementing the `ArrayAccess` interface for
|
||||||
|
`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)!
|
||||||
|
|
||||||
|
## 2.0.0 / 2015-04-01
|
||||||
|
- **Note**: It is strongly recommended that you update to > v2.0.0 to address
|
||||||
|
known security vulnerabilities in prior versions when both symmetric and
|
||||||
|
asymmetric keys are used together.
|
||||||
|
- Update signature for `JWT::decode(...)` to require an array of supported
|
||||||
|
algorithms to use when verifying token signatures.
|
||||||
Vendored
+30
@@ -0,0 +1,30 @@
|
|||||||
|
Copyright (c) 2011, Neuman Vong
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of other
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
+425
@@ -0,0 +1,425 @@
|
|||||||
|

|
||||||
|
[](https://packagist.org/packages/firebase/php-jwt)
|
||||||
|
[](https://packagist.org/packages/firebase/php-jwt)
|
||||||
|
[](https://packagist.org/packages/firebase/php-jwt)
|
||||||
|
|
||||||
|
PHP-JWT
|
||||||
|
=======
|
||||||
|
A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to [RFC 7519](https://tools.ietf.org/html/rfc7519).
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Use composer to manage your dependencies and download PHP-JWT:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require firebase/php-jwt
|
||||||
|
```
|
||||||
|
|
||||||
|
Optionally, install the `paragonie/sodium_compat` package from composer if your
|
||||||
|
php env does not have libsodium installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require paragonie/sodium_compat
|
||||||
|
```
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\Key;
|
||||||
|
|
||||||
|
$key = 'example_key';
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'http://example.org',
|
||||||
|
'aud' => 'http://example.com',
|
||||||
|
'iat' => 1356999524,
|
||||||
|
'nbf' => 1357000000
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMPORTANT:
|
||||||
|
* You must specify supported algorithms for your application. See
|
||||||
|
* https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
|
||||||
|
* for a list of spec-compliant algorithms.
|
||||||
|
*/
|
||||||
|
$jwt = JWT::encode($payload, $key, 'HS256');
|
||||||
|
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
|
||||||
|
print_r($decoded);
|
||||||
|
|
||||||
|
// Pass a stdClass in as the third parameter to get the decoded header values
|
||||||
|
$headers = new stdClass();
|
||||||
|
$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers);
|
||||||
|
print_r($headers);
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE: This will now be an object instead of an associative array. To get
|
||||||
|
an associative array, you will need to cast it as such:
|
||||||
|
*/
|
||||||
|
|
||||||
|
$decoded_array = (array) $decoded;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can add a leeway to account for when there is a clock skew times between
|
||||||
|
* the signing and verifying servers. It is recommended that this leeway should
|
||||||
|
* not be bigger than a few minutes.
|
||||||
|
*
|
||||||
|
* Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
|
||||||
|
*/
|
||||||
|
JWT::$leeway = 60; // $leeway in seconds
|
||||||
|
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
|
||||||
|
```
|
||||||
|
Example encode/decode headers
|
||||||
|
-------
|
||||||
|
Decoding the JWT headers without verifying the JWT first is NOT recommended, and is not supported by
|
||||||
|
this library. This is because without verifying the JWT, the header values could have been tampered with.
|
||||||
|
Any value pulled from an unverified header should be treated as if it could be any string sent in from an
|
||||||
|
attacker. If this is something you still want to do in your application for whatever reason, it's possible to
|
||||||
|
decode the header values manually simply by calling `json_decode` and `base64_decode` on the JWT
|
||||||
|
header part:
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
|
||||||
|
$key = 'example_key';
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'http://example.org',
|
||||||
|
'aud' => 'http://example.com',
|
||||||
|
'iat' => 1356999524,
|
||||||
|
'nbf' => 1357000000
|
||||||
|
];
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'x-forwarded-for' => 'www.google.com'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Encode headers in the JWT string
|
||||||
|
$jwt = JWT::encode($payload, $key, 'HS256', null, $headers);
|
||||||
|
|
||||||
|
// Decode headers from the JWT string WITHOUT validation
|
||||||
|
// **IMPORTANT**: This operation is vulnerable to attacks, as the JWT has not yet been verified.
|
||||||
|
// These headers could be any value sent by an attacker.
|
||||||
|
list($headersB64, $payloadB64, $sig) = explode('.', $jwt);
|
||||||
|
$decoded = json_decode(base64_decode($headersB64), true);
|
||||||
|
|
||||||
|
print_r($decoded);
|
||||||
|
```
|
||||||
|
Example with RS256 (openssl)
|
||||||
|
----------------------------
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\Key;
|
||||||
|
|
||||||
|
$privateKey = <<<EOD
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAuzWHNM5f+amCjQztc5QTfJfzCC5J4nuW+L/aOxZ4f8J3Frew
|
||||||
|
M2c/dufrnmedsApb0By7WhaHlcqCh/ScAPyJhzkPYLae7bTVro3hok0zDITR8F6S
|
||||||
|
JGL42JAEUk+ILkPI+DONM0+3vzk6Kvfe548tu4czCuqU8BGVOlnp6IqBHhAswNMM
|
||||||
|
78pos/2z0CjPM4tbeXqSTTbNkXRboxjU29vSopcT51koWOgiTf3C7nJUoMWZHZI5
|
||||||
|
HqnIhPAG9yv8HAgNk6CMk2CadVHDo4IxjxTzTTqo1SCSH2pooJl9O8at6kkRYsrZ
|
||||||
|
WwsKlOFE2LUce7ObnXsYihStBUDoeBQlGG/BwQIDAQABAoIBAFtGaOqNKGwggn9k
|
||||||
|
6yzr6GhZ6Wt2rh1Xpq8XUz514UBhPxD7dFRLpbzCrLVpzY80LbmVGJ9+1pJozyWc
|
||||||
|
VKeCeUdNwbqkr240Oe7GTFmGjDoxU+5/HX/SJYPpC8JZ9oqgEA87iz+WQX9hVoP2
|
||||||
|
oF6EB4ckDvXmk8FMwVZW2l2/kd5mrEVbDaXKxhvUDf52iVD+sGIlTif7mBgR99/b
|
||||||
|
c3qiCnxCMmfYUnT2eh7Vv2LhCR/G9S6C3R4lA71rEyiU3KgsGfg0d82/XWXbegJW
|
||||||
|
h3QbWNtQLxTuIvLq5aAryV3PfaHlPgdgK0ft6ocU2de2FagFka3nfVEyC7IUsNTK
|
||||||
|
bq6nhAECgYEA7d/0DPOIaItl/8BWKyCuAHMss47j0wlGbBSHdJIiS55akMvnAG0M
|
||||||
|
39y22Qqfzh1at9kBFeYeFIIU82ZLF3xOcE3z6pJZ4Dyvx4BYdXH77odo9uVK9s1l
|
||||||
|
3T3BlMcqd1hvZLMS7dviyH79jZo4CXSHiKzc7pQ2YfK5eKxKqONeXuECgYEAyXlG
|
||||||
|
vonaus/YTb1IBei9HwaccnQ/1HRn6MvfDjb7JJDIBhNClGPt6xRlzBbSZ73c2QEC
|
||||||
|
6Fu9h36K/HZ2qcLd2bXiNyhIV7b6tVKk+0Psoj0dL9EbhsD1OsmE1nTPyAc9XZbb
|
||||||
|
OPYxy+dpBCUA8/1U9+uiFoCa7mIbWcSQ+39gHuECgYAz82pQfct30aH4JiBrkNqP
|
||||||
|
nJfRq05UY70uk5k1u0ikLTRoVS/hJu/d4E1Kv4hBMqYCavFSwAwnvHUo51lVCr/y
|
||||||
|
xQOVYlsgnwBg2MX4+GjmIkqpSVCC8D7j/73MaWb746OIYZervQ8dbKahi2HbpsiG
|
||||||
|
8AHcVSA/agxZr38qvWV54QKBgCD5TlDE8x18AuTGQ9FjxAAd7uD0kbXNz2vUYg9L
|
||||||
|
hFL5tyL3aAAtUrUUw4xhd9IuysRhW/53dU+FsG2dXdJu6CxHjlyEpUJl2iZu/j15
|
||||||
|
YnMzGWHIEX8+eWRDsw/+Ujtko/B7TinGcWPz3cYl4EAOiCeDUyXnqnO1btCEUU44
|
||||||
|
DJ1BAoGBAJuPD27ErTSVtId90+M4zFPNibFP50KprVdc8CR37BE7r8vuGgNYXmnI
|
||||||
|
RLnGP9p3pVgFCktORuYS2J/6t84I3+A17nEoB4xvhTLeAinAW/uTQOUmNicOP4Ek
|
||||||
|
2MsLL2kHgL8bLTmvXV4FX+PXphrDKg1XxzOYn0otuoqdAQrkK4og
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
$publicKey = <<<EOD
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzWHNM5f+amCjQztc5QT
|
||||||
|
fJfzCC5J4nuW+L/aOxZ4f8J3FrewM2c/dufrnmedsApb0By7WhaHlcqCh/ScAPyJ
|
||||||
|
hzkPYLae7bTVro3hok0zDITR8F6SJGL42JAEUk+ILkPI+DONM0+3vzk6Kvfe548t
|
||||||
|
u4czCuqU8BGVOlnp6IqBHhAswNMM78pos/2z0CjPM4tbeXqSTTbNkXRboxjU29vS
|
||||||
|
opcT51koWOgiTf3C7nJUoMWZHZI5HqnIhPAG9yv8HAgNk6CMk2CadVHDo4IxjxTz
|
||||||
|
TTqo1SCSH2pooJl9O8at6kkRYsrZWwsKlOFE2LUce7ObnXsYihStBUDoeBQlGG/B
|
||||||
|
wQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'example.org',
|
||||||
|
'aud' => 'example.com',
|
||||||
|
'iat' => 1356999524,
|
||||||
|
'nbf' => 1357000000
|
||||||
|
];
|
||||||
|
|
||||||
|
$jwt = JWT::encode($payload, $privateKey, 'RS256');
|
||||||
|
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
||||||
|
|
||||||
|
$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE: This will now be an object instead of an associative array. To get
|
||||||
|
an associative array, you will need to cast it as such:
|
||||||
|
*/
|
||||||
|
|
||||||
|
$decoded_array = (array) $decoded;
|
||||||
|
echo "Decode:\n" . print_r($decoded_array, true) . "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
Example with a passphrase
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\Key;
|
||||||
|
|
||||||
|
// Your passphrase
|
||||||
|
$passphrase = '[YOUR_PASSPHRASE]';
|
||||||
|
|
||||||
|
// Your private key file with passphrase
|
||||||
|
// Can be generated with "ssh-keygen -t rsa -m pem"
|
||||||
|
$privateKeyFile = '/path/to/key-with-passphrase.pem';
|
||||||
|
|
||||||
|
// Create a private key of type "resource"
|
||||||
|
$privateKey = openssl_pkey_get_private(
|
||||||
|
file_get_contents($privateKeyFile),
|
||||||
|
$passphrase
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'example.org',
|
||||||
|
'aud' => 'example.com',
|
||||||
|
'iat' => 1356999524,
|
||||||
|
'nbf' => 1357000000
|
||||||
|
];
|
||||||
|
|
||||||
|
$jwt = JWT::encode($payload, $privateKey, 'RS256');
|
||||||
|
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
||||||
|
|
||||||
|
// Get public key from the private key, or pull from from a file.
|
||||||
|
$publicKey = openssl_pkey_get_details($privateKey)['key'];
|
||||||
|
|
||||||
|
$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
|
||||||
|
echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
Example with EdDSA (libsodium and Ed25519 signature)
|
||||||
|
----------------------------
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\Key;
|
||||||
|
|
||||||
|
// Public and private keys are expected to be Base64 encoded. The last
|
||||||
|
// non-empty line is used so that keys can be generated with
|
||||||
|
// sodium_crypto_sign_keypair(). The secret keys generated by other tools may
|
||||||
|
// need to be adjusted to match the input expected by libsodium.
|
||||||
|
|
||||||
|
$keyPair = sodium_crypto_sign_keypair();
|
||||||
|
|
||||||
|
$privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));
|
||||||
|
|
||||||
|
$publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'example.org',
|
||||||
|
'aud' => 'example.com',
|
||||||
|
'iat' => 1356999524,
|
||||||
|
'nbf' => 1357000000
|
||||||
|
];
|
||||||
|
|
||||||
|
$jwt = JWT::encode($payload, $privateKey, 'EdDSA');
|
||||||
|
echo "Encode:\n" . print_r($jwt, true) . "\n";
|
||||||
|
|
||||||
|
$decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA'));
|
||||||
|
echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
|
||||||
|
````
|
||||||
|
|
||||||
|
Example with multiple keys
|
||||||
|
--------------------------
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\Key;
|
||||||
|
|
||||||
|
// Example RSA keys from previous example
|
||||||
|
// $privateKey1 = '...';
|
||||||
|
// $publicKey1 = '...';
|
||||||
|
|
||||||
|
// Example EdDSA keys from previous example
|
||||||
|
// $privateKey2 = '...';
|
||||||
|
// $publicKey2 = '...';
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'iss' => 'example.org',
|
||||||
|
'aud' => 'example.com',
|
||||||
|
'iat' => 1356999524,
|
||||||
|
'nbf' => 1357000000
|
||||||
|
];
|
||||||
|
|
||||||
|
$jwt1 = JWT::encode($payload, $privateKey1, 'RS256', 'kid1');
|
||||||
|
$jwt2 = JWT::encode($payload, $privateKey2, 'EdDSA', 'kid2');
|
||||||
|
echo "Encode 1:\n" . print_r($jwt1, true) . "\n";
|
||||||
|
echo "Encode 2:\n" . print_r($jwt2, true) . "\n";
|
||||||
|
|
||||||
|
$keys = [
|
||||||
|
'kid1' => new Key($publicKey1, 'RS256'),
|
||||||
|
'kid2' => new Key($publicKey2, 'EdDSA'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$decoded1 = JWT::decode($jwt1, $keys);
|
||||||
|
$decoded2 = JWT::decode($jwt2, $keys);
|
||||||
|
|
||||||
|
echo "Decode 1:\n" . print_r((array) $decoded1, true) . "\n";
|
||||||
|
echo "Decode 2:\n" . print_r((array) $decoded2, true) . "\n";
|
||||||
|
```
|
||||||
|
|
||||||
|
Using JWKs
|
||||||
|
----------
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWK;
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
|
||||||
|
// Set of keys. The "keys" key is required. For example, the JSON response to
|
||||||
|
// this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
|
||||||
|
$jwks = ['keys' => []];
|
||||||
|
|
||||||
|
// JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key
|
||||||
|
// objects. Pass this as the second parameter to JWT::decode.
|
||||||
|
JWT::decode($jwt, JWK::parseKeySet($jwks));
|
||||||
|
```
|
||||||
|
|
||||||
|
Using Cached Key Sets
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The `CachedKeySet` class can be used to fetch and cache JWKS (JSON Web Key Sets) from a public URI.
|
||||||
|
This has the following advantages:
|
||||||
|
|
||||||
|
1. The results are cached for performance.
|
||||||
|
2. If an unrecognized key is requested, the cache is refreshed, to accomodate for key rotation.
|
||||||
|
3. If rate limiting is enabled, the JWKS URI will not make more than 10 requests a second.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\CachedKeySet;
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
|
||||||
|
// The URI for the JWKS you wish to cache the results from
|
||||||
|
$jwksUri = 'https://www.gstatic.com/iap/verify/public_key-jwk';
|
||||||
|
|
||||||
|
// Create an HTTP client (can be any PSR-7 compatible HTTP client)
|
||||||
|
$httpClient = new GuzzleHttp\Client();
|
||||||
|
|
||||||
|
// Create an HTTP request factory (can be any PSR-17 compatible HTTP request factory)
|
||||||
|
$httpFactory = new GuzzleHttp\Psr\HttpFactory();
|
||||||
|
|
||||||
|
// Create a cache item pool (can be any PSR-6 compatible cache item pool)
|
||||||
|
$cacheItemPool = Phpfastcache\CacheManager::getInstance('files');
|
||||||
|
|
||||||
|
$keySet = new CachedKeySet(
|
||||||
|
$jwksUri,
|
||||||
|
$httpClient,
|
||||||
|
$httpFactory,
|
||||||
|
$cacheItemPool,
|
||||||
|
null, // $expiresAfter int seconds to set the JWKS to expire
|
||||||
|
true // $rateLimit true to enable rate limit of 10 RPS on lookup of invalid keys
|
||||||
|
);
|
||||||
|
|
||||||
|
$jwt = 'eyJhbGci...'; // Some JWT signed by a key from the $jwkUri above
|
||||||
|
$decoded = JWT::decode($jwt, $keySet);
|
||||||
|
```
|
||||||
|
|
||||||
|
Miscellaneous
|
||||||
|
-------------
|
||||||
|
|
||||||
|
#### Exception Handling
|
||||||
|
|
||||||
|
When a call to `JWT::decode` is invalid, it will throw one of the following exceptions:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\SignatureInvalidException;
|
||||||
|
use Firebase\JWT\BeforeValidException;
|
||||||
|
use Firebase\JWT\ExpiredException;
|
||||||
|
use DomainException;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$decoded = JWT::decode($jwt, $keys);
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
// provided key/key-array is empty or malformed.
|
||||||
|
} catch (DomainException $e) {
|
||||||
|
// provided algorithm is unsupported OR
|
||||||
|
// provided key is invalid OR
|
||||||
|
// unknown error thrown in openSSL or libsodium OR
|
||||||
|
// libsodium is required but not available.
|
||||||
|
} catch (SignatureInvalidException $e) {
|
||||||
|
// provided JWT signature verification failed.
|
||||||
|
} catch (BeforeValidException $e) {
|
||||||
|
// provided JWT is trying to be used before "nbf" claim OR
|
||||||
|
// provided JWT is trying to be used before "iat" claim.
|
||||||
|
} catch (ExpiredException $e) {
|
||||||
|
// provided JWT is trying to be used after "exp" claim.
|
||||||
|
} catch (UnexpectedValueException $e) {
|
||||||
|
// provided JWT is malformed OR
|
||||||
|
// provided JWT is missing an algorithm / using an unsupported algorithm OR
|
||||||
|
// provided JWT algorithm does not match provided key OR
|
||||||
|
// provided key ID in key/key-array is empty or invalid.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All exceptions in the `Firebase\JWT` namespace extend `UnexpectedValueException`, and can be simplified
|
||||||
|
like this:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
try {
|
||||||
|
$decoded = JWT::decode($jwt, $keys);
|
||||||
|
} catch (LogicException $e) {
|
||||||
|
// errors having to do with environmental setup or malformed JWT Keys
|
||||||
|
} catch (UnexpectedValueException $e) {
|
||||||
|
// errors having to do with JWT signature and claims
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Casting to array
|
||||||
|
|
||||||
|
The return value of `JWT::decode` is the generic PHP object `stdClass`. If you'd like to handle with arrays
|
||||||
|
instead, you can do the following:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// return type is stdClass
|
||||||
|
$decoded = JWT::decode($jwt, $keys);
|
||||||
|
|
||||||
|
// cast to array
|
||||||
|
$decoded = json_decode(json_encode($decoded), true);
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests
|
||||||
|
-----
|
||||||
|
Run the tests using phpunit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ pear install PHPUnit
|
||||||
|
$ phpunit --configuration phpunit.xml.dist
|
||||||
|
PHPUnit 3.7.10 by Sebastian Bergmann.
|
||||||
|
.....
|
||||||
|
Time: 0 seconds, Memory: 2.50Mb
|
||||||
|
OK (5 tests, 5 assertions)
|
||||||
|
```
|
||||||
|
|
||||||
|
New Lines in private keys
|
||||||
|
-----
|
||||||
|
|
||||||
|
If your private key contains `\n` characters, be sure to wrap it in double quotes `""`
|
||||||
|
and not single quotes `''` in order to properly interpret the escaped characters.
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
[3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause).
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "firebase/php-jwt",
|
||||||
|
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
|
||||||
|
"homepage": "https://github.com/firebase/php-jwt",
|
||||||
|
"keywords": [
|
||||||
|
"php",
|
||||||
|
"jwt"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Neuman Vong",
|
||||||
|
"email": "neuman+pear@twilio.com",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Anant Narayanan",
|
||||||
|
"email": "anant@php.net",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present",
|
||||||
|
"ext-sodium": "Support EdDSA (Ed25519) signatures"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Firebase\\JWT\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
|
"phpspec/prophecy-phpunit": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"psr/cache": "^2.0||^3.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
|
"psr/http-factory": "^1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
class BeforeValidException extends \UnexpectedValueException implements JWTExceptionWithPayloadInterface
|
||||||
|
{
|
||||||
|
private object $payload;
|
||||||
|
|
||||||
|
public function setPayload(object $payload): void
|
||||||
|
{
|
||||||
|
$this->payload = $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPayload(): object
|
||||||
|
{
|
||||||
|
return $this->payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
+274
@@ -0,0 +1,274 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use LogicException;
|
||||||
|
use OutOfBoundsException;
|
||||||
|
use Psr\Cache\CacheItemInterface;
|
||||||
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
use Psr\Http\Client\ClientInterface;
|
||||||
|
use Psr\Http\Message\RequestFactoryInterface;
|
||||||
|
use RuntimeException;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @implements ArrayAccess<string, Key>
|
||||||
|
*/
|
||||||
|
class CachedKeySet implements ArrayAccess
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $jwksUri;
|
||||||
|
/**
|
||||||
|
* @var ClientInterface
|
||||||
|
*/
|
||||||
|
private $httpClient;
|
||||||
|
/**
|
||||||
|
* @var RequestFactoryInterface
|
||||||
|
*/
|
||||||
|
private $httpFactory;
|
||||||
|
/**
|
||||||
|
* @var CacheItemPoolInterface
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
/**
|
||||||
|
* @var ?int
|
||||||
|
*/
|
||||||
|
private $expiresAfter;
|
||||||
|
/**
|
||||||
|
* @var ?CacheItemInterface
|
||||||
|
*/
|
||||||
|
private $cacheItem;
|
||||||
|
/**
|
||||||
|
* @var array<string, array<mixed>>
|
||||||
|
*/
|
||||||
|
private $keySet;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cacheKey;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cacheKeyPrefix = 'jwks';
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $maxKeyLength = 64;
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $rateLimit;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $rateLimitCacheKey;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $maxCallsPerMinute = 10;
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $defaultAlg;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $jwksUri,
|
||||||
|
ClientInterface $httpClient,
|
||||||
|
RequestFactoryInterface $httpFactory,
|
||||||
|
CacheItemPoolInterface $cache,
|
||||||
|
?int $expiresAfter = null,
|
||||||
|
bool $rateLimit = false,
|
||||||
|
?string $defaultAlg = null
|
||||||
|
) {
|
||||||
|
$this->jwksUri = $jwksUri;
|
||||||
|
$this->httpClient = $httpClient;
|
||||||
|
$this->httpFactory = $httpFactory;
|
||||||
|
$this->cache = $cache;
|
||||||
|
$this->expiresAfter = $expiresAfter;
|
||||||
|
$this->rateLimit = $rateLimit;
|
||||||
|
$this->defaultAlg = $defaultAlg;
|
||||||
|
$this->setCacheKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $keyId
|
||||||
|
* @return Key
|
||||||
|
*/
|
||||||
|
public function offsetGet($keyId): Key
|
||||||
|
{
|
||||||
|
if (!$this->keyIdExists($keyId)) {
|
||||||
|
throw new OutOfBoundsException('Key ID not found');
|
||||||
|
}
|
||||||
|
return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $keyId
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function offsetExists($keyId): bool
|
||||||
|
{
|
||||||
|
return $this->keyIdExists($keyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $offset
|
||||||
|
* @param Key $value
|
||||||
|
*/
|
||||||
|
public function offsetSet($offset, $value): void
|
||||||
|
{
|
||||||
|
throw new LogicException('Method not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $offset
|
||||||
|
*/
|
||||||
|
public function offsetUnset($offset): void
|
||||||
|
{
|
||||||
|
throw new LogicException('Method not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<mixed>
|
||||||
|
*/
|
||||||
|
private function formatJwksForCache(string $jwks): array
|
||||||
|
{
|
||||||
|
$jwks = json_decode($jwks, true);
|
||||||
|
|
||||||
|
if (!isset($jwks['keys'])) {
|
||||||
|
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($jwks['keys'])) {
|
||||||
|
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
||||||
|
}
|
||||||
|
|
||||||
|
$keys = [];
|
||||||
|
foreach ($jwks['keys'] as $k => $v) {
|
||||||
|
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
||||||
|
$keys[(string) $kid] = $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function keyIdExists(string $keyId): bool
|
||||||
|
{
|
||||||
|
if (null === $this->keySet) {
|
||||||
|
$item = $this->getCacheItem();
|
||||||
|
// Try to load keys from cache
|
||||||
|
if ($item->isHit()) {
|
||||||
|
// item found! retrieve it
|
||||||
|
$this->keySet = $item->get();
|
||||||
|
// If the cached item is a string, the JWKS response was cached (previous behavior).
|
||||||
|
// Parse this into expected format array<kid, jwk> instead.
|
||||||
|
if (\is_string($this->keySet)) {
|
||||||
|
$this->keySet = $this->formatJwksForCache($this->keySet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($this->keySet[$keyId])) {
|
||||||
|
if ($this->rateLimitExceeded()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$request = $this->httpFactory->createRequest('GET', $this->jwksUri);
|
||||||
|
$jwksResponse = $this->httpClient->sendRequest($request);
|
||||||
|
if ($jwksResponse->getStatusCode() !== 200) {
|
||||||
|
throw new UnexpectedValueException(
|
||||||
|
\sprintf('HTTP Error: %d %s for URI "%s"',
|
||||||
|
$jwksResponse->getStatusCode(),
|
||||||
|
$jwksResponse->getReasonPhrase(),
|
||||||
|
$this->jwksUri,
|
||||||
|
),
|
||||||
|
$jwksResponse->getStatusCode()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody());
|
||||||
|
|
||||||
|
if (!isset($this->keySet[$keyId])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $this->getCacheItem();
|
||||||
|
$item->set($this->keySet);
|
||||||
|
if ($this->expiresAfter) {
|
||||||
|
$item->expiresAfter($this->expiresAfter);
|
||||||
|
}
|
||||||
|
$this->cache->save($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function rateLimitExceeded(): bool
|
||||||
|
{
|
||||||
|
if (!$this->rateLimit) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheItem = $this->cache->getItem($this->rateLimitCacheKey);
|
||||||
|
|
||||||
|
$cacheItemData = [];
|
||||||
|
if ($cacheItem->isHit() && \is_array($data = $cacheItem->get())) {
|
||||||
|
$cacheItemData = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$callsPerMinute = $cacheItemData['callsPerMinute'] ?? 0;
|
||||||
|
$expiry = $cacheItemData['expiry'] ?? new \DateTime('+60 seconds', new \DateTimeZone('UTC'));
|
||||||
|
|
||||||
|
if (++$callsPerMinute > $this->maxCallsPerMinute) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheItem->set(['expiry' => $expiry, 'callsPerMinute' => $callsPerMinute]);
|
||||||
|
$cacheItem->expiresAt($expiry);
|
||||||
|
$this->cache->save($cacheItem);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCacheItem(): CacheItemInterface
|
||||||
|
{
|
||||||
|
if (\is_null($this->cacheItem)) {
|
||||||
|
$this->cacheItem = $this->cache->getItem($this->cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->cacheItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setCacheKeys(): void
|
||||||
|
{
|
||||||
|
if (empty($this->jwksUri)) {
|
||||||
|
throw new RuntimeException('JWKS URI is empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure we do not have illegal characters
|
||||||
|
$key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $this->jwksUri);
|
||||||
|
|
||||||
|
// add prefix
|
||||||
|
$key = $this->cacheKeyPrefix . $key;
|
||||||
|
|
||||||
|
// Hash keys if they exceed $maxKeyLength of 64
|
||||||
|
if (\strlen($key) > $this->maxKeyLength) {
|
||||||
|
$key = substr(hash('sha256', $key), 0, $this->maxKeyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cacheKey = $key;
|
||||||
|
|
||||||
|
if ($this->rateLimit) {
|
||||||
|
// add prefix
|
||||||
|
$rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key;
|
||||||
|
|
||||||
|
// Hash keys if they exceed $maxKeyLength of 64
|
||||||
|
if (\strlen($rateLimitKey) > $this->maxKeyLength) {
|
||||||
|
$rateLimitKey = substr(hash('sha256', $rateLimitKey), 0, $this->maxKeyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->rateLimitCacheKey = $rateLimitKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
class ExpiredException extends \UnexpectedValueException implements JWTExceptionWithPayloadInterface
|
||||||
|
{
|
||||||
|
private object $payload;
|
||||||
|
|
||||||
|
public function setPayload(object $payload): void
|
||||||
|
{
|
||||||
|
$this->payload = $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPayload(): object
|
||||||
|
{
|
||||||
|
return $this->payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
+355
@@ -0,0 +1,355 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
use DomainException;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON Web Key implementation, based on this spec:
|
||||||
|
* https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* @category Authentication
|
||||||
|
* @package Authentication_JWT
|
||||||
|
* @author Bui Sy Nguyen <nguyenbs@gmail.com>
|
||||||
|
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
|
||||||
|
* @link https://github.com/firebase/php-jwt
|
||||||
|
*/
|
||||||
|
class JWK
|
||||||
|
{
|
||||||
|
private const OID = '1.2.840.10045.2.1';
|
||||||
|
private const ASN1_OBJECT_IDENTIFIER = 0x06;
|
||||||
|
private const ASN1_SEQUENCE = 0x10; // also defined in JWT
|
||||||
|
private const ASN1_BIT_STRING = 0x03;
|
||||||
|
private const EC_CURVES = [
|
||||||
|
'P-256' => '1.2.840.10045.3.1.7', // Len: 64
|
||||||
|
'secp256k1' => '1.3.132.0.10', // Len: 64
|
||||||
|
'P-384' => '1.3.132.0.34', // Len: 96
|
||||||
|
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
|
||||||
|
];
|
||||||
|
|
||||||
|
// For keys with "kty" equal to "OKP" (Octet Key Pair), the "crv" parameter must contain the key subtype.
|
||||||
|
// This library supports the following subtypes:
|
||||||
|
private const OKP_SUBTYPES = [
|
||||||
|
'Ed25519' => true, // RFC 8037
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a set of JWK keys
|
||||||
|
*
|
||||||
|
* @param array<mixed> $jwks The JSON Web Key Set as an associative array
|
||||||
|
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
|
||||||
|
* JSON Web Key Set
|
||||||
|
*
|
||||||
|
* @return array<string, Key> An associative array of key IDs (kid) to Key objects
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException Provided JWK Set is empty
|
||||||
|
* @throws UnexpectedValueException Provided JWK Set was invalid
|
||||||
|
* @throws DomainException OpenSSL failure
|
||||||
|
*
|
||||||
|
* @uses parseKey
|
||||||
|
*/
|
||||||
|
public static function parseKeySet(array $jwks, ?string $defaultAlg = null): array
|
||||||
|
{
|
||||||
|
$keys = [];
|
||||||
|
|
||||||
|
if (!isset($jwks['keys'])) {
|
||||||
|
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($jwks['keys'])) {
|
||||||
|
throw new InvalidArgumentException('JWK Set did not contain any keys');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($jwks['keys'] as $k => $v) {
|
||||||
|
$kid = isset($v['kid']) ? $v['kid'] : $k;
|
||||||
|
if ($key = self::parseKey($v, $defaultAlg)) {
|
||||||
|
$keys[(string) $kid] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === \count($keys)) {
|
||||||
|
throw new UnexpectedValueException('No supported algorithms found in JWK Set');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a JWK key
|
||||||
|
*
|
||||||
|
* @param array<mixed> $jwk An individual JWK
|
||||||
|
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
|
||||||
|
* JSON Web Key Set
|
||||||
|
*
|
||||||
|
* @return Key The key object for the JWK
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException Provided JWK is empty
|
||||||
|
* @throws UnexpectedValueException Provided JWK was invalid
|
||||||
|
* @throws DomainException OpenSSL failure
|
||||||
|
*
|
||||||
|
* @uses createPemFromModulusAndExponent
|
||||||
|
*/
|
||||||
|
public static function parseKey(array $jwk, ?string $defaultAlg = null): ?Key
|
||||||
|
{
|
||||||
|
if (empty($jwk)) {
|
||||||
|
throw new InvalidArgumentException('JWK must not be empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($jwk['kty'])) {
|
||||||
|
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($jwk['alg'])) {
|
||||||
|
if (\is_null($defaultAlg)) {
|
||||||
|
// The "alg" parameter is optional in a KTY, but an algorithm is required
|
||||||
|
// for parsing in this library. Use the $defaultAlg parameter when parsing the
|
||||||
|
// key set in order to prevent this error.
|
||||||
|
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
|
||||||
|
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
|
||||||
|
}
|
||||||
|
$jwk['alg'] = $defaultAlg;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($jwk['kty']) {
|
||||||
|
case 'RSA':
|
||||||
|
if (!empty($jwk['d'])) {
|
||||||
|
throw new UnexpectedValueException('RSA private keys are not supported');
|
||||||
|
}
|
||||||
|
if (!isset($jwk['n']) || !isset($jwk['e'])) {
|
||||||
|
throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
|
||||||
|
$publicKey = \openssl_pkey_get_public($pem);
|
||||||
|
if (false === $publicKey) {
|
||||||
|
throw new DomainException(
|
||||||
|
'OpenSSL error: ' . \openssl_error_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new Key($publicKey, $jwk['alg']);
|
||||||
|
case 'EC':
|
||||||
|
if (isset($jwk['d'])) {
|
||||||
|
// The key is actually a private key
|
||||||
|
throw new UnexpectedValueException('Key data must be for a public key');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($jwk['crv'])) {
|
||||||
|
throw new UnexpectedValueException('crv not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset(self::EC_CURVES[$jwk['crv']])) {
|
||||||
|
throw new DomainException('Unrecognised or unsupported EC curve');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($jwk['x']) || empty($jwk['y'])) {
|
||||||
|
throw new UnexpectedValueException('x and y not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
$publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
|
||||||
|
return new Key($publicKey, $jwk['alg']);
|
||||||
|
case 'OKP':
|
||||||
|
if (isset($jwk['d'])) {
|
||||||
|
// The key is actually a private key
|
||||||
|
throw new UnexpectedValueException('Key data must be for a public key');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($jwk['crv'])) {
|
||||||
|
throw new UnexpectedValueException('crv not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty(self::OKP_SUBTYPES[$jwk['crv']])) {
|
||||||
|
throw new DomainException('Unrecognised or unsupported OKP key subtype');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($jwk['x'])) {
|
||||||
|
throw new UnexpectedValueException('x not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This library works internally with EdDSA keys (Ed25519) encoded in standard base64.
|
||||||
|
$publicKey = JWT::convertBase64urlToBase64($jwk['x']);
|
||||||
|
return new Key($publicKey, $jwk['alg']);
|
||||||
|
case 'oct':
|
||||||
|
if (!isset($jwk['k'])) {
|
||||||
|
throw new UnexpectedValueException('k not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Key(JWT::urlsafeB64Decode($jwk['k']), $jwk['alg']);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the EC JWK values to pem format.
|
||||||
|
*
|
||||||
|
* @param string $crv The EC curve (only P-256 & P-384 is supported)
|
||||||
|
* @param string $x The EC x-coordinate
|
||||||
|
* @param string $y The EC y-coordinate
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
|
||||||
|
{
|
||||||
|
$pem =
|
||||||
|
self::encodeDER(
|
||||||
|
self::ASN1_SEQUENCE,
|
||||||
|
self::encodeDER(
|
||||||
|
self::ASN1_SEQUENCE,
|
||||||
|
self::encodeDER(
|
||||||
|
self::ASN1_OBJECT_IDENTIFIER,
|
||||||
|
self::encodeOID(self::OID)
|
||||||
|
)
|
||||||
|
. self::encodeDER(
|
||||||
|
self::ASN1_OBJECT_IDENTIFIER,
|
||||||
|
self::encodeOID(self::EC_CURVES[$crv])
|
||||||
|
)
|
||||||
|
) .
|
||||||
|
self::encodeDER(
|
||||||
|
self::ASN1_BIT_STRING,
|
||||||
|
\chr(0x00) . \chr(0x04)
|
||||||
|
. JWT::urlsafeB64Decode($x)
|
||||||
|
. JWT::urlsafeB64Decode($y)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return \sprintf(
|
||||||
|
"-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
|
||||||
|
wordwrap(base64_encode($pem), 64, "\n", true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a public key represented in PEM format from RSA modulus and exponent information
|
||||||
|
*
|
||||||
|
* @param string $n The RSA modulus encoded in Base64
|
||||||
|
* @param string $e The RSA exponent encoded in Base64
|
||||||
|
*
|
||||||
|
* @return string The RSA public key represented in PEM format
|
||||||
|
*
|
||||||
|
* @uses encodeLength
|
||||||
|
*/
|
||||||
|
private static function createPemFromModulusAndExponent(
|
||||||
|
string $n,
|
||||||
|
string $e
|
||||||
|
): string {
|
||||||
|
$mod = JWT::urlsafeB64Decode($n);
|
||||||
|
$exp = JWT::urlsafeB64Decode($e);
|
||||||
|
|
||||||
|
$modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
|
||||||
|
$publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
|
||||||
|
|
||||||
|
$rsaPublicKey = \pack(
|
||||||
|
'Ca*a*a*',
|
||||||
|
48,
|
||||||
|
self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
|
||||||
|
$modulus,
|
||||||
|
$publicExponent
|
||||||
|
);
|
||||||
|
|
||||||
|
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
|
||||||
|
$rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
|
||||||
|
$rsaPublicKey = \chr(0) . $rsaPublicKey;
|
||||||
|
$rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
|
||||||
|
|
||||||
|
$rsaPublicKey = \pack(
|
||||||
|
'Ca*a*',
|
||||||
|
48,
|
||||||
|
self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
|
||||||
|
$rsaOID . $rsaPublicKey
|
||||||
|
);
|
||||||
|
|
||||||
|
return "-----BEGIN PUBLIC KEY-----\r\n" .
|
||||||
|
\chunk_split(\base64_encode($rsaPublicKey), 64) .
|
||||||
|
'-----END PUBLIC KEY-----';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DER-encode the length
|
||||||
|
*
|
||||||
|
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
|
||||||
|
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
|
||||||
|
*
|
||||||
|
* @param int $length
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function encodeLength(int $length): string
|
||||||
|
{
|
||||||
|
if ($length <= 0x7F) {
|
||||||
|
return \chr($length);
|
||||||
|
}
|
||||||
|
|
||||||
|
$temp = \ltrim(\pack('N', $length), \chr(0));
|
||||||
|
|
||||||
|
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a value into a DER object.
|
||||||
|
* Also defined in Firebase\JWT\JWT
|
||||||
|
*
|
||||||
|
* @param int $type DER tag
|
||||||
|
* @param string $value the value to encode
|
||||||
|
* @return string the encoded object
|
||||||
|
*/
|
||||||
|
private static function encodeDER(int $type, string $value): string
|
||||||
|
{
|
||||||
|
$tag_header = 0;
|
||||||
|
if ($type === self::ASN1_SEQUENCE) {
|
||||||
|
$tag_header |= 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type
|
||||||
|
$der = \chr($tag_header | $type);
|
||||||
|
|
||||||
|
// Length
|
||||||
|
$der .= \chr(\strlen($value));
|
||||||
|
|
||||||
|
return $der . $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a string into a DER-encoded OID.
|
||||||
|
*
|
||||||
|
* @param string $oid the OID string
|
||||||
|
* @return string the binary DER-encoded OID
|
||||||
|
*/
|
||||||
|
private static function encodeOID(string $oid): string
|
||||||
|
{
|
||||||
|
$octets = explode('.', $oid);
|
||||||
|
|
||||||
|
// Get the first octet
|
||||||
|
$first = (int) array_shift($octets);
|
||||||
|
$second = (int) array_shift($octets);
|
||||||
|
$oid = \chr($first * 40 + $second);
|
||||||
|
|
||||||
|
// Iterate over subsequent octets
|
||||||
|
foreach ($octets as $octet) {
|
||||||
|
if ($octet == 0) {
|
||||||
|
$oid .= \chr(0x00);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$bin = '';
|
||||||
|
|
||||||
|
while ($octet) {
|
||||||
|
$bin .= \chr(0x80 | ($octet & 0x7f));
|
||||||
|
$octet >>= 7;
|
||||||
|
}
|
||||||
|
$bin[0] = $bin[0] & \chr(0x7f);
|
||||||
|
|
||||||
|
// Convert to big endian if necessary
|
||||||
|
if (pack('V', 65534) == pack('L', 65534)) {
|
||||||
|
$oid .= strrev($bin);
|
||||||
|
} else {
|
||||||
|
$oid .= $bin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oid;
|
||||||
|
}
|
||||||
|
}
|
||||||
+667
@@ -0,0 +1,667 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
use ArrayAccess;
|
||||||
|
use DateTime;
|
||||||
|
use DomainException;
|
||||||
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use OpenSSLAsymmetricKey;
|
||||||
|
use OpenSSLCertificate;
|
||||||
|
use stdClass;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON Web Token implementation, based on this spec:
|
||||||
|
* https://tools.ietf.org/html/rfc7519
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* @category Authentication
|
||||||
|
* @package Authentication_JWT
|
||||||
|
* @author Neuman Vong <neuman@twilio.com>
|
||||||
|
* @author Anant Narayanan <anant@php.net>
|
||||||
|
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
|
||||||
|
* @link https://github.com/firebase/php-jwt
|
||||||
|
*/
|
||||||
|
class JWT
|
||||||
|
{
|
||||||
|
private const ASN1_INTEGER = 0x02;
|
||||||
|
private const ASN1_SEQUENCE = 0x10;
|
||||||
|
private const ASN1_BIT_STRING = 0x03;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When checking nbf, iat or expiration times,
|
||||||
|
* we want to provide some extra leeway time to
|
||||||
|
* account for clock skew.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public static $leeway = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow the current timestamp to be specified.
|
||||||
|
* Useful for fixing a value within unit testing.
|
||||||
|
* Will default to PHP time() value if null.
|
||||||
|
*
|
||||||
|
* @var ?int
|
||||||
|
*/
|
||||||
|
public static $timestamp = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string[]>
|
||||||
|
*/
|
||||||
|
public static $supported_algs = [
|
||||||
|
'ES384' => ['openssl', 'SHA384'],
|
||||||
|
'ES256' => ['openssl', 'SHA256'],
|
||||||
|
'ES256K' => ['openssl', 'SHA256'],
|
||||||
|
'HS256' => ['hash_hmac', 'SHA256'],
|
||||||
|
'HS384' => ['hash_hmac', 'SHA384'],
|
||||||
|
'HS512' => ['hash_hmac', 'SHA512'],
|
||||||
|
'RS256' => ['openssl', 'SHA256'],
|
||||||
|
'RS384' => ['openssl', 'SHA384'],
|
||||||
|
'RS512' => ['openssl', 'SHA512'],
|
||||||
|
'EdDSA' => ['sodium_crypto', 'EdDSA'],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a JWT string into a PHP object.
|
||||||
|
*
|
||||||
|
* @param string $jwt The JWT
|
||||||
|
* @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray The Key or associative array of key IDs
|
||||||
|
* (kid) to Key objects.
|
||||||
|
* If the algorithm used is asymmetric, this is
|
||||||
|
* the public key.
|
||||||
|
* Each Key object contains an algorithm and
|
||||||
|
* matching key.
|
||||||
|
* Supported algorithms are 'ES384','ES256',
|
||||||
|
* 'HS256', 'HS384', 'HS512', 'RS256', 'RS384'
|
||||||
|
* and 'RS512'.
|
||||||
|
* @param stdClass $headers Optional. Populates stdClass with headers.
|
||||||
|
*
|
||||||
|
* @return stdClass The JWT's payload as a PHP object
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException Provided key/key-array was empty or malformed
|
||||||
|
* @throws DomainException Provided JWT is malformed
|
||||||
|
* @throws UnexpectedValueException Provided JWT was invalid
|
||||||
|
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
|
||||||
|
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
|
||||||
|
* @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
|
||||||
|
* @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
|
||||||
|
*
|
||||||
|
* @uses jsonDecode
|
||||||
|
* @uses urlsafeB64Decode
|
||||||
|
*/
|
||||||
|
public static function decode(
|
||||||
|
string $jwt,
|
||||||
|
$keyOrKeyArray,
|
||||||
|
?stdClass &$headers = null
|
||||||
|
): stdClass {
|
||||||
|
// Validate JWT
|
||||||
|
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
|
||||||
|
|
||||||
|
if (empty($keyOrKeyArray)) {
|
||||||
|
throw new InvalidArgumentException('Key may not be empty');
|
||||||
|
}
|
||||||
|
$tks = \explode('.', $jwt);
|
||||||
|
if (\count($tks) !== 3) {
|
||||||
|
throw new UnexpectedValueException('Wrong number of segments');
|
||||||
|
}
|
||||||
|
list($headb64, $bodyb64, $cryptob64) = $tks;
|
||||||
|
$headerRaw = static::urlsafeB64Decode($headb64);
|
||||||
|
if (null === ($header = static::jsonDecode($headerRaw))) {
|
||||||
|
throw new UnexpectedValueException('Invalid header encoding');
|
||||||
|
}
|
||||||
|
if ($headers !== null) {
|
||||||
|
$headers = $header;
|
||||||
|
}
|
||||||
|
$payloadRaw = static::urlsafeB64Decode($bodyb64);
|
||||||
|
if (null === ($payload = static::jsonDecode($payloadRaw))) {
|
||||||
|
throw new UnexpectedValueException('Invalid claims encoding');
|
||||||
|
}
|
||||||
|
if (\is_array($payload)) {
|
||||||
|
// prevent PHP Fatal Error in edge-cases when payload is empty array
|
||||||
|
$payload = (object) $payload;
|
||||||
|
}
|
||||||
|
if (!$payload instanceof stdClass) {
|
||||||
|
throw new UnexpectedValueException('Payload must be a JSON object');
|
||||||
|
}
|
||||||
|
$sig = static::urlsafeB64Decode($cryptob64);
|
||||||
|
if (empty($header->alg)) {
|
||||||
|
throw new UnexpectedValueException('Empty algorithm');
|
||||||
|
}
|
||||||
|
if (empty(static::$supported_algs[$header->alg])) {
|
||||||
|
throw new UnexpectedValueException('Algorithm not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null);
|
||||||
|
|
||||||
|
// Check the algorithm
|
||||||
|
if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
|
||||||
|
// See issue #351
|
||||||
|
throw new UnexpectedValueException('Incorrect key for this algorithm');
|
||||||
|
}
|
||||||
|
if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) {
|
||||||
|
// OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures
|
||||||
|
$sig = self::signatureToDER($sig);
|
||||||
|
}
|
||||||
|
if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) {
|
||||||
|
throw new SignatureInvalidException('Signature verification failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the nbf if it is defined. This is the time that the
|
||||||
|
// token can actually be used. If it's not yet that time, abort.
|
||||||
|
if (isset($payload->nbf) && floor($payload->nbf) > ($timestamp + static::$leeway)) {
|
||||||
|
$ex = new BeforeValidException(
|
||||||
|
'Cannot handle token with nbf prior to ' . \date(DateTime::ISO8601, (int) floor($payload->nbf))
|
||||||
|
);
|
||||||
|
$ex->setPayload($payload);
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that this token has been created before 'now'. This prevents
|
||||||
|
// using tokens that have been created for later use (and haven't
|
||||||
|
// correctly used the nbf claim).
|
||||||
|
if (!isset($payload->nbf) && isset($payload->iat) && floor($payload->iat) > ($timestamp + static::$leeway)) {
|
||||||
|
$ex = new BeforeValidException(
|
||||||
|
'Cannot handle token with iat prior to ' . \date(DateTime::ISO8601, (int) floor($payload->iat))
|
||||||
|
);
|
||||||
|
$ex->setPayload($payload);
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this token has expired.
|
||||||
|
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
|
||||||
|
$ex = new ExpiredException('Expired token');
|
||||||
|
$ex->setPayload($payload);
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts and signs a PHP array into a JWT string.
|
||||||
|
*
|
||||||
|
* @param array<mixed> $payload PHP array
|
||||||
|
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||||
|
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
|
||||||
|
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||||
|
* @param string $keyId
|
||||||
|
* @param array<string, string> $head An array with header elements to attach
|
||||||
|
*
|
||||||
|
* @return string A signed JWT
|
||||||
|
*
|
||||||
|
* @uses jsonEncode
|
||||||
|
* @uses urlsafeB64Encode
|
||||||
|
*/
|
||||||
|
public static function encode(
|
||||||
|
array $payload,
|
||||||
|
$key,
|
||||||
|
string $alg,
|
||||||
|
?string $keyId = null,
|
||||||
|
?array $head = null
|
||||||
|
): string {
|
||||||
|
$header = ['typ' => 'JWT'];
|
||||||
|
if (isset($head)) {
|
||||||
|
$header = \array_merge($header, $head);
|
||||||
|
}
|
||||||
|
$header['alg'] = $alg;
|
||||||
|
if ($keyId !== null) {
|
||||||
|
$header['kid'] = $keyId;
|
||||||
|
}
|
||||||
|
$segments = [];
|
||||||
|
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header));
|
||||||
|
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload));
|
||||||
|
$signing_input = \implode('.', $segments);
|
||||||
|
|
||||||
|
$signature = static::sign($signing_input, $key, $alg);
|
||||||
|
$segments[] = static::urlsafeB64Encode($signature);
|
||||||
|
|
||||||
|
return \implode('.', $segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign a string with a given key and algorithm.
|
||||||
|
*
|
||||||
|
* @param string $msg The message to sign
|
||||||
|
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||||
|
* @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256',
|
||||||
|
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||||
|
*
|
||||||
|
* @return string An encrypted message
|
||||||
|
*
|
||||||
|
* @throws DomainException Unsupported algorithm or bad key was specified
|
||||||
|
*/
|
||||||
|
public static function sign(
|
||||||
|
string $msg,
|
||||||
|
$key,
|
||||||
|
string $alg
|
||||||
|
): string {
|
||||||
|
if (empty(static::$supported_algs[$alg])) {
|
||||||
|
throw new DomainException('Algorithm not supported');
|
||||||
|
}
|
||||||
|
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||||
|
switch ($function) {
|
||||||
|
case 'hash_hmac':
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||||
|
}
|
||||||
|
return \hash_hmac($algorithm, $msg, $key, true);
|
||||||
|
case 'openssl':
|
||||||
|
$signature = '';
|
||||||
|
if (!\is_resource($key) && !openssl_pkey_get_private($key)) {
|
||||||
|
throw new DomainException('OpenSSL unable to validate key');
|
||||||
|
}
|
||||||
|
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
|
||||||
|
if (!$success) {
|
||||||
|
throw new DomainException('OpenSSL unable to sign data');
|
||||||
|
}
|
||||||
|
if ($alg === 'ES256' || $alg === 'ES256K') {
|
||||||
|
$signature = self::signatureFromDER($signature, 256);
|
||||||
|
} elseif ($alg === 'ES384') {
|
||||||
|
$signature = self::signatureFromDER($signature, 384);
|
||||||
|
}
|
||||||
|
return $signature;
|
||||||
|
case 'sodium_crypto':
|
||||||
|
if (!\function_exists('sodium_crypto_sign_detached')) {
|
||||||
|
throw new DomainException('libsodium is not available');
|
||||||
|
}
|
||||||
|
if (!\is_string($key)) {
|
||||||
|
throw new InvalidArgumentException('key must be a string when using EdDSA');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// The last non-empty line is used as the key.
|
||||||
|
$lines = array_filter(explode("\n", $key));
|
||||||
|
$key = base64_decode((string) end($lines));
|
||||||
|
if (\strlen($key) === 0) {
|
||||||
|
throw new DomainException('Key cannot be empty string');
|
||||||
|
}
|
||||||
|
return sodium_crypto_sign_detached($msg, $key);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new DomainException($e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new DomainException('Algorithm not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify a signature with the message, key and method. Not all methods
|
||||||
|
* are symmetric, so we must have a separate verify and sign method.
|
||||||
|
*
|
||||||
|
* @param string $msg The original message (header and body)
|
||||||
|
* @param string $signature The original signature
|
||||||
|
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
|
||||||
|
* @param string $alg The algorithm
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
* @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
|
||||||
|
*/
|
||||||
|
private static function verify(
|
||||||
|
string $msg,
|
||||||
|
string $signature,
|
||||||
|
$keyMaterial,
|
||||||
|
string $alg
|
||||||
|
): bool {
|
||||||
|
if (empty(static::$supported_algs[$alg])) {
|
||||||
|
throw new DomainException('Algorithm not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||||
|
switch ($function) {
|
||||||
|
case 'openssl':
|
||||||
|
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
|
||||||
|
if ($success === 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($success === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// returns 1 on success, 0 on failure, -1 on error.
|
||||||
|
throw new DomainException(
|
||||||
|
'OpenSSL error: ' . \openssl_error_string()
|
||||||
|
);
|
||||||
|
case 'sodium_crypto':
|
||||||
|
if (!\function_exists('sodium_crypto_sign_verify_detached')) {
|
||||||
|
throw new DomainException('libsodium is not available');
|
||||||
|
}
|
||||||
|
if (!\is_string($keyMaterial)) {
|
||||||
|
throw new InvalidArgumentException('key must be a string when using EdDSA');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// The last non-empty line is used as the key.
|
||||||
|
$lines = array_filter(explode("\n", $keyMaterial));
|
||||||
|
$key = base64_decode((string) end($lines));
|
||||||
|
if (\strlen($key) === 0) {
|
||||||
|
throw new DomainException('Key cannot be empty string');
|
||||||
|
}
|
||||||
|
if (\strlen($signature) === 0) {
|
||||||
|
throw new DomainException('Signature cannot be empty string');
|
||||||
|
}
|
||||||
|
return sodium_crypto_sign_verify_detached($signature, $msg, $key);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new DomainException($e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
case 'hash_hmac':
|
||||||
|
default:
|
||||||
|
if (!\is_string($keyMaterial)) {
|
||||||
|
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||||
|
}
|
||||||
|
$hash = \hash_hmac($algorithm, $msg, $keyMaterial, true);
|
||||||
|
return self::constantTimeEquals($hash, $signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a JSON string into a PHP object.
|
||||||
|
*
|
||||||
|
* @param string $input JSON string
|
||||||
|
*
|
||||||
|
* @return mixed The decoded JSON string
|
||||||
|
*
|
||||||
|
* @throws DomainException Provided string was invalid JSON
|
||||||
|
*/
|
||||||
|
public static function jsonDecode(string $input)
|
||||||
|
{
|
||||||
|
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
|
||||||
|
|
||||||
|
if ($errno = \json_last_error()) {
|
||||||
|
self::handleJsonError($errno);
|
||||||
|
} elseif ($obj === null && $input !== 'null') {
|
||||||
|
throw new DomainException('Null result with non-null input');
|
||||||
|
}
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a PHP array into a JSON string.
|
||||||
|
*
|
||||||
|
* @param array<mixed> $input A PHP array
|
||||||
|
*
|
||||||
|
* @return string JSON representation of the PHP array
|
||||||
|
*
|
||||||
|
* @throws DomainException Provided object could not be encoded to valid JSON
|
||||||
|
*/
|
||||||
|
public static function jsonEncode(array $input): string
|
||||||
|
{
|
||||||
|
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
|
||||||
|
if ($errno = \json_last_error()) {
|
||||||
|
self::handleJsonError($errno);
|
||||||
|
} elseif ($json === 'null') {
|
||||||
|
throw new DomainException('Null result with non-null input');
|
||||||
|
}
|
||||||
|
if ($json === false) {
|
||||||
|
throw new DomainException('Provided object could not be encoded to valid JSON');
|
||||||
|
}
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a string with URL-safe Base64.
|
||||||
|
*
|
||||||
|
* @param string $input A Base64 encoded string
|
||||||
|
*
|
||||||
|
* @return string A decoded string
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException invalid base64 characters
|
||||||
|
*/
|
||||||
|
public static function urlsafeB64Decode(string $input): string
|
||||||
|
{
|
||||||
|
return \base64_decode(self::convertBase64UrlToBase64($input));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a string in the base64url (URL-safe Base64) encoding to standard base64.
|
||||||
|
*
|
||||||
|
* @param string $input A Base64 encoded string with URL-safe characters (-_ and no padding)
|
||||||
|
*
|
||||||
|
* @return string A Base64 encoded string with standard characters (+/) and padding (=), when
|
||||||
|
* needed.
|
||||||
|
*
|
||||||
|
* @see https://www.rfc-editor.org/rfc/rfc4648
|
||||||
|
*/
|
||||||
|
public static function convertBase64UrlToBase64(string $input): string
|
||||||
|
{
|
||||||
|
$remainder = \strlen($input) % 4;
|
||||||
|
if ($remainder) {
|
||||||
|
$padlen = 4 - $remainder;
|
||||||
|
$input .= \str_repeat('=', $padlen);
|
||||||
|
}
|
||||||
|
return \strtr($input, '-_', '+/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a string with URL-safe Base64.
|
||||||
|
*
|
||||||
|
* @param string $input The string you want encoded
|
||||||
|
*
|
||||||
|
* @return string The base64 encode of what you passed in
|
||||||
|
*/
|
||||||
|
public static function urlsafeB64Encode(string $input): string
|
||||||
|
{
|
||||||
|
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if an algorithm has been provided for each Key
|
||||||
|
*
|
||||||
|
* @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray
|
||||||
|
* @param string|null $kid
|
||||||
|
*
|
||||||
|
* @throws UnexpectedValueException
|
||||||
|
*
|
||||||
|
* @return Key
|
||||||
|
*/
|
||||||
|
private static function getKey(
|
||||||
|
$keyOrKeyArray,
|
||||||
|
?string $kid
|
||||||
|
): Key {
|
||||||
|
if ($keyOrKeyArray instanceof Key) {
|
||||||
|
return $keyOrKeyArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($kid) && $kid !== '0') {
|
||||||
|
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($keyOrKeyArray instanceof CachedKeySet) {
|
||||||
|
// Skip "isset" check, as this will automatically refresh if not set
|
||||||
|
return $keyOrKeyArray[$kid];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($keyOrKeyArray[$kid])) {
|
||||||
|
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $keyOrKeyArray[$kid];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $left The string of known length to compare against
|
||||||
|
* @param string $right The user-supplied string
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function constantTimeEquals(string $left, string $right): bool
|
||||||
|
{
|
||||||
|
if (\function_exists('hash_equals')) {
|
||||||
|
return \hash_equals($left, $right);
|
||||||
|
}
|
||||||
|
$len = \min(self::safeStrlen($left), self::safeStrlen($right));
|
||||||
|
|
||||||
|
$status = 0;
|
||||||
|
for ($i = 0; $i < $len; $i++) {
|
||||||
|
$status |= (\ord($left[$i]) ^ \ord($right[$i]));
|
||||||
|
}
|
||||||
|
$status |= (self::safeStrlen($left) ^ self::safeStrlen($right));
|
||||||
|
|
||||||
|
return ($status === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to create a JSON error.
|
||||||
|
*
|
||||||
|
* @param int $errno An error number from json_last_error()
|
||||||
|
*
|
||||||
|
* @throws DomainException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function handleJsonError(int $errno): void
|
||||||
|
{
|
||||||
|
$messages = [
|
||||||
|
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
|
||||||
|
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
||||||
|
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
|
||||||
|
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
|
||||||
|
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
|
||||||
|
];
|
||||||
|
throw new DomainException(
|
||||||
|
isset($messages[$errno])
|
||||||
|
? $messages[$errno]
|
||||||
|
: 'Unknown JSON error: ' . $errno
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of bytes in cryptographic strings.
|
||||||
|
*
|
||||||
|
* @param string $str
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private static function safeStrlen(string $str): int
|
||||||
|
{
|
||||||
|
if (\function_exists('mb_strlen')) {
|
||||||
|
return \mb_strlen($str, '8bit');
|
||||||
|
}
|
||||||
|
return \strlen($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an ECDSA signature to an ASN.1 DER sequence
|
||||||
|
*
|
||||||
|
* @param string $sig The ECDSA signature to convert
|
||||||
|
* @return string The encoded DER object
|
||||||
|
*/
|
||||||
|
private static function signatureToDER(string $sig): string
|
||||||
|
{
|
||||||
|
// Separate the signature into r-value and s-value
|
||||||
|
$length = max(1, (int) (\strlen($sig) / 2));
|
||||||
|
list($r, $s) = \str_split($sig, $length);
|
||||||
|
|
||||||
|
// Trim leading zeros
|
||||||
|
$r = \ltrim($r, "\x00");
|
||||||
|
$s = \ltrim($s, "\x00");
|
||||||
|
|
||||||
|
// Convert r-value and s-value from unsigned big-endian integers to
|
||||||
|
// signed two's complement
|
||||||
|
if (\ord($r[0]) > 0x7f) {
|
||||||
|
$r = "\x00" . $r;
|
||||||
|
}
|
||||||
|
if (\ord($s[0]) > 0x7f) {
|
||||||
|
$s = "\x00" . $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::encodeDER(
|
||||||
|
self::ASN1_SEQUENCE,
|
||||||
|
self::encodeDER(self::ASN1_INTEGER, $r) .
|
||||||
|
self::encodeDER(self::ASN1_INTEGER, $s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a value into a DER object.
|
||||||
|
*
|
||||||
|
* @param int $type DER tag
|
||||||
|
* @param string $value the value to encode
|
||||||
|
*
|
||||||
|
* @return string the encoded object
|
||||||
|
*/
|
||||||
|
private static function encodeDER(int $type, string $value): string
|
||||||
|
{
|
||||||
|
$tag_header = 0;
|
||||||
|
if ($type === self::ASN1_SEQUENCE) {
|
||||||
|
$tag_header |= 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type
|
||||||
|
$der = \chr($tag_header | $type);
|
||||||
|
|
||||||
|
// Length
|
||||||
|
$der .= \chr(\strlen($value));
|
||||||
|
|
||||||
|
return $der . $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes signature from a DER object.
|
||||||
|
*
|
||||||
|
* @param string $der binary signature in DER format
|
||||||
|
* @param int $keySize the number of bits in the key
|
||||||
|
*
|
||||||
|
* @return string the signature
|
||||||
|
*/
|
||||||
|
private static function signatureFromDER(string $der, int $keySize): string
|
||||||
|
{
|
||||||
|
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
|
||||||
|
list($offset, $_) = self::readDER($der);
|
||||||
|
list($offset, $r) = self::readDER($der, $offset);
|
||||||
|
list($offset, $s) = self::readDER($der, $offset);
|
||||||
|
|
||||||
|
// Convert r-value and s-value from signed two's compliment to unsigned
|
||||||
|
// big-endian integers
|
||||||
|
$r = \ltrim($r, "\x00");
|
||||||
|
$s = \ltrim($s, "\x00");
|
||||||
|
|
||||||
|
// Pad out r and s so that they are $keySize bits long
|
||||||
|
$r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||||
|
$s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||||
|
|
||||||
|
return $r . $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads binary DER-encoded data and decodes into a single object
|
||||||
|
*
|
||||||
|
* @param string $der the binary data in DER format
|
||||||
|
* @param int $offset the offset of the data stream containing the object
|
||||||
|
* to decode
|
||||||
|
*
|
||||||
|
* @return array{int, string|null} the new offset and the decoded object
|
||||||
|
*/
|
||||||
|
private static function readDER(string $der, int $offset = 0): array
|
||||||
|
{
|
||||||
|
$pos = $offset;
|
||||||
|
$size = \strlen($der);
|
||||||
|
$constructed = (\ord($der[$pos]) >> 5) & 0x01;
|
||||||
|
$type = \ord($der[$pos++]) & 0x1f;
|
||||||
|
|
||||||
|
// Length
|
||||||
|
$len = \ord($der[$pos++]);
|
||||||
|
if ($len & 0x80) {
|
||||||
|
$n = $len & 0x1f;
|
||||||
|
$len = 0;
|
||||||
|
while ($n-- && $pos < $size) {
|
||||||
|
$len = ($len << 8) | \ord($der[$pos++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value
|
||||||
|
if ($type === self::ASN1_BIT_STRING) {
|
||||||
|
$pos++; // Skip the first contents octet (padding indicator)
|
||||||
|
$data = \substr($der, $pos, $len - 1);
|
||||||
|
$pos += $len - 1;
|
||||||
|
} elseif (!$constructed) {
|
||||||
|
$data = \substr($der, $pos, $len);
|
||||||
|
$pos += $len;
|
||||||
|
} else {
|
||||||
|
$data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$pos, $data];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
interface JWTExceptionWithPayloadInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the payload that caused this exception.
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function getPayload(): object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the payload that caused this exception.
|
||||||
|
*
|
||||||
|
* @param object $payload
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPayload(object $payload): void;
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use OpenSSLAsymmetricKey;
|
||||||
|
use OpenSSLCertificate;
|
||||||
|
use TypeError;
|
||||||
|
|
||||||
|
class Key
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
|
||||||
|
* @param string $algorithm
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
private $keyMaterial,
|
||||||
|
private string $algorithm
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!\is_string($keyMaterial)
|
||||||
|
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
|
||||||
|
&& !$keyMaterial instanceof OpenSSLCertificate
|
||||||
|
&& !\is_resource($keyMaterial)
|
||||||
|
) {
|
||||||
|
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($keyMaterial)) {
|
||||||
|
throw new InvalidArgumentException('Key material must not be empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($algorithm)) {
|
||||||
|
throw new InvalidArgumentException('Algorithm must not be empty');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the algorithm valid for this key
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAlgorithm(): string
|
||||||
|
{
|
||||||
|
return $this->algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
|
||||||
|
*/
|
||||||
|
public function getKeyMaterial()
|
||||||
|
{
|
||||||
|
return $this->keyMaterial;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
class SignatureInvalidException extends \UnexpectedValueException
|
||||||
|
{
|
||||||
|
}
|
||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2009-2020 Daniele Alessandri (original work)
|
||||||
|
Copyright (c) 2021-2024 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.
|
||||||
Vendored
+510
@@ -0,0 +1,510 @@
|
|||||||
|
# Predis #
|
||||||
|
|
||||||
|
[![Software license][ico-license]](LICENSE)
|
||||||
|
[![Latest stable][ico-version-stable]][link-releases]
|
||||||
|
[![Latest development][ico-version-dev]][link-releases]
|
||||||
|
[![Monthly installs][ico-downloads-monthly]][link-downloads]
|
||||||
|
[![Build status][ico-build]][link-actions]
|
||||||
|
[![Coverage Status][ico-coverage]][link-coverage]
|
||||||
|
|
||||||
|
A flexible and feature-complete [Redis](http://redis.io) / [Valkey](https://github.com/valkey-io/valkey) client for PHP 7.2 and newer.
|
||||||
|
|
||||||
|
More details about this project can be found on the [frequently asked questions](FAQ.md).
|
||||||
|
|
||||||
|
|
||||||
|
## Main features ##
|
||||||
|
|
||||||
|
- Support for Redis from __3.0__ to __8.0__.
|
||||||
|
- Support for clustering using client-side sharding and pluggable keyspace distributors.
|
||||||
|
- Support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
|
||||||
|
- Support for master-slave replication setups and [redis-sentinel](http://redis.io/topics/sentinel).
|
||||||
|
- Transparent key prefixing of keys using a customizable prefix strategy.
|
||||||
|
- Command pipelining on both single nodes and clusters (client-side sharding only).
|
||||||
|
- Abstraction for Redis transactions (Redis >= 2.0) and CAS operations (Redis >= 2.2).
|
||||||
|
- Abstraction for Lua scripting (Redis >= 2.6) and automatic switching between `EVALSHA` or `EVAL`.
|
||||||
|
- Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators.
|
||||||
|
- Connections are established lazily by the client upon the first command and can be persisted.
|
||||||
|
- Connections can be established via TCP/IP (also TLS/SSL-encrypted) or UNIX domain sockets.
|
||||||
|
- Support for custom connection classes for providing different network or protocol backends.
|
||||||
|
- Flexible system for defining custom commands and override the default ones.
|
||||||
|
|
||||||
|
|
||||||
|
## How to _install_ and use Predis ##
|
||||||
|
|
||||||
|
This library can be found on [Packagist](http://packagist.org/packages/predis/predis) for an easier
|
||||||
|
management of projects dependencies using [Composer](http://packagist.org/about-composer).
|
||||||
|
Compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
|
||||||
|
|
||||||
|
```shell
|
||||||
|
composer require predis/predis
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Loading the library ###
|
||||||
|
|
||||||
|
Predis relies on the autoloading features of PHP to load its files when needed and complies with the
|
||||||
|
[PSR-4 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md).
|
||||||
|
Autoloading is handled automatically when dependencies are managed through Composer, but it is also
|
||||||
|
possible to leverage its own autoloader in projects or scripts lacking any autoload facility:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Prepend a base path if Predis is not available in your "include_path".
|
||||||
|
require 'Predis/Autoloader.php';
|
||||||
|
|
||||||
|
Predis\Autoloader::register();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Connecting to Redis ###
|
||||||
|
|
||||||
|
When creating a client instance without passing any connection parameter, Predis assumes `127.0.0.1`
|
||||||
|
and `6379` as default host and port. The default timeout for the `connect()` operation is 5 seconds:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new Predis\Client();
|
||||||
|
$client->set('foo', 'bar');
|
||||||
|
$value = $client->get('foo');
|
||||||
|
```
|
||||||
|
|
||||||
|
Connection parameters can be supplied either in the form of URI strings or named arrays. The latter
|
||||||
|
is the preferred way to supply parameters, but URI strings can be useful when parameters are read
|
||||||
|
from non-structured or partially-structured sources:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Parameters passed using a named array:
|
||||||
|
$client = new Predis\Client([
|
||||||
|
'scheme' => 'tcp',
|
||||||
|
'host' => '10.0.0.1',
|
||||||
|
'port' => 6379,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Same set of parameters, passed using an URI string:
|
||||||
|
$client = new Predis\Client('tcp://10.0.0.1:6379');
|
||||||
|
```
|
||||||
|
|
||||||
|
Password protected servers can be accessed by adding `password` to the parameters set. When ACLs are
|
||||||
|
enabled on Redis >= 6.0, both `username` and `password` are required for user authentication.
|
||||||
|
|
||||||
|
It is also possible to connect to local instances of Redis using UNIX domain sockets, in this case
|
||||||
|
the parameters must use the `unix` scheme and specify a path for the socket file:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new Predis\Client(['scheme' => 'unix', 'path' => '/path/to/redis.sock']);
|
||||||
|
$client = new Predis\Client('unix:/path/to/redis.sock');
|
||||||
|
```
|
||||||
|
|
||||||
|
The client can leverage TLS/SSL encryption to connect to secured remote Redis instances without the
|
||||||
|
need to configure an SSL proxy like stunnel. This can be useful when connecting to nodes running on
|
||||||
|
various cloud hosting providers. Encryption can be enabled with using the `tls` scheme and an array
|
||||||
|
of suitable [options](http://php.net/manual/context.ssl.php) passed via the `ssl` parameter:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Named array of connection parameters:
|
||||||
|
$client = new Predis\Client([
|
||||||
|
'scheme' => 'tls',
|
||||||
|
'ssl' => ['cafile' => 'private.pem', 'verify_peer' => true],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Same set of parameters, but using an URI string:
|
||||||
|
$client = new Predis\Client('tls://127.0.0.1?ssl[cafile]=private.pem&ssl[verify_peer]=1');
|
||||||
|
```
|
||||||
|
|
||||||
|
The connection schemes [`redis`](http://www.iana.org/assignments/uri-schemes/prov/redis) (alias of
|
||||||
|
`tcp`) and [`rediss`](http://www.iana.org/assignments/uri-schemes/prov/rediss) (alias of `tls`) are
|
||||||
|
also supported, with the difference that URI strings containing these schemes are parsed following
|
||||||
|
the rules described on their respective IANA provisional registration documents.
|
||||||
|
|
||||||
|
The actual list of supported connection parameters can vary depending on each connection backend so
|
||||||
|
it is recommended to refer to their specific documentation or implementation for details.
|
||||||
|
|
||||||
|
Predis can aggregate multiple connections when providing an array of connection parameters and the
|
||||||
|
appropriate option to instruct the client about how to aggregate them (clustering, replication or a
|
||||||
|
custom aggregation logic). Named arrays and URI strings can be mixed when providing configurations
|
||||||
|
for each node:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new Predis\Client([
|
||||||
|
'tcp://10.0.0.1?alias=first-node', ['host' => '10.0.0.2', 'alias' => 'second-node'],
|
||||||
|
], [
|
||||||
|
'cluster' => 'predis',
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [aggregate connections](#aggregate-connections) section of this document for more details.
|
||||||
|
|
||||||
|
Connections to Redis are lazy meaning that the client connects to a server only if and when needed.
|
||||||
|
While it is recommended to let the client do its own stuff under the hood, there may be times when
|
||||||
|
it is still desired to have control of when the connection is opened or closed: this can easily be
|
||||||
|
achieved by invoking `$client->connect()` and `$client->disconnect()`. Please note that the effect
|
||||||
|
of these methods on aggregate connections may differ depending on each specific implementation.
|
||||||
|
|
||||||
|
#### Persistent connections ####
|
||||||
|
|
||||||
|
To increase a performance of your application you may set up a client to use persistent TCP connection, this way
|
||||||
|
client saves a time on socket creation and connection handshake. By default, connection is created on first-command
|
||||||
|
execution and will be automatically closed by GC before the process is being killed.
|
||||||
|
However, if your application is backed by PHP-FPM the processes are idle, and you may set up it to be persistent and
|
||||||
|
reusable across multiple script execution within the same process.
|
||||||
|
|
||||||
|
To enable the persistent connection mode you should provide following configuration:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Standalone
|
||||||
|
$client = new Predis\Client(['persistent' => true]);
|
||||||
|
|
||||||
|
// Cluster
|
||||||
|
$client = new Predis\Client(
|
||||||
|
['tcp://host:port', 'tcp://host:port', 'tcp://host:port'],
|
||||||
|
['cluster' => 'redis', 'parameters' => ['persistent' => true]]
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**
|
||||||
|
|
||||||
|
If you operate on multiple clients within the same application, and they communicate with the same resource, by default
|
||||||
|
they will share the same socket (that's the default behaviour of persistent sockets). So in this case you would need
|
||||||
|
to additionally provide a `conn_uid` identifier for each client, this way each client will create its own socket so
|
||||||
|
the connection context won't be shared across clients. This socket behaviour explained
|
||||||
|
[here](https://www.php.net/manual/en/function.stream-socket-client.php#105393)
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Standalone
|
||||||
|
$client1 = new Predis\Client(['persistent' => true, 'conn_uid' => 'id_1']);
|
||||||
|
$client2 = new Predis\Client(['persistent' => true, 'conn_uid' => 'id_2']);
|
||||||
|
|
||||||
|
// Cluster
|
||||||
|
$client1 = new Predis\Client(
|
||||||
|
['tcp://host:port', 'tcp://host:port', 'tcp://host:port'],
|
||||||
|
['cluster' => 'redis', 'parameters' => ['persistent' => true, 'conn_uid' => 'id_1']]
|
||||||
|
);
|
||||||
|
$client2 = new Predis\Client(
|
||||||
|
['tcp://host:port', 'tcp://host:port', 'tcp://host:port'],
|
||||||
|
['cluster' => 'redis', 'parameters' => ['persistent' => true, 'conn_uid' => 'id_2']]
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client configuration ###
|
||||||
|
|
||||||
|
Many aspects and behaviors of the client can be configured by passing specific client options to the
|
||||||
|
second argument of `Predis\Client::__construct()`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new Predis\Client($parameters, ['prefix' => 'sample:']);
|
||||||
|
```
|
||||||
|
|
||||||
|
Options are managed using a mini DI-alike container and their values can be lazily initialized only
|
||||||
|
when needed. The client options supported by default in Predis are:
|
||||||
|
|
||||||
|
- `prefix`: prefix string applied to every key found in commands.
|
||||||
|
- `exceptions`: whether the client should throw or return responses upon Redis errors.
|
||||||
|
- `connections`: list of connection backends or a connection factory instance.
|
||||||
|
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable).
|
||||||
|
- `replication`: specifies a replication backend (`predis`, `sentinel` or callable).
|
||||||
|
- `aggregate`: configures the client with a custom aggregate connection (callable).
|
||||||
|
- `parameters`: list of default connection parameters for aggregate connections.
|
||||||
|
- `commands`: specifies a command factory instance to use through the library.
|
||||||
|
|
||||||
|
Users can also provide custom options with values or callable objects (for lazy initialization) that
|
||||||
|
are stored in the options container for later use through the library.
|
||||||
|
|
||||||
|
|
||||||
|
### Aggregate connections ###
|
||||||
|
|
||||||
|
Aggregate connections are the foundation upon which Predis implements clustering and replication and
|
||||||
|
they are used to group multiple connections to single Redis nodes and hide the specific logic needed
|
||||||
|
to handle them properly depending on the context. Aggregate connections usually require an array of
|
||||||
|
connection parameters along with the appropriate client option when creating a new client instance.
|
||||||
|
|
||||||
|
#### Cluster ####
|
||||||
|
|
||||||
|
Predis can be configured to work in clustering mode with a traditional client-side sharding approach
|
||||||
|
to create a cluster of independent nodes and distribute the keyspace among them. This approach needs
|
||||||
|
some sort of external health monitoring of nodes and requires the keyspace to be rebalanced manually
|
||||||
|
when nodes are added or removed:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||||
|
$options = ['cluster' => 'predis'];
|
||||||
|
|
||||||
|
$client = new Predis\Client($parameters);
|
||||||
|
```
|
||||||
|
|
||||||
|
Along with Redis 3.0, a new supervised and coordinated type of clustering was introduced in the form
|
||||||
|
of [redis-cluster](http://redis.io/topics/cluster-tutorial). This kind of approach uses a different
|
||||||
|
algorithm to distribute the keyspaces, with Redis nodes coordinating themselves by communicating via
|
||||||
|
a gossip protocol to handle health status, rebalancing, nodes discovery and request redirection. In
|
||||||
|
order to connect to a cluster managed by redis-cluster, the client requires a list of its nodes (not
|
||||||
|
necessarily complete since it will automatically discover new nodes if necessary) and the `cluster`
|
||||||
|
client options set to `redis`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||||
|
$options = ['cluster' => 'redis'];
|
||||||
|
|
||||||
|
$client = new Predis\Client($parameters, $options);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Replication ####
|
||||||
|
|
||||||
|
The client can be configured to operate in a single master / multiple slaves setup to provide better
|
||||||
|
service availability. When using replication, Predis recognizes read-only commands and sends them to
|
||||||
|
a random slave in order to provide some sort of load-balancing and switches to the master as soon as
|
||||||
|
it detects a command that performs any kind of operation that would end up modifying the keyspace or
|
||||||
|
the value of a key. Instead of raising a connection error when a slave fails, the client attempts to
|
||||||
|
fall back to a different slave among the ones provided in the configuration.
|
||||||
|
|
||||||
|
The basic configuration needed to use the client in replication mode requires one Redis server to be
|
||||||
|
identified as the master (this can be done via connection parameters by setting the `role` parameter
|
||||||
|
to `master`) and one or more slaves (in this case setting `role` to `slave` for slaves is optional):
|
||||||
|
|
||||||
|
```php
|
||||||
|
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||||
|
$options = ['replication' => 'predis'];
|
||||||
|
|
||||||
|
$client = new Predis\Client($parameters, $options);
|
||||||
|
```
|
||||||
|
|
||||||
|
The above configuration has a static list of servers and relies entirely on the client's logic, but
|
||||||
|
it is possible to rely on [`redis-sentinel`](http://redis.io/topics/sentinel) for a more robust HA
|
||||||
|
environment with sentinel servers acting as a source of authority for clients for service discovery.
|
||||||
|
The minimum configuration required by the client to work with redis-sentinel is a list of connection
|
||||||
|
parameters pointing to a bunch of sentinel instances, the `replication` option set to `sentinel` and
|
||||||
|
the `service` option set to the name of the service:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||||
|
$options = ['replication' => 'sentinel', 'service' => 'mymaster'];
|
||||||
|
|
||||||
|
$client = new Predis\Client($sentinels, $options);
|
||||||
|
```
|
||||||
|
|
||||||
|
If the master and slave nodes are configured to require an authentication from clients, a password
|
||||||
|
must be provided via the global `parameters` client option. This option can also be used to specify
|
||||||
|
a different database index. The client options array would then look like this:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$options = [
|
||||||
|
'replication' => 'sentinel',
|
||||||
|
'service' => 'mymaster',
|
||||||
|
'parameters' => [
|
||||||
|
'password' => $secretpassword,
|
||||||
|
'database' => 10,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
While Predis is able to distinguish commands performing write and read-only operations, `EVAL` and
|
||||||
|
`EVALSHA` represent a corner case in which the client switches to the master node because it cannot
|
||||||
|
tell when a Lua script is safe to be executed on slaves. While this is indeed the default behavior,
|
||||||
|
when certain Lua scripts do not perform write operations it is possible to provide an hint to tell
|
||||||
|
the client to stick with slaves for their execution:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||||
|
$options = ['replication' => function () {
|
||||||
|
// Set scripts that won't trigger a switch from a slave to the master node.
|
||||||
|
$strategy = new Predis\Replication\ReplicationStrategy();
|
||||||
|
$strategy->setScriptReadOnly($LUA_SCRIPT);
|
||||||
|
|
||||||
|
return new Predis\Connection\Replication\MasterSlaveReplication($strategy);
|
||||||
|
}];
|
||||||
|
|
||||||
|
$client = new Predis\Client($parameters, $options);
|
||||||
|
$client->eval($LUA_SCRIPT, 0); // Sticks to slave using `eval`...
|
||||||
|
$client->evalsha(sha1($LUA_SCRIPT), 0); // ... and `evalsha`, too.
|
||||||
|
```
|
||||||
|
|
||||||
|
The [`examples`](examples/) directory contains a few scripts that demonstrate how the client can be
|
||||||
|
configured and used to leverage replication in both basic and complex scenarios.
|
||||||
|
|
||||||
|
|
||||||
|
### Command pipelines ###
|
||||||
|
|
||||||
|
Pipelining can help with performances when many commands need to be sent to a server by reducing the
|
||||||
|
latency introduced by network round-trip timings. Pipelining also works with aggregate connections.
|
||||||
|
The client can execute the pipeline inside a callable block or return a pipeline instance with the
|
||||||
|
ability to chain commands thanks to its fluent interface:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Executes a pipeline inside the given callable block:
|
||||||
|
$responses = $client->pipeline(function ($pipe) {
|
||||||
|
for ($i = 0; $i < 1000; $i++) {
|
||||||
|
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
|
||||||
|
$pipe->get("key:$i");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns a pipeline that can be chained thanks to its fluent interface:
|
||||||
|
$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Transactions ###
|
||||||
|
|
||||||
|
The client provides an abstraction for Redis transactions based on `MULTI` and `EXEC` with a similar
|
||||||
|
interface to command pipelines:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Executes a transaction inside the given callable block:
|
||||||
|
$responses = $client->transaction(function ($tx) {
|
||||||
|
$tx->set('foo', 'bar');
|
||||||
|
$tx->get('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Returns a transaction that can be chained thanks to its fluent interface:
|
||||||
|
$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute();
|
||||||
|
```
|
||||||
|
|
||||||
|
This abstraction can perform check-and-set operations thanks to `WATCH` and `UNWATCH` and provides
|
||||||
|
automatic retries of transactions aborted by Redis when `WATCH`ed keys are touched. For an example
|
||||||
|
of a transaction using CAS you can see [the following example](examples/transaction_using_cas.php).
|
||||||
|
|
||||||
|
|
||||||
|
### Adding new commands ###
|
||||||
|
|
||||||
|
While we try to update Predis to stay up to date with all the commands available in Redis, you might
|
||||||
|
prefer to stick with an old version of the library or provide a different way to filter arguments or
|
||||||
|
parse responses for specific commands. To achieve that, Predis provides the ability to implement new
|
||||||
|
command classes to define or override commands in the default command factory used by the client:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Define a new command by extending Predis\Command\Command:
|
||||||
|
class BrandNewRedisCommand extends Predis\Command\Command
|
||||||
|
{
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return 'NEWCMD';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject your command in the current command factory:
|
||||||
|
$client = new Predis\Client($parameters, [
|
||||||
|
'commands' => [
|
||||||
|
'newcmd' => 'BrandNewRedisCommand',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $client->newcmd();
|
||||||
|
```
|
||||||
|
|
||||||
|
There is also a method to send raw commands without filtering their arguments or parsing responses.
|
||||||
|
Users must provide the list of arguments for the command as an array, following the signatures as
|
||||||
|
defined by the [Redis documentation for commands](http://redis.io/commands):
|
||||||
|
|
||||||
|
```php
|
||||||
|
$response = $client->executeRaw(['SET', 'foo', 'bar']);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Script commands ###
|
||||||
|
|
||||||
|
While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using
|
||||||
|
directly [`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha),
|
||||||
|
Predis offers script commands as an higher level abstraction built upon them to make things simple.
|
||||||
|
Script commands can be registered in the command factory used by the client and are accessible as if
|
||||||
|
they were plain Redis commands, but they define Lua scripts that get transmitted to the server for
|
||||||
|
remote execution. Internally they use [`EVALSHA`](http://redis.io/commands/evalsha) by default and
|
||||||
|
identify a script by its SHA1 hash to save bandwidth, but [`EVAL`](http://redis.io/commands/eval)
|
||||||
|
is used as a fall back when needed:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Define a new script command by extending Predis\Command\ScriptCommand:
|
||||||
|
class ListPushRandomValue extends Predis\Command\ScriptCommand
|
||||||
|
{
|
||||||
|
public function getKeysCount()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScript()
|
||||||
|
{
|
||||||
|
return <<<LUA
|
||||||
|
math.randomseed(ARGV[1])
|
||||||
|
local rnd = tostring(math.random())
|
||||||
|
redis.call('lpush', KEYS[1], rnd)
|
||||||
|
return rnd
|
||||||
|
LUA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject the script command in the current command factory:
|
||||||
|
$client = new Predis\Client($parameters, [
|
||||||
|
'commands' => [
|
||||||
|
'lpushrand' => 'ListPushRandomValue',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $client->lpushrand('random_values', $seed = mt_rand());
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Customizable connection backends ###
|
||||||
|
|
||||||
|
Predis can use different connection backends to connect to Redis. The builtin Relay integration
|
||||||
|
leverages the [Relay](https://github.com/cachewerk/relay) extension for PHP for major performance
|
||||||
|
gains, by caching a partial replica of the Redis dataset in PHP shared runtime memory.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new Predis\Client('tcp://127.0.0.1', [
|
||||||
|
'connections' => 'relay',
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
Developers can create their own connection classes to support whole new network backends, extend
|
||||||
|
existing classes or provide completely different implementations. Connection classes must implement
|
||||||
|
`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
class MyConnectionClass implements Predis\Connection\NodeConnectionInterface
|
||||||
|
{
|
||||||
|
// Implementation goes here...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use MyConnectionClass to handle connections for the `tcp` scheme:
|
||||||
|
$client = new Predis\Client('tcp://127.0.0.1', [
|
||||||
|
'connections' => ['tcp' => 'MyConnectionClass'],
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
For a more in-depth insight on how to create new connection backends you can refer to the actual
|
||||||
|
implementation of the standard connection classes available in the `Predis\Connection` namespace.
|
||||||
|
|
||||||
|
|
||||||
|
## Development ##
|
||||||
|
|
||||||
|
|
||||||
|
### Reporting bugs and contributing code ###
|
||||||
|
|
||||||
|
Contributions to Predis are highly appreciated either in the form of pull requests for new features,
|
||||||
|
bug fixes, or just bug reports. We only ask you to adhere to issue and pull request templates.
|
||||||
|
|
||||||
|
|
||||||
|
### Test suite ###
|
||||||
|
|
||||||
|
__ATTENTION__: Do not ever run the test suite shipped with Predis against instances of Redis running
|
||||||
|
in production environments or containing data you are interested in!
|
||||||
|
|
||||||
|
Predis has a comprehensive test suite covering every aspect of the library and that can optionally
|
||||||
|
perform integration tests against a running instance of Redis (required >= 2.4.0 in order to verify
|
||||||
|
the correct behavior of the implementation of each command. Integration tests for unsupported Redis
|
||||||
|
commands are automatically skipped. If you do not have Redis up and running, integration tests can
|
||||||
|
be disabled. See [the tests README](tests/README.md) for more details about testing this library.
|
||||||
|
|
||||||
|
Predis uses GitHub Actions for continuous integration and the history for past and current builds can be
|
||||||
|
found [on its actions page](https://github.com/predis/predis/actions).
|
||||||
|
|
||||||
|
### License ###
|
||||||
|
|
||||||
|
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
|
||||||
|
|
||||||
|
[ico-license]: https://img.shields.io/github/license/predis/predis.svg?style=flat-square
|
||||||
|
[ico-version-stable]: https://img.shields.io/github/v/tag/predis/predis?label=stable&style=flat-square
|
||||||
|
[ico-version-dev]: https://img.shields.io/github/v/tag/predis/predis?include_prereleases&label=pre-release&style=flat-square
|
||||||
|
[ico-downloads-monthly]: https://img.shields.io/packagist/dm/predis/predis.svg?style=flat-square
|
||||||
|
[ico-build]: https://img.shields.io/github/actions/workflow/status/predis/predis/tests.yml?branch=main&style=flat-square
|
||||||
|
[ico-coverage]: https://img.shields.io/coverallsCoverage/github/predis/predis?style=flat-square
|
||||||
|
|
||||||
|
[link-releases]: https://github.com/predis/predis/releases
|
||||||
|
[link-actions]: https://github.com/predis/predis/actions
|
||||||
|
[link-downloads]: https://packagist.org/packages/predis/predis/stats
|
||||||
|
[link-coverage]: https://coveralls.io/github/predis/predis
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require __DIR__.'/src/Autoloader.php';
|
||||||
|
|
||||||
|
Predis\Autoloader::register();
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "predis/predis",
|
||||||
|
"type": "library",
|
||||||
|
"description": "A flexible and feature-complete Redis/Valkey client for PHP.",
|
||||||
|
"keywords": ["nosql", "redis", "predis"],
|
||||||
|
"homepage": "http://github.com/predis/predis",
|
||||||
|
"license": "MIT",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/predis/predis/issues"
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Till Krüss",
|
||||||
|
"homepage": "https://till.im",
|
||||||
|
"role": "Maintainer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tillkruss"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.3",
|
||||||
|
"phpstan/phpstan": "^1.9",
|
||||||
|
"phpunit/phpunit": "^8.0 || ^9.4",
|
||||||
|
"phpunit/phpcov": "^6.0 || ^8.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"phpstan": "phpstan analyse",
|
||||||
|
"style": "php-cs-fixer fix --diff --dry-run",
|
||||||
|
"style:fix": "php-cs-fixer fix"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Predis\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"sort-packages": true,
|
||||||
|
"preferred-install": "dist"
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"prefer-stable": true
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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
@@ -0,0 +1,612 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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.4.1';
|
||||||
|
|
||||||
|
/** @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|null $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|null $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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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'];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,396 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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;
|
||||||
|
use Predis\Command\Redis\HGETEX;
|
||||||
|
use Predis\Command\Redis\HSETEX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 bitfield_ro(string $key, ?array $encodingOffsetMap = null)
|
||||||
|
* @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 ft_list()
|
||||||
|
* @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 hexpire(string $key, int $seconds, array $fields, string $flag = null)
|
||||||
|
* @method $this hexpireat(string $key, int $unixTimeSeconds, array $fields, string $flag = null)
|
||||||
|
* @method $this hexpiretime(string $key, array $fields)
|
||||||
|
* @method $this hpersist(string $key, array $fields)
|
||||||
|
* @method $this hpexpire(string $key, int $milliseconds, array $fields, string $flag = null)
|
||||||
|
* @method $this hpexpireat(string $key, int $unixTimeMilliseconds, array $fields, string $flag = null)
|
||||||
|
* @method $this hpexpiretime(string $key, array $fields)
|
||||||
|
* @method $this hget($key, $field)
|
||||||
|
* @method $this hgetex(string $key, array $fields, string $modifier = HGETEX::NULL)
|
||||||
|
* @method $this hgetall($key)
|
||||||
|
* @method $this hgetdel(string $key, array $fields)
|
||||||
|
* @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 hsetex(string $key, array $fieldValueMap, string $setModifier = HSETEX::SET_NULL, string $ttlModifier = HSETEX::TTL_NULL, int|bool $ttlModifierValue = false)
|
||||||
|
* @method $this hsetnx($key, $field, $value)
|
||||||
|
* @method $this httl(string $key, array $fields)
|
||||||
|
* @method $this hpttl(string $key, array $fields)
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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
|
||||||
|
{
|
||||||
|
}
|
||||||
+450
@@ -0,0 +1,450 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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\Command\Redis\HGETEX;
|
||||||
|
use Predis\Command\Redis\HSETEX;
|
||||||
|
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 array|null bitfield_ro(string $key, ?array $encodingOffsetMap = null)
|
||||||
|
* @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 ft_list()
|
||||||
|
* @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|null 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 array|null hexpire(string $key, int $seconds, array $fields, string $flag = null)
|
||||||
|
* @method array|null hexpireat(string $key, int $unixTimeSeconds, array $fields, string $flag = null)
|
||||||
|
* @method array|null hexpiretime(string $key, array $fields)
|
||||||
|
* @method array|null hpersist(string $key, array $fields)
|
||||||
|
* @method array|null hpexpire(string $key, int $milliseconds, array $fields, string $flag = null)
|
||||||
|
* @method array|null hpexpireat(string $key, int $unixTimeMilliseconds, array $fields, string $flag = null)
|
||||||
|
* @method array|null hpexpiretime(string $key, array $fields)
|
||||||
|
* @method string|null hget(string $key, string $field)
|
||||||
|
* @method array|null hgetex(string $key, array $fields, string $modifier = HGETEX::NULL, int|bool $modifierValue = false)
|
||||||
|
* @method array hgetall(string $key)
|
||||||
|
* @method array hgetdel(string $key, array $fields)
|
||||||
|
* @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 hsetex(string $key, array $fieldValueMap, string $setModifier = HSETEX::SET_NULL, string $ttlModifier = HSETEX::TTL_NULL, int|bool $ttlModifierValue = false)
|
||||||
|
* @method int hsetnx(string $key, string $field, string $value)
|
||||||
|
* @method array|null httl(string $key, array $fields)
|
||||||
|
* @method array|null hpttl(string $key, array $fields)
|
||||||
|
* @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 array|null xread(int $count = null, int $block = null, array $streams = null, string ...$id)
|
||||||
|
* @method int zadd(string $key, array $membersAndScoresDictionary)
|
||||||
|
* @method int zcard(string $key)
|
||||||
|
* @method int 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[]|string $keyOrKeys)
|
||||||
|
* @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
|
||||||
|
*
|
||||||
|
* @no-named-arguments
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,495 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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'],
|
||||||
|
'EVAL_RO' => [$this, 'getKeyFromScriptingCommands'],
|
||||||
|
'EVALSHA_RO' => [$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-2025 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-2025 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
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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-2025 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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-2025 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the gap between slot ranges.
|
||||||
|
*/
|
||||||
|
class NullSlotRange extends SlotRange
|
||||||
|
{
|
||||||
|
public function __construct(int $start, int $end)
|
||||||
|
{
|
||||||
|
parent::__construct($start, $end, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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|null $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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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|null $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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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 SimpleSlotMap 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+417
@@ -0,0 +1,417 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compact slot map for redis-cluster.
|
||||||
|
*/
|
||||||
|
class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Slot ranges list.
|
||||||
|
*
|
||||||
|
* @var SlotRange[]
|
||||||
|
*/
|
||||||
|
private $slotRanges = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given slot is valid.
|
||||||
|
*
|
||||||
|
* @param int $slot Slot index.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isValid($slot)
|
||||||
|
{
|
||||||
|
return $slot >= 0 && $slot <= SlotRange::MAX_SLOTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 SlotRange::isValidRange($first, $last);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the slot map.
|
||||||
|
*/
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
$this->slotRanges = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the slot map is empty.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEmpty()
|
||||||
|
{
|
||||||
|
return empty($this->slotRanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 array_reduce(
|
||||||
|
$this->slotRanges,
|
||||||
|
function ($carry, $slotRange) {
|
||||||
|
return $carry + $slotRange->toArray();
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of unique nodes in the slot map.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getNodes()
|
||||||
|
{
|
||||||
|
return array_unique(array_map(
|
||||||
|
function ($slotRange) {
|
||||||
|
return $slotRange->getConnection();
|
||||||
|
},
|
||||||
|
$this->slotRanges
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of slot ranges.
|
||||||
|
*
|
||||||
|
* @return SlotRange[]
|
||||||
|
*/
|
||||||
|
public function getSlotRanges()
|
||||||
|
{
|
||||||
|
return $this->slotRanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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`");
|
||||||
|
}
|
||||||
|
|
||||||
|
$targetSlotRange = new SlotRange($first, $last, (string) $connection);
|
||||||
|
|
||||||
|
// Get gaps of slot ranges list.
|
||||||
|
$gaps = $this->getGaps($this->slotRanges);
|
||||||
|
|
||||||
|
$results = $this->slotRanges;
|
||||||
|
|
||||||
|
foreach ($gaps as $gap) {
|
||||||
|
if (!$gap->hasIntersectionWith($targetSlotRange)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get intersection of the gap and target slot range.
|
||||||
|
$results[] = new SlotRange(
|
||||||
|
max($gap->getStart(), $targetSlotRange->getStart()),
|
||||||
|
min($gap->getEnd(), $targetSlotRange->getEnd()),
|
||||||
|
$targetSlotRange->getConnection()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->sortSlotRanges($results);
|
||||||
|
|
||||||
|
$results = $this->compactSlotRanges($results);
|
||||||
|
|
||||||
|
$this->slotRanges = $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the specified slot range.
|
||||||
|
*
|
||||||
|
* @param int $first Initial slot of the range.
|
||||||
|
* @param int $last Last slot of the range.
|
||||||
|
*
|
||||||
|
* @return array<int, string>
|
||||||
|
*/
|
||||||
|
public function getSlots($first, $last)
|
||||||
|
{
|
||||||
|
if (!static::isValidRange($first, $last)) {
|
||||||
|
throw new OutOfBoundsException("Invalid slot range $first-$last");
|
||||||
|
}
|
||||||
|
|
||||||
|
$placeHolder = new NullSlotRange($first, $last);
|
||||||
|
|
||||||
|
$intersections = [];
|
||||||
|
foreach ($this->slotRanges as $slotRange) {
|
||||||
|
if (!$placeHolder->hasIntersectionWith($slotRange)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$intersections[] = new SlotRange(
|
||||||
|
max($placeHolder->getStart(), $slotRange->getStart()),
|
||||||
|
min($placeHolder->getEnd(), $slotRange->getEnd()),
|
||||||
|
$slotRange->getConnection()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_reduce(
|
||||||
|
$intersections,
|
||||||
|
function ($carry, $slotRange) {
|
||||||
|
return $carry + $slotRange->toArray();
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specified slot is assigned.
|
||||||
|
*
|
||||||
|
* @param int $slot Slot index.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetExists($slot)
|
||||||
|
{
|
||||||
|
return $this->findRangeBySlot($slot) !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the node assigned to the specified slot.
|
||||||
|
*
|
||||||
|
* @param int $slot Slot index.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetGet($slot)
|
||||||
|
{
|
||||||
|
$found = $this->findRangeBySlot($slot);
|
||||||
|
|
||||||
|
return $found ? $found->getConnection() : 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->offsetUnset($slot);
|
||||||
|
$this->setSlots($slot, $slot, $connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the node assigned to the specified slot.
|
||||||
|
*
|
||||||
|
* @param int $slot Slot index.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function offsetUnset($slot)
|
||||||
|
{
|
||||||
|
if (!static::isValid($slot)) {
|
||||||
|
throw new OutOfBoundsException("Invalid slot $slot");
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
foreach ($this->slotRanges as $slotRange) {
|
||||||
|
if (!$slotRange->hasSlot($slot)) {
|
||||||
|
$results[] = $slotRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static::isValidRange($slotRange->getStart(), $slot - 1)) {
|
||||||
|
$results[] = new SlotRange($slotRange->getStart(), $slot - 1, $slotRange->getConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static::isValidRange($slot + 1, $slotRange->getEnd())) {
|
||||||
|
$results[] = new SlotRange($slot + 1, $slotRange->getEnd(), $slotRange->getConnection());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->slotRanges = $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current number of assigned slots.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
return array_sum(array_map(
|
||||||
|
function ($slotRange) {
|
||||||
|
return $slotRange->count();
|
||||||
|
},
|
||||||
|
$this->slotRanges
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator over the slot map.
|
||||||
|
*
|
||||||
|
* @return Traversable<int, string>
|
||||||
|
*/
|
||||||
|
#[ReturnTypeWillChange]
|
||||||
|
public function getIterator()
|
||||||
|
{
|
||||||
|
return new ArrayIterator($this->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the slot range which contains the specific slot index.
|
||||||
|
*
|
||||||
|
* @param int $slot Slot index.
|
||||||
|
*
|
||||||
|
* @return SlotRange|false The slot range object or false if not found.
|
||||||
|
*/
|
||||||
|
protected function findRangeBySlot(int $slot)
|
||||||
|
{
|
||||||
|
foreach ($this->slotRanges as $slotRange) {
|
||||||
|
if ($slotRange->hasSlot($slot)) {
|
||||||
|
return $slotRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get gaps between sorted slot ranges with NullSlotRange object.
|
||||||
|
*
|
||||||
|
* @param SlotRange[] $slotRanges
|
||||||
|
*
|
||||||
|
* @return SlotRange[]
|
||||||
|
*/
|
||||||
|
protected function getGaps(array $slotRanges)
|
||||||
|
{
|
||||||
|
if (empty($slotRanges)) {
|
||||||
|
return [
|
||||||
|
new NullSlotRange(0, SlotRange::MAX_SLOTS),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$gaps = [];
|
||||||
|
$count = count($slotRanges);
|
||||||
|
$i = 0;
|
||||||
|
foreach ($slotRanges as $key => $slotRange) {
|
||||||
|
$start = $slotRange->getStart();
|
||||||
|
$end = $slotRange->getEnd();
|
||||||
|
if (static::isValidRange($i, $start - 1)) {
|
||||||
|
$gaps[] = new NullSlotRange($i, $start - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$i = $end + 1;
|
||||||
|
|
||||||
|
if ($key === $count - 1) {
|
||||||
|
if (static::isValidRange($i, SlotRange::MAX_SLOTS)) {
|
||||||
|
$gaps[] = new NullSlotRange($i, SlotRange::MAX_SLOTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $gaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort slot ranges by start index.
|
||||||
|
*
|
||||||
|
* @param SlotRange[] $slotRanges
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function sortSlotRanges(array &$slotRanges)
|
||||||
|
{
|
||||||
|
usort(
|
||||||
|
$slotRanges,
|
||||||
|
function (SlotRange $a, SlotRange $b) {
|
||||||
|
if ($a->getStart() == $b->getStart()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a->getStart() < $b->getStart() ? -1 : 1;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compact adjacent slot ranges with the same connection.
|
||||||
|
*
|
||||||
|
* @param SlotRange[] $slotRanges
|
||||||
|
*
|
||||||
|
* @return SlotRange[]
|
||||||
|
*/
|
||||||
|
protected function compactSlotRanges(array $slotRanges)
|
||||||
|
{
|
||||||
|
if (empty($slotRanges)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$compacted = [];
|
||||||
|
$count = count($slotRanges);
|
||||||
|
$i = 0;
|
||||||
|
$carry = $slotRanges[0];
|
||||||
|
while ($i < $count) {
|
||||||
|
$next = $slotRanges[$i + 1] ?? null;
|
||||||
|
if (
|
||||||
|
!is_null($next)
|
||||||
|
&& ($carry->getEnd() + 1) === $next->getStart()
|
||||||
|
&& $carry->getConnection() === $next->getConnection()
|
||||||
|
) {
|
||||||
|
$carry = new SlotRange($carry->getStart(), $next->getEnd(), $carry->getConnection());
|
||||||
|
} else {
|
||||||
|
$compacted[] = $carry;
|
||||||
|
$carry = $next;
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values($compacted);
|
||||||
|
}
|
||||||
|
}
|
||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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 Countable;
|
||||||
|
use OutOfBoundsException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a range of slots in a Redis cluster.
|
||||||
|
*/
|
||||||
|
class SlotRange implements Countable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Maximum number of slots in a Redis cluster is 16384.
|
||||||
|
*/
|
||||||
|
public const MAX_SLOTS = 0x3FFF;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starting slot of the range.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $start;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ending slot of the range.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $end;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connection to the server hosting this slot range.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $connection;
|
||||||
|
|
||||||
|
public function __construct(int $start, int $end, string $connection)
|
||||||
|
{
|
||||||
|
if (!static::isValidRange($start, $end)) {
|
||||||
|
throw new OutOfBoundsException("Invalid slot range $start-$end for `$connection`");
|
||||||
|
}
|
||||||
|
$this->start = $start;
|
||||||
|
$this->end = $end;
|
||||||
|
$this->connection = $connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a slot range is valid.
|
||||||
|
*
|
||||||
|
* @param int $first
|
||||||
|
* @param int $last
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isValidRange($first, $last)
|
||||||
|
{
|
||||||
|
return $first >= 0 && $first <= self::MAX_SLOTS && $last >= 0x0000 && $last <= self::MAX_SLOTS && $first <= $last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the start slot index of this range.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getStart()
|
||||||
|
{
|
||||||
|
return $this->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the end slot index of this range.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getEnd()
|
||||||
|
{
|
||||||
|
return $this->end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the connection to the server hosting this slot range.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getConnection()
|
||||||
|
{
|
||||||
|
return $this->connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specific slot is contained in this range.
|
||||||
|
*
|
||||||
|
* @param int $slot
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasSlot(int $slot)
|
||||||
|
{
|
||||||
|
return $this->start <= $slot && $this->end >= $slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of connection strings for each slot in this range.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
return array_fill($this->start, $this->end - $this->start + 1, $this->connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of slots in this range.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return $this->end - $this->start + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this range has an intersection with the given slot range.
|
||||||
|
*
|
||||||
|
* @param SlotRange $slotRange
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasIntersectionWith(SlotRange $slotRange): bool
|
||||||
|
{
|
||||||
|
return $this->start <= $slotRange->getEnd() && $this->end >= $slotRange->getStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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-2025 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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
|
||||||
|
* @param bool $allowsMissing
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function setCommonOptions(
|
||||||
|
string $fieldType,
|
||||||
|
string $identifier,
|
||||||
|
string $alias = '',
|
||||||
|
$sortable = self::NOT_SORTABLE,
|
||||||
|
bool $noIndex = false,
|
||||||
|
bool $allowsMissing = 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';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($allowsMissing) {
|
||||||
|
$this->fieldArguments[] = 'INDEXMISSING';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
return $this->fieldArguments;
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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
|
||||||
|
{
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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
|
||||||
|
* @param bool $allowsMissing
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
string $identifier,
|
||||||
|
string $alias = '',
|
||||||
|
$sortable = self::NOT_SORTABLE,
|
||||||
|
bool $noIndex = false,
|
||||||
|
bool $allowsMissing = false
|
||||||
|
) {
|
||||||
|
$this->setCommonOptions('GEO', $identifier, $alias, $sortable, $noIndex, $allowsMissing);
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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 GeoShapeField extends AbstractField
|
||||||
|
{
|
||||||
|
public const COORD_FLAT = 'FLAT';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $identifier
|
||||||
|
* @param string $alias
|
||||||
|
* @param bool|string $sortable
|
||||||
|
* @param bool $noIndex
|
||||||
|
* @param string|null $coordSystem Constants that represents available systems available on a class level.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
string $identifier,
|
||||||
|
string $alias = '',
|
||||||
|
$sortable = self::NOT_SORTABLE,
|
||||||
|
bool $noIndex = false,
|
||||||
|
?string $coordSystem = null
|
||||||
|
) {
|
||||||
|
$this->fieldArguments[] = $identifier;
|
||||||
|
|
||||||
|
if ($alias !== '') {
|
||||||
|
$this->fieldArguments[] = 'AS';
|
||||||
|
$this->fieldArguments[] = $alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->fieldArguments[] = 'GEOSHAPE';
|
||||||
|
|
||||||
|
if (null !== $coordSystem) {
|
||||||
|
$this->fieldArguments[] = $coordSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sortable === self::SORTABLE) {
|
||||||
|
$this->fieldArguments[] = 'SORTABLE';
|
||||||
|
} elseif ($sortable === self::SORTABLE_UNF) {
|
||||||
|
$this->fieldArguments[] = 'SORTABLE';
|
||||||
|
$this->fieldArguments[] = 'UNF';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($noIndex) {
|
||||||
|
$this->fieldArguments[] = 'NOINDEX';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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
|
||||||
|
* @param bool $allowsMissing
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
string $identifier,
|
||||||
|
string $alias = '',
|
||||||
|
$sortable = self::NOT_SORTABLE,
|
||||||
|
bool $noIndex = false,
|
||||||
|
bool $allowsMissing = false
|
||||||
|
) {
|
||||||
|
$this->setCommonOptions('NUMERIC', $identifier, $alias, $sortable, $noIndex, $allowsMissing);
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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
|
||||||
|
* @param bool $allowsEmpty
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
string $identifier,
|
||||||
|
string $alias = '',
|
||||||
|
$sortable = self::NOT_SORTABLE,
|
||||||
|
bool $noIndex = false,
|
||||||
|
string $separator = ',',
|
||||||
|
bool $caseSensitive = false,
|
||||||
|
bool $allowsEmpty = false,
|
||||||
|
bool $allowsMissing = false
|
||||||
|
) {
|
||||||
|
$this->setCommonOptions('TAG', $identifier, $alias, $sortable, $noIndex, $allowsMissing);
|
||||||
|
|
||||||
|
if ($separator !== ',') {
|
||||||
|
$this->fieldArguments[] = 'SEPARATOR';
|
||||||
|
$this->fieldArguments[] = $separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($caseSensitive) {
|
||||||
|
$this->fieldArguments[] = 'CASESENSITIVE';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($allowsEmpty) {
|
||||||
|
$this->fieldArguments[] = 'INDEXEMPTY';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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
|
||||||
|
* @param bool $allowsEmpty
|
||||||
|
* @param bool $allowsMissing
|
||||||
|
*/
|
||||||
|
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,
|
||||||
|
bool $allowsEmpty = false,
|
||||||
|
bool $allowsMissing = false
|
||||||
|
) {
|
||||||
|
$this->setCommonOptions('TEXT', $identifier, $alias, $sortable, $noIndex, $allowsMissing);
|
||||||
|
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($allowsEmpty) {
|
||||||
|
$this->fieldArguments[] = 'INDEXEMPTY';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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-2025 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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-2025 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-2025 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
|
||||||
|
{
|
||||||
|
}
|
||||||
+162
@@ -0,0 +1,162 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Predis package.
|
||||||
|
*
|
||||||
|
* (c) 2009-2020 Daniele Alessandri
|
||||||
|
* (c) 2021-2025 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;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignore samples with given time or value difference.
|
||||||
|
*
|
||||||
|
* @param int $maxTimeDiff Non-negative integer value in milliseconds
|
||||||
|
* @param float $maxValDiff Non-negative float value
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function ignore(int $maxTimeDiff, float $maxValDiff): self
|
||||||
|
{
|
||||||
|
if ($maxTimeDiff < 0 || $maxValDiff < 0) {
|
||||||
|
throw new UnexpectedValueException('Ignore does not accept negative values');
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push($this->arguments, 'IGNORE', $maxTimeDiff, $maxValDiff);
|
||||||
|
|
||||||
|
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-2025 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-2025 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-2025 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-2025 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user