Completed
Push — master ( 8457cc...195f4c )
by Mathieu
8s
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($closure = $this->releaseClosure());
36
        pcntl_signal(SIGINT, $closure);
37
    }
38
39
    public function acquire()
40
    {
41
        $log_data = ["identifier" => $this->identifier];
42
        $response = $this->client->set($this->identifier, $this->id, "PX", $this->ttl, "NX");
43
        if (!$response instanceof Status || $response->getPayload() !== "OK") {
44
            $this->logger->debug("could not acquire lock on {identifier}", $log_data);
45
46
            throw new Exception("Could not acquire lock on " . $this->identifier);
47
        }
48
        $this->logger->debug("lock acquired on {identifier}", $log_data);
49
    }
50
51
    public function release()
52
    {
53
        $closure = $this->releaseClosure();
54
        $closure();
55
    }
56
57
    public function __destruct()
58
    {
59
        $this->release();
60
    }
61
62
    private function releaseClosure()
63
    {
64
        $client = $this->client;
65
        $id = $this->id;
66
        $identifier = $this->identifier;
67
        $logger = $this->logger;
68
69
        $closure = function () use ($client, $identifier, $id, $logger) {
70
            $script = <<<LUA
71
    if redis.call("get", KEYS[1]) == ARGV[1] then
72
        return redis.call("del", KEYS[1])
73
    end
74
LUA;
75
            if ($client->eval($script, 1, $identifier, $id)) {
76
                $logger->debug("lock released on {identifier}", ["identifier" => $identifier]);
77
            }
78
        };
79
        return $closure->bindTo(null);
80
    }
81
}
82