Passed
Pull Request — master (#822)
by Sergei
02:51
created

PdoDataReader::key()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
c 0
b 0
f 0
nc 4
nop 0
dl 0
loc 15
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Driver\Pdo;
6
7
use Closure;
8
use Countable;
9
use Iterator;
10
use PDO;
11
use PDOStatement;
12
use Yiisoft\Db\Exception\InvalidCallException;
13
use Yiisoft\Db\Exception\InvalidParamException;
14
use Yiisoft\Db\Query\DataReaderInterface;
15
use Yiisoft\Db\Query\QueryPartsInterface;
16
17
use function is_string;
18
19
/**
20
 * Provides an abstract way to read data from a database.
21
 *
22
 * A data reader is an object that can be used to read a forward-only stream of rows from a database.
23
 *
24
 * It's typically used in combination with a command object, such as a {@see \Yiisoft\Db\Command\AbstractCommand},
25
 * to execute a SELECT statement and read the results.
26
 *
27
 * The class provides methods for accessing the data returned by the query.
28
 *
29
 * @psalm-import-type IndexBy from QueryPartsInterface
30
 */
31
final class PdoDataReader implements DataReaderInterface
32
{
33
    /** @psalm-var IndexBy $indexBy */
34
    private Closure|string|null $indexBy = null;
35
    private int $index = 0;
36
    private array|false $row;
37
    private PDOStatement $statement;
38
39
    /**
40
     * @throws InvalidParamException If the PDOStatement is null.
41
     */
42
    public function __construct(PdoCommandInterface $command)
43
    {
44
        $statement = $command->getPDOStatement();
45
46
        if ($statement === null) {
47
            throw new InvalidParamException('The PDOStatement cannot be null.');
48
        }
49
50
        $this->statement = $statement;
51
        /** @var array|false */
52
        $this->row = $statement->fetch(PDO::FETCH_ASSOC);
0 ignored issues
show
Bug introduced by
The property row does not exist on false.
Loading history...
53
    }
54
55
    public function __destruct()
56
    {
57
        $this->statement->closeCursor();
58
    }
59
60
    /**
61
     * Returns the number of rows in the result set.
62
     *
63
     * This method is required by the interface {@see Countable}.
64
     *
65
     * Note, most DBMS mayn't give a meaningful count. In this case, use "SELECT COUNT(*) FROM tableName" to obtain the
66
     * number of rows.
67
     */
68
    public function count(): int
69
    {
70
        return $this->statement->rowCount();
71
    }
72
73
    /**
74
     * Resets the iterator to the initial state.
75
     *
76
     * This method is required by the interface {@see Iterator}.
77
     *
78
     * @throws InvalidCallException If the data reader isn't at the beginning.
79
     */
80
    public function rewind(): void
81
    {
82
        if ($this->index === 0) {
83
            return;
84
        }
85
86
        throw new InvalidCallException('DataReader cannot rewind. It is a forward-only reader.');
87
    }
88
89
    public function key(): int|string|null
90
    {
91
        if ($this->indexBy === null) {
92
            return $this->index;
93
        }
94
95
        if ($this->row === false) {
96
            return null;
97
        }
98
99
        if (is_string($this->indexBy)) {
100
            return (string) $this->row[$this->indexBy];
101
        }
102
103
        return ($this->indexBy)($this->row);
104
    }
105
106
    public function current(): array|false
107
    {
108
        return $this->row;
109
    }
110
111
    /**
112
     * Moves the internal pointer to the next row.
113
     *
114
     * This method is required by the interface {@see Iterator}.
115
     */
116
    public function next(): void
117
    {
118
        /** @var array|false */
119
        $this->row = $this->statement->fetch(PDO::FETCH_ASSOC);
0 ignored issues
show
Bug introduced by
The property row does not exist on false.
Loading history...
120
        $this->index++;
121
    }
122
123
    /**
124
     * Returns whether there is a row of data at current position.
125
     *
126
     * This method is required by the interface {@see Iterator}.
127
     */
128
    public function valid(): bool
129
    {
130
        return $this->row !== false;
131
    }
132
133
    public function indexBy(Closure|string|null $indexBy): static
134
    {
135
        $this->indexBy = $indexBy;
136
        return $this;
137
    }
138
}
139