9use Psr\Log\LoggerAwareInterface;
10use Psr\Log\LoggerInterface;
43 private const AUTH_NO_ERROR = 200;
48 private const AUTH_ERROR_TEMPORARY = 201;
53 private const AUTH_ERROR_PERMANENT = 202;
86 return $this->lastError;
90 $this->lastError =
null;
101 public function __call( $name, $arguments ) {
103 $lname = strtolower( $name );
105 ( $lname ===
'blpop' || $lname ===
'brpop' || $lname ===
'brpoplpush' )
106 && count( $arguments ) > 1
109 $timeout = end( $arguments );
111 $this->pool->resetTimeout( $this->conn, ( $timeout > 0 ? $timeout + 1 : $timeout ) );
114 return $this->tryCall( $name, $arguments );
125 private function tryCall( $method, $arguments ) {
126 $this->conn->clearLastError();
128 $res = $this->conn->$method( ...$arguments );
129 $authError = $this->checkAuthentication();
130 if ( $authError === self::AUTH_ERROR_TEMPORARY ) {
131 $res = $this->conn->$method( ...$arguments );
133 if ( $authError === self::AUTH_ERROR_PERMANENT ) {
134 throw new RedisException(
"Failure reauthenticating to Redis." );
138 $this->postCallCleanup();
152 public function scan( &$iterator, $pattern =
null, $count =
null ) {
153 return $this->tryCall(
'scan', [ &$iterator, $pattern, $count ] );
167 public function sScan( $key, &$iterator, $pattern =
null, $count =
null ) {
168 return $this->tryCall(
'sScan', [ $key, &$iterator, $pattern, $count ] );
182 public function hScan( $key, &$iterator, $pattern =
null, $count =
null ) {
183 return $this->tryCall(
'hScan', [ $key, &$iterator, $pattern, $count ] );
197 public function zScan( $key, &$iterator, $pattern =
null, $count =
null ) {
198 return $this->tryCall(
'zScan', [ $key, &$iterator, $pattern, $count ] );
206 private function checkAuthentication() {
207 $lastError = $this->conn->getLastError();
208 if ( $lastError && preg_match(
'/^ERR operation not permitted\b/', $lastError ) ) {
209 if ( !$this->pool->reauthenticateConnection( $this->server, $this->conn ) ) {
210 return self::AUTH_ERROR_PERMANENT;
212 $this->conn->clearLastError();
214 "Used automatic re-authentication for Redis.",
215 [
'redis_server' => $this->server ]
217 return self::AUTH_ERROR_TEMPORARY;
219 return self::AUTH_NO_ERROR;
227 private function postCallCleanup() {
228 $this->lastError = $this->conn->getLastError() ?: $this->lastError;
231 $this->pool->resetTimeout( $this->conn );
241 public function luaEval( $script, array $params, $numKeys ) {
242 $sha1 = sha1( $script );
244 $server = $this->server;
247 $conn->clearLastError();
248 $res = $conn->evalSha( $sha1, $params, $numKeys );
253 $lastError = $conn->getLastError();
254 if ( $lastError && preg_match(
'/^ERR operation not permitted\b/', $lastError ) ) {
255 $this->pool->reauthenticateConnection( $server, $conn );
256 $conn->clearLastError();
257 $res = $conn->eval( $script, $params, $numKeys );
259 "Used automatic re-authentication for Lua script '$sha1'.",
260 [
'redis_server' => $server ]
264 $lastError = $conn->getLastError();
265 if ( $lastError && preg_match(
'/^NOSCRIPT/', $lastError ) ) {
266 $conn->clearLastError();
267 $res = $conn->eval( $script, $params, $numKeys );
269 "Used eval() for Lua script '$sha1'.",
270 [
'redis_server' => $server ]
274 $lastError = $conn->getLastError();
276 $this->logger->error(
277 'Lua script error on server "{redis_server}": {lua_error}',
279 'redis_server' => $server,
280 'lua_error' => $lastError
283 $this->lastError = $lastError;
294 return $this->conn === $conn;
298 $this->pool->freeConnection( $this->server, $this->conn );
303class_alias( RedisConnRef::class,
'RedisConnRef' );