OracleMutex   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 59
dl 0
loc 118
ccs 40
cts 40
cp 1
rs 10
c 0
b 0
f 0
wmc 8

3 Methods

Rating   Name   Duplication   Size   Complexity  
A acquireLock() 0 28 3
A releaseLock() 0 18 2
A __construct() 0 28 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Mutex\Oracle;
6
7
use InvalidArgumentException;
8
use PDO;
9
use Yiisoft\Mutex\Mutex;
10
11
use function implode;
12
use function in_array;
13
use function sprintf;
14
15
/**
16
 * OracleMutex implements mutex "lock" mechanism via Oracle locks.
17
 */
18
final class OracleMutex extends Mutex
19
{
20
    /** available lock modes */
21
    public const MODE_X = 'X_MODE';
22
    public const MODE_NL = 'NL_MODE';
23
    public const MODE_S = 'S_MODE';
24
    public const MODE_SX = 'SX_MODE';
25
    public const MODE_SS = 'SS_MODE';
26
    public const MODE_SSX = 'SSX_MODE';
27
28
    private const MODES = [
29
        self::MODE_X,
30
        self::MODE_NL,
31
        self::MODE_S,
32
        self::MODE_SX,
33
        self::MODE_SS,
34
        self::MODE_SSX,
35
    ];
36
37
    private string $lockName;
38
    private PDO $connection;
39
    private string $lockMode;
40
    private bool $releaseOnCommit;
41
42
    /**
43
     * @param string $name Mutex name.
44
     * @param PDO $connection PDO connection instance to use.
45
     * @param string $lockMode Lock mode to be used {@see https://docs.oracle.com/en/database/oracle/oracle-database/21/arpls/DBMS_LOCK.html#GUID-8F868C41-CEA3-48E2-8701-3C0F8D2B308C}.
46
     * @param bool $releaseOnCommit Whether to release lock on commit.
47
     */
48 9
    public function __construct(
49
        string $name,
50
        PDO $connection,
51
        string $lockMode = self::MODE_X,
52
        bool $releaseOnCommit = false
53
    ) {
54 9
        if (!in_array($lockMode, self::MODES, true)) {
55 1
            throw new InvalidArgumentException(sprintf(
56 1
                '"%s" is not valid lock mode for "%s". It must be one of the values of: "%s".',
57 1
                $lockMode,
58 1
                self::class,
59 1
                implode('", "', self::MODES),
60 1
            ));
61
        }
62
63 8
        $this->lockName = $name;
64 8
        $this->connection = $connection;
65 8
        $this->lockMode = $lockMode;
66 8
        $this->releaseOnCommit = $releaseOnCommit;
67
68
        /** @var string $driverName */
69 8
        $driverName = $connection->getAttribute(PDO::ATTR_DRIVER_NAME);
70
71 8
        if (!in_array($driverName, ['oci', 'obdb'], true)) {
72 1
            throw new InvalidArgumentException("Oracle connection instance should be passed. Got \"$driverName\".");
73
        }
74
75 7
        parent::__construct(self::class, $name);
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     *
81
     * @see https://docs.oracle.com/en/database/oracle/oracle-database/21/arpls/DBMS_LOCK.html
82
     */
83 7
    protected function acquireLock(int $timeout = 0): bool
84
    {
85 7
        $lockStatus = 4;
86
87
        // clean vars before using
88 7
        $releaseOnCommit = $this->releaseOnCommit ? 'TRUE' : 'FALSE';
89
90
        // inside pl/sql scopes pdo binding not working correctly :(
91
92 7
        $statement = $this->connection->prepare(
93 7
            "DECLARE
94
                handle VARCHAR2(128);
95
            BEGIN
96
                DBMS_LOCK.ALLOCATE_UNIQUE(:name, handle);
97
                :lockStatus := DBMS_LOCK.REQUEST(
98
                    handle,
99 7
                    DBMS_LOCK.$this->lockMode,
100 7
                    $timeout,
101 7
                    $releaseOnCommit
102
                );
103 7
            END;"
104 7
        );
105
106 7
        $statement->bindValue(':name', $this->lockName);
107 7
        $statement->bindParam(':lockStatus', $lockStatus, PDO::PARAM_INT, 1);
108 7
        $statement->execute();
109
110 7
        return $lockStatus === 0 || $lockStatus === '0';
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     *
116
     * @see https://docs.oracle.com/en/database/oracle/oracle-database/21/arpls/DBMS_LOCK.html
117
     */
118 7
    protected function releaseLock(): bool
119
    {
120 7
        $releaseStatus = 4;
121
122 7
        $statement = $this->connection->prepare(
123 7
            'DECLARE
124
                handle VARCHAR2(128);
125
            BEGIN
126
                DBMS_LOCK.ALLOCATE_UNIQUE(:name, handle);
127
                :releaseStatus := DBMS_LOCK.RELEASE(handle);
128 7
            END;'
129 7
        );
130
131 7
        $statement->bindValue(':name', $this->lockName);
132 7
        $statement->bindParam(':releaseStatus', $releaseStatus, PDO::PARAM_INT, 1);
133 7
        $statement->execute();
134
135 7
        return $releaseStatus === 0 || $releaseStatus === '0';
136
    }
137
}
138