Completed
Pull Request — master (#2)
by Mathieu
03:27 queued 01:02
created

RedisSimpleLock::releaseClosure()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 19
rs 9.4285
cc 2
eloc 12
nc 1
nop 0
1
<?php
2
3
namespace TH\RedisLock;
4
5
use Exception;
6
use Predis\Client;
7
use Predis\Response\Status;
8
use Psr\Log\LoggerInterface;
9
use Psr\Log\NullLogger;
10
use TH\Lock\Lock;
11
12
class RedisSimpleLock implements Lock
13
{
14
    private $identifier;
15
    private $client;
16
    private $ttl;
17
    private $logger;
18
    private $id;
19
20
    /**
21
     * Create new RedisSimpleLock
22
     *
23
     * @param string               $identifier the redis lock key
24
     * @param Client               $client     the Predis client
25
     * @param integer              $ttl        lock time-to-live in milliseconds
26
     * @param LoggerInterface|null $logger
27
     */
28
    public function __construct($identifier, Client $client, $ttl = 10000, LoggerInterface $logger = null)
29
    {
30
        $this->identifier = $identifier;
31
        $this->client     = $client;
32
        $this->ttl        = $ttl;
33
        $this->logger     = $logger ?: new NullLogger;
34
        $this->id         = mt_rand();
35
        register_shutdown_function($this->releaseClosure());
36
    }
37
38
    public function acquire()
39
    {
40
        $log_data = ["identifier" => $this->identifier];
41
        $response = $this->client->set($this->identifier, $this->id, "PX", $this->ttl, "NX");
42
        if (!$response instanceof Status || $response->getPayload() !== "OK") {
43
            $this->logger->debug("could not acquire lock on {identifier}", $log_data);
44
45
            throw new Exception("Could not acquire lock on " . $this->identifier);
46
        }
47
        $this->logger->debug("lock acquired on {identifier}", $log_data);
48
    }
49
50
    public function release()
51
    {
52
        $closure = $this->releaseClosure();
53
        $closure();
54
    }
55
56
    public function __destruct()
57
    {
58
        $this->release();
59
    }
60
61
    private function releaseClosure()
62
    {
63
        $client = $this->client;
64
        $id = $this->id;
65
        $identifier = $this->identifier;
66
        $logger = $this->logger;
67
68
        $closure = function () use ($client, $identifier, $id, $logger) {
69
            $script = <<<LUA
70
    if redis.call("get", KEYS[1]) == ARGV[1] then
71
        return redis.call("del", KEYS[1])
72
    end
73
LUA;
74
            if ($this->client->eval($script, 1, $this->identifier, $this->id)) {
75
                $this->logger->debug("lock released on {identifier}", ["identifier" => $this->identifier]);
76
            }
77
        };
78
        return $closure->bindTo(null);
79
    }
80
}
81