Failed Conditions
Pull Request — master (#4007)
by Sergei
62:58
created

ResultCacheStatement::fetch()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 4
nop 0
dl 0
loc 17
ccs 0
cts 0
cp 0
crap 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Cache;
6
7
use Doctrine\Common\Cache\Cache;
8
use Doctrine\DBAL\Driver\DriverException;
9
use Doctrine\DBAL\Driver\FetchUtils;
10
use Doctrine\DBAL\Driver\ResultStatement;
11
use Traversable;
12
use function array_values;
13
14
/**
15
 * Cache statement for SQL results.
16
 *
17
 * A result is saved in multiple cache keys, there is the originally specified
18
 * cache key which is just pointing to result rows by key. The following things
19
 * have to be ensured:
20
 *
21
 * 1. lifetime of the original key has to be longer than that of all the individual rows keys
22
 * 2. if any one row key is missing the query has to be re-executed.
23
 *
24
 * Also you have to realize that the cache will load the whole result into memory at once to ensure 2.
25
 * This means that the memory usage for cached results might increase by using this feature.
26
 */
27
final class ResultCacheStatement implements ResultStatement
28
{
29
    /** @var Cache */
30
    private $resultCache;
31
32
    /** @var string */
33
    private $cacheKey;
34
35
    /** @var string */
36
    private $realKey;
37
38
    /** @var int */
39
    private $lifetime;
40
41
    /** @var ResultStatement */
42
    private $statement;
43
44
    /**
45
     * Did we reach the end of the statement?
46
     *
47
     * @var bool
48
     */
49
    private $emptied = false;
50
51
    /** @var array<int,array<mixed>> */
52
    private $data;
53
54
    public function __construct(ResultStatement $stmt, Cache $resultCache, string $cacheKey, string $realKey, int $lifetime)
55
    {
56
        $this->statement   = $stmt;
57
        $this->resultCache = $resultCache;
58
        $this->cacheKey    = $cacheKey;
59
        $this->realKey     = $realKey;
60 264
        $this->lifetime    = $lifetime;
61
    }
62 264
63 264
    public function closeCursor() : void
64 264
    {
65 264
        $this->statement->closeCursor();
66 264
67 264
        if (! $this->emptied || $this->data === null) {
68
            return;
69 242
        }
70
71 242
        $data = $this->resultCache->fetch($this->cacheKey);
72
        if ($data === false) {
73 242
            $data = [];
74 22
        }
75
76
        $data[$this->realKey] = $this->data;
77 242
78 242
        $this->resultCache->save($this->cacheKey, $data, $this->lifetime);
79 242
        unset($this->data);
80
    }
81
82 242
    public function columnCount() : int
83
    {
84 242
        return $this->statement->columnCount();
85 242
    }
86 242
87
    /**
88 88
     * {@inheritdoc}
89
     */
90 88
    public function fetchNumeric()
91
    {
92
        $row = $this->fetch();
93 264
94
        if ($row === false) {
95 264
            return false;
96 264
        }
97
98
        return array_values($row);
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104
    public function fetchAssociative()
105
    {
106
        return $this->fetch();
107
    }
108
109
    /**
110
     * {@inheritdoc}
111 220
     */
112
    public function fetchOne()
113 220
    {
114 220
        return FetchUtils::fetchOne($this);
115
    }
116
117 220
    /**
118
     * {@inheritdoc}
119 220
     */
120 176
    public function fetchAllNumeric() : array
121
    {
122 176
        return $this->store(
123
            $this->statement->fetchAllAssociative(),
124 176
            true
125 88
        );
126
    }
127
128 132
    /**
129 66
     * {@inheritdoc}
130
     */
131
    public function fetchAllAssociative() : array
132 66
    {
133 44
        return $this->store(
134
            $this->statement->fetchAllAssociative(),
135
            true
136 22
        );
137 22
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142
    public function fetchColumn() : array
143 220
    {
144
        return $this->store(
145 220
            $this->statement->fetchAllAssociative(),
146
            true
147
        );
148
    }
149
150
    /**
151 44
     * @return Traversable<int,array<int,mixed>>
152
     *
153 44
     * @throws DriverException
154
     */
155 44
    public function iterateNumeric() : Traversable
156 22
    {
157 22
        return FetchUtils::iterateNumeric($this);
158
    }
159
160
    /**
161 44
     * @return Traversable<int,array<string,mixed>>
162 44
     *
163
     * @throws DriverException
164 44
     */
165
    public function iterateAssociative() : Traversable
166
    {
167
        return FetchUtils::iterateAssociative($this);
168
    }
169
170
    /**
171
     * @return Traversable<int,mixed>
172
     *
173
     * @throws DriverException
174
     */
175
    public function iterateColumn() : Traversable
176
    {
177
        return FetchUtils::iterateColumn($this);
178
    }
179
180
    /**
181
     * Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement
182
     * executed by the corresponding object.
183
     *
184
     * If the last SQL statement executed by the associated Statement object was a SELECT statement,
185
     * some databases may return the number of rows returned by that statement. However,
186
     * this behaviour is not guaranteed for all databases and should not be
187
     * relied on for portable applications.
188
     *
189
     * @return int The number of rows.
190
     */
191
    public function rowCount() : int
192
    {
193
        return $this->statement->rowCount();
194
    }
195
196
    /**
197
     * @return array<string,mixed>|false
198
     *
199
     * @throws DriverException
200
     */
201
    private function fetch()
202
    {
203
        if ($this->data === null) {
204
            $this->data = [];
205
        }
206
207
        $row = $this->statement->fetchAssociative();
208
209
        if ($row !== false) {
210
            $this->data[] = $row;
211
212
            return $row;
213
        }
214
215
        $this->emptied = true;
216
217
        return false;
218
    }
219
220
    /**
221
     * @param  array<int,array<mixed>> $data
222
     *
223
     * @return array<int,array<mixed>>
224
     */
225
    private function store(array $data, bool $isArray) : array
226
    {
227
        if (! $isArray) {
228
            foreach ($data as $key => $value) {
229
                $data[$key] = [$value];
230
            }
231
        }
232
233
        $this->data    = $data;
234
        $this->emptied = true;
235
236
        return $this->data;
237
    }
238
}
239