GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

SingleStoreLocker::unlock()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 9.6666
cc 3
eloc 4
nc 3
nop 1
crap 3
1
<?php
2
3
namespace RemiSan\Lock\Locker;
4
5
use Psr\Log\LoggerAwareInterface;
6
use Psr\Log\LoggerAwareTrait;
7
use Psr\Log\NullLogger;
8
use RemiSan\Lock\Exceptions\LockingException;
9
use RemiSan\Lock\Exceptions\UnlockingException;
10
use RemiSan\Lock\Lock;
11
use RemiSan\Lock\Locker;
12
use RemiSan\Lock\LockStore;
13
use RemiSan\Lock\TokenGenerator;
14
use Symfony\Component\Stopwatch\Stopwatch;
15
16
final class SingleStoreLocker implements Locker, LoggerAwareInterface
17
{
18
    use LoggerAwareTrait;
19
20
    /** @var LockStore */
21
    private $store;
22
23
    /** @var TokenGenerator */
24
    private $tokenGenerator;
25
26
    /** @var Stopwatch */
27
    private $stopwatch;
28
29
    /**
30
     * SingleStoreLocker constructor.
31
     *
32
     * @param LockStore      $store          The persisting store for the locks
33
     * @param TokenGenerator $tokenGenerator The token generator
34
     * @param Stopwatch      $stopwatch      A way to measure time passed
35
     */
36 21
    public function __construct(
37
        $store,
38
        TokenGenerator $tokenGenerator,
39
        Stopwatch $stopwatch
40
    ) {
41 21
        $this->store = $store;
42 21
        $this->tokenGenerator = $tokenGenerator;
43 21
        $this->stopwatch = $stopwatch;
44
45 21
        $this->logger = new NullLogger();
46 21
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51 9
    public function lock($resource, $ttl = null, $retryDelay = 0, $retryCount = 0)
52
    {
53 9
        $lock = new Lock((string) $resource, $this->tokenGenerator->generateToken());
54
55 9
        $tried = 0;
56 9
        while (true) {
57
            try {
58 9
                return $this->lockAndCheckQuorumAndTtl($lock, $ttl);
59 3
            } catch (LockingException $e) {
60 3
                $this->logger->notice($e->getMessage(), ['resource' => $lock->getResource()]);
61
            }
62
63 3
            if ($tried++ === $retryCount) {
64 3
                break;
65
            }
66
67 3
            $this->waitBeforeRetrying($retryDelay);
68 2
        }
69
70 3
        throw new LockingException('Failed locking the resource.');
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 3
    public function isLocked($resource)
77
    {
78 3
        return $this->store->exists((string) $resource);
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84 9
    public function unlock(Lock $lock)
85
    {
86 9
        if (!$this->store->delete($lock)) {
87 6
            if ($this->store->exists($lock->getResource())) {
88
                // Only throw an exception if the lock is still present
89 3
                throw new UnlockingException('Failed releasing the lock.');
90
            }
91 2
        }
92 6
    }
93
94
    /**
95
     * Try locking resource on store.
96
     *
97
     * Measure the time to do it and reject if time to lock on store have exceeded the ttl.
98
     *
99
     * @param Lock $lock The lock instance
100
     * @param int  $ttl  Time to live in milliseconds
101
     *
102
     * @throws LockingException
103
     *
104
     * @return Lock
105
     */
106 9
    private function lockAndCheckQuorumAndTtl(Lock $lock, $ttl)
107
    {
108 9
        $timeMeasure = $this->stopwatch->start($lock->getToken());
109 9
        $this->store->set($lock, $ttl);
110 9
        $timeMeasure->stop();
111
112 9
        if ($ttl) {
113 6
            $this->checkTtl($timeMeasure->getDuration(), $ttl);
114 3
            $lock->setValidityEndTime($timeMeasure->getOrigin() + $ttl);
115 2
        }
116
117 6
        return $lock;
118
    }
119
120
    /**
121
     * Make the script wait before retrying to lock.
122
     *
123
     * @param int $retryDelay The retry delay in milliseconds
124
     */
125 3
    private function waitBeforeRetrying($retryDelay)
126
    {
127 3
        usleep($retryDelay * 1000);
128 3
    }
129
130
    /**
131
     * Checks if the elapsed time is inferior to the ttl.
132
     *
133
     * To the elapsed time is added a drift time to have a margin of error.
134
     * If this adjusted time is greater than the ttl, it will throw a LockingException.
135
     *
136
     * @param int $elapsedTime The time elapsed in milliseconds
137
     * @param int $ttl         The time to live in milliseconds
138
     *
139
     * @throws LockingException
140
     */
141 6
    private function checkTtl($elapsedTime, $ttl)
142
    {
143 6
        $adjustedElapsedTime = $elapsedTime + $this->store->getDrift($ttl);
144
145 6
        if ($adjustedElapsedTime >= $ttl) {
146 3
            throw new LockingException('Time to lock the resource has exceeded the ttl.');
147
        }
148 3
    }
149
}
150