Completed
Push — master ( c27d2d...a49ccc )
by Ondřej
03:50
created

TxConfig::setReadOnly()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace Ivory\Connection;
3
4
use Ivory\Exception\InternalException;
5
6
/**
7
 * Transaction configuration. To be used, e.g., for {@link Connection::startTransaction()}.
8
 *
9
 * There are several configurable parameters of transactions in PostgreSQL. For each of them, a group of constants,
10
 * representing the possible values for the respective parameter, is available. Constants from distinct groups may be
11
 * combined together using the binary `|` operator.
12
 */
13
class TxConfig
14
{
15
    /** The `SERIALIZABLE` isolation level. */
16
    const ISOLATION_SERIALIZABLE = 1;
17
    /** The `REPEATABLE READ` isolation level. */
18
    const ISOLATION_REPEATABLE_READ = 2;
19
    /** The `READ COMMITTED` isolation level. */
20
    const ISOLATION_READ_COMMITTED = 4;
21
    /** The `READ UNCOMMITTED` isolation level. */
22
    const ISOLATION_READ_UNCOMMITTED = 8;
23
24
    /** The `READ WRITE` access mode. */
25
    const ACCESS_READ_WRITE = 128;
26
    /** The `READ ONLY` access mode. */
27
    const ACCESS_READ_ONLY = 256;
28
29
    /** The `DEFERRABLE` deferrable mode. Only effective in `SERIALIZABLE` `READ ONLY` transactions. */
30
    const DEFERRABLE = 4096;
31
    /** The `NOT DEFERRABLE` deferrable mode. */
32
    const NOT_DEFERRABLE = 8192;
33
34
35
    private $isolationLevel = null;
36
    private $readOnly = null;
37
    private $deferrable = null;
38
39
40
    public static function create($transactionOptions)
41
    {
42
        if ($transactionOptions instanceof TxConfig) {
43
            return $transactionOptions;
44
        } else {
45
            return new TxConfig($transactionOptions);
46
        }
47
    }
48
49
    /**
50
     * @param int $options one or more {@link TxConfig} constants combined together with the <tt>|</tt> operator
51
     */
52
    public function __construct(int $options = 0)
53
    {
54
        $isolationLevel = $options & (
55
                self::ISOLATION_SERIALIZABLE | self::ISOLATION_REPEATABLE_READ |
56
                self::ISOLATION_READ_COMMITTED | self::ISOLATION_READ_UNCOMMITTED
57
            );
58
        if ($isolationLevel != 0) {
59
            $this->setIsolationLevel($isolationLevel);
60
        }
61
62
        $this->setReadOnly(
63
            self::initBool($options, self::ACCESS_READ_ONLY, self::ACCESS_READ_WRITE, 'read/write and read-only access mode')
64
        );
65
        $this->setDeferrable(
66
            self::initBool($options, self::DEFERRABLE, self::NOT_DEFERRABLE, 'deferrable and non-deferrable mode')
67
        );
68
    }
69
70
    private static function initBool(int $options, int $trueBit, int $falseBit, string $errorDesc)
71
    {
72
        if ($options & $trueBit) {
73
            if ($options & $falseBit) {
74
                throw new \InvalidArgumentException(__CLASS__ . " options specify both $errorDesc");
75
            }
76
            return true;
77
        } elseif ($options & $falseBit) {
78
            return false;
79
        } else {
80
            return null;
81
        }
82
    }
83
84
    /**
85
     * @return int|null one of the <tt>ISOLATION_*</tt> constants, or <tt>null</tt> if no isolation level is specified
86
     */
87
    public function getIsolationLevel()
88
    {
89
        return $this->isolationLevel;
90
    }
91
92
    /**
93
     * @param int|null $isolationLevel one of the <tt>ISOLATION_*</tt> constants, or <tt>null</tt> to clear specifying
94
     *                                   the isolation level
95
     * @return $this
96
     */
97
    public function setIsolationLevel($isolationLevel)
98
    {
99
        static $levels = [
100
            null,
101
            self::ISOLATION_SERIALIZABLE,
102
            self::ISOLATION_REPEATABLE_READ,
103
            self::ISOLATION_READ_COMMITTED,
104
            self::ISOLATION_READ_UNCOMMITTED,
105
        ];
106
        if (!in_array($isolationLevel, $levels)) {
107
            throw new \InvalidArgumentException('Invalid isolation level specification');
108
        }
109
110
        $this->isolationLevel = $isolationLevel;
111
        return $this;
112
    }
113
114
    /**
115
     * @return bool|null whether the access mode is specified as read-only, or <tt>null</tt> if no access mode is
116
     *                     specified
117
     */
118
    public function isReadOnly()
119
    {
120
        return $this->readOnly;
121
    }
122
123
    /**
124
     * @param bool|null $readOnly
125
     * @return TxConfig
126
     */
127
    public function setReadOnly($readOnly): TxConfig
128
    {
129
        $this->readOnly = $readOnly;
130
        return $this;
131
    }
132
133
    /**
134
     * @return bool|null
135
     */
136
    public function isDeferrable()
137
    {
138
        return $this->deferrable;
139
    }
140
141
    /**
142
     * @param bool|null $deferrable
143
     * @return TxConfig
144
     */
145
    public function setDeferrable($deferrable): TxConfig
146
    {
147
        $this->deferrable = $deferrable;
148
        return $this;
149
    }
150
151
    public function toSql(): string
152
    {
153
        $clauses = [];
154
155
        $il = $this->getIsolationLevel();
156
        if ($il !== null) {
157
            switch ($il) {
158
                case self::ISOLATION_SERIALIZABLE:
159
                    $clauses[] = 'ISOLATION LEVEL SERIALIZABLE';
160
                    break;
161
                case self::ISOLATION_REPEATABLE_READ:
162
                    $clauses[] = 'ISOLATION LEVEL REPEATABLE READ';
163
                    break;
164
                case self::ISOLATION_READ_COMMITTED:
165
                    $clauses[] = 'ISOLATION LEVEL READ COMMITTED';
166
                    break;
167
                case self::ISOLATION_READ_UNCOMMITTED:
168
                    $clauses[] = 'ISOLATION LEVEL READ UNCOMMITTED';
169
                    break;
170
                default:
171
                    throw new InternalException('Undefined isolation level');
172
            }
173
        }
174
175
        $ro = $this->isReadOnly();
176
        if ($ro !== null) {
177
            $clauses[] = ($ro ? 'READ ONLY' : 'READ WRITE');
178
        }
179
180
        $d = $this->isDeferrable();
181
        if ($d !== null) {
182
            $clauses[] = ($d ? 'DEFERRABLE' : 'NOT DEFERRABLE');
183
        }
184
185
        return implode(' ', $clauses);
186
    }
187
}
188