Test Failed
Push — master ( 10b2f4...461d1e )
by Rustam
02:18
created

PgsqlMutex::__destruct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 4
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Mutex;
6
7
use PDO;
8
use RuntimeException;
9
10
/**
11
 * PgsqlMutex implements mutex "lock" mechanism via PgSQL locks.
12
 */
13
final class PgsqlMutex implements MutexInterface
14
{
15
    use RetryAcquireTrait;
16
17
    private string $name;
18
    private PDO $connection;
19
    private bool $released = false;
20
21
    /**
22
     * @param string $name Mutex name.
23
     * @param PDO $connection PDO connection instance to use.
24
     */
25
    public function __construct(string $name, PDO $connection)
26
    {
27
        $this->name = $name;
28
        $this->connection = $connection;
29
        $driverName = $connection->getAttribute(PDO::ATTR_DRIVER_NAME);
30
        if ($driverName !== 'pgsql') {
31
            throw new \InvalidArgumentException(
32
                'Connection must be configured to use PgSQL database. Got ' . $driverName . '.'
33
            );
34
        }
35
    }
36
37
    public function __destruct()
38
    {
39
        if (!$this->released) {
40
            $this->release();
41
        }
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     *
47
     * @see http://www.postgresql.org/docs/9.0/static/functions-admin.html
48
     */
49
    public function acquire(int $timeout = 0): bool
50
    {
51
        [$key1, $key2] = $this->getKeysFromName($this->name);
52
53
        return $this->retryAcquire($timeout, function () use ($key1, $key2) {
54
            $statement = $this->connection->prepare('SELECT pg_try_advisory_lock(:key1, :key2)');
55
            $statement->bindValue(':key1', $key1);
56
            $statement->bindValue(':key2', $key2);
57
            $statement->execute();
58
59
            if ($statement->fetchColumn()) {
60
                $this->released = false;
61
                return true;
62
            }
63
64
            return false;
65
        });
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     *
71
     * @see http://www.postgresql.org/docs/9.0/static/functions-admin.html
72
     */
73
    public function release(): void
74
    {
75
        [$key1, $key2] = $this->getKeysFromName($this->name);
76
77
        $statement = $this->connection->prepare('SELECT pg_advisory_unlock(:key1, :key2)');
78
        $statement->bindValue(':key1', $key1);
79
        $statement->bindValue(':key2', $key2);
80
        $statement->execute();
81
82
        if (!$statement->fetchColumn()) {
83
            throw new RuntimeException("Unable to release lock \"$this->name\".");
84
        }
85
86
        $this->released = true;
87
    }
88
89
    /**
90
     * Converts a string into two 16 bit integer keys using the SHA1 hash function.
91
     *
92
     * @param string $name
93
     *
94
     * @return array contains two 16 bit integer keys
95
     */
96
    private function getKeysFromName(string $name): array
97
    {
98
        return array_values(unpack('n2', sha1($name, true)));
99
    }
100
}
101