Result::getIdFields()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
/** @noinspection PhpComposerExtensionStubsInspection */
4
5
declare(strict_types=1);
6
7
namespace EngineWorks\DBAL\Sqlsrv;
8
9
use EngineWorks\DBAL\CommonTypes;
10
use EngineWorks\DBAL\Internal\SqlServerResultFields;
11
use EngineWorks\DBAL\Result as ResultInterface;
12
use EngineWorks\DBAL\Traits\ResultImplementsCountable;
13
use EngineWorks\DBAL\Traits\ResultImplementsIterator;
14
use PDO;
15
use PDOStatement;
16
use RuntimeException;
17
18
class Result implements ResultInterface
19
{
20
    use ResultImplementsCountable;
21
    use ResultImplementsIterator;
22
23
    /**
24
     * PDO element
25
     * @var PDOStatement
26
     */
27
    private $stmt;
28
29
    /**
30
     * The number of the result rows
31
     * @var int
32
     */
33
    private $numRows;
34
35
    /**
36
     * Set of fieldname and commontype to use instead of detectedTypes
37
     * @var array<string, string>
38
     */
39
    private $overrideTypes;
40
41
    /**
42
     * The place where getFields result is cached
43
     * @var array<int, array{name: string, table: string, commontype: string}>|null
44
     */
45
    private $cachedGetFields;
46
47
    /**
48
     * Result based on PDOStatement
49
     *
50
     * @param PDOStatement $result
51
     * @param array<string, string> $overrideTypes
52
     */
53 52
    public function __construct(PDOStatement $result, array $overrideTypes = [])
54
    {
55
        /** @phpstan-var int<-1, max> $numRows Sqlsrv returns -1 */
56 52
        $numRows = $result->rowCount();
57 52
        if (-1 === $numRows) {
58
            throw new RuntimeException('Must use cursor PDO::CURSOR_SCROLL');
59
        }
60
61 52
        $this->stmt = $result;
62 52
        $this->overrideTypes = $overrideTypes;
63 52
        $this->numRows = $numRows;
64
    }
65
66
    /**
67
     * Close the query and remove property association
68
     */
69 42
    public function __destruct()
70
    {
71 42
        $this->stmt->closeCursor();
72
        // attatch this unset, otherwise it created a segment violation
73 42
        unset($this->stmt);
74
    }
75
76 41
    public function getFields(): array
77
    {
78 41
        if (null === $this->cachedGetFields) {
79 41
            $typeChecker = new SqlServerResultFields($this->overrideTypes, 'sqlsrv:decl_type');
80 41
            $this->cachedGetFields = $typeChecker->obtainFields($this->stmt);
81
        }
82
83 41
        return $this->cachedGetFields;
84
    }
85
86 6
    public function getIdFields(): bool
87
    {
88 6
        return false;
89
    }
90
91 14
    public function resultCount(): int
92
    {
93 14
        return $this->numRows;
94
    }
95
96 40
    public function fetchRow()
97
    {
98
        /** @var array<string, scalar|null>|false $values */
99 40
        $values = $this->stmt->fetch(PDO::FETCH_ASSOC);
100 40
        if (false === $values) {
101 18
            return false;
102
        }
103 36
        return $this->convertToExpectedValues($values);
104
    }
105
106
    /**
107
     * @param array<string, scalar|null> $values
108
     * @return array<string, scalar|null>
109
     */
110 36
    private function convertToExpectedValues(array $values): array
111
    {
112 36
        $fields = $this->getFields();
113 36
        foreach ($fields as $field) {
114 36
            $fieldname = strval($field['name']);
115 36
            $values[$fieldname] = $this->convertToExpectedValue($values[$fieldname], (string) $field['commontype']);
116
        }
117 36
        return $values;
118
    }
119
120
    /**
121
     * @param scalar|null $value
122
     * @param string $type
123
     * @return scalar|null
124
     */
125 36
    private function convertToExpectedValue($value, string $type)
126
    {
127 36
        if (null === $value) {
128 1
            return null;
129
        }
130 36
        if (CommonTypes::TDATETIME === $type) {
131 15
            return substr((string) $value, 0, 19);
132
        }
133 36
        if (CommonTypes::TTIME === $type) {
134
            return substr((string) $value, 0, 8);
135
        }
136 36
        return $value;
137
    }
138
139 3
    public function moveTo(int $offset): bool
140
    {
141
        // there are no records
142 3
        if ($this->resultCount() <= 0) {
143 1
            return false;
144
        }
145
        // the offset is out of bounds
146 2
        if ($offset < 0 || $offset > $this->resultCount() - 1) {
147 1
            return false;
148
        }
149
        // if the offset is on previous
150 1
        if (! $this->moveFirst()) {
151
            return false;
152
        }
153
        // move to the offset
154 1
        for ($i = 0; $i < $offset; $i++) {
155 1
            if (false === $this->stmt->fetch(PDO::FETCH_NUM)) {
156
                return false;
157
            }
158
        }
159 1
        return true;
160
    }
161
162 6
    public function moveFirst(): bool
163
    {
164 6
        if ($this->resultCount() <= 0) {
165 1
            return false;
166
        }
167 5
        return (false !== $this->stmt->execute());
168
    }
169
}
170