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:
2026-08-18 16:21:27 +02:00
parent db4b75c5e6
commit 5d94572569
585 changed files with 39582 additions and 0 deletions
+15
View File
@@ -14,3 +14,18 @@ DB_PORT=3306
# ElevenLabs Sound Effects API — nur für generate-sounds.py (Dev-Zeit)
# 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)
+18
View File
@@ -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."
}
}
}
}
+149
View File
@@ -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"
}
+18
View File
@@ -38,6 +38,13 @@ if (strpos($host, 'geograsim.at') !== false) {
}
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
// dauern. Schützt DB vor absurden Werten (Tab über Nacht offen, Bug im
// 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_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:
* - Lehrkraft eingeloggt → /teacher
+58
View File
@@ -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, …
}
}
}
+82
View File
@@ -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;
}
}
}
+22
View File
@@ -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();
+579
View File
@@ -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
View File
@@ -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;
}
}
+21
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);
+11
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
+137
View File
@@ -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": []
}
+41
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+30
View File
@@ -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
View File
@@ -0,0 +1,425 @@
![Build Status](https://github.com/firebase/php-jwt/actions/workflows/tests.yml/badge.svg)
[![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt)
[![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt)
[![License](https://poser.pugx.org/firebase/php-jwt/license)](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
View File
@@ -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
View File
@@ -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;
}
}
}
+18
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
{
}
+22
View File
@@ -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.
+510
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
+42
View File
@@ -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'];
}
}
+396
View File
@@ -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);
}
+20
View File
@@ -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
View File
@@ -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);
}
+495
View File
@@ -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;
}
}
+73
View File
@@ -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);
}
}
+40
View File
@@ -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;
}
}
+77
View File
@@ -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;
}
}
+55
View File
@@ -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");
}
}
+209
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
@@ -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;
}
}
@@ -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
{
}
@@ -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);
}
}
@@ -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';
}
}
}
@@ -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);
}
}
@@ -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';
}
}
}
@@ -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';
}
}
}
@@ -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
{
}
@@ -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