Cursor::__destruct()   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
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 1
1
<?php declare(strict_types=1);
2
3
namespace Igni\Storage\Driver\Pdo;
4
5
use Igni\Storage\Driver\MemorySavingCursor;
6
use Igni\Storage\Exception\CursorException;
7
use Igni\Storage\Hydration\MemorySavingHydrator;
8
use Igni\Storage\Hydration\ObjectHydrator;
9
use Igni\Storage\Storable;
10
use IteratorIterator;
11
12
class Cursor implements MemorySavingCursor
13
{
14
    /** @var Connection  */
15
    private $connection;
16
    /** @var string */
17
    private $query;
18
    /** @var array */
19
    private $params;
20
    /** @var ObjectHydrator */
21
    private $hydrator;
22
    /** @var \PDOStatement */
23
    private $baseCursor;
24
    /** @var Storable|array|null */
25
    private $current = null;
26
    /** @var  IteratorIterator */
27
    private $iterator;
28
29 26
    public function __construct(
30
        Connection $connection,
31
        string $query,
32
        array $params = null
33
    ) {
34 26
        $this->connection = $connection;
35 26
        $this->query = $query;
36 26
        $this->params = $params;
37 26
    }
38
39
    public function getBaseCursor(): \PDOStatement
40
    {
41
        $this->open();
42
        return $this->baseCursor;
43
    }
44
45
    public function getConnection(): \Igni\Storage\Driver\Connection
46
    {
47
        return $this->connection;
48
    }
49
50 13
    public function hydrateWith(ObjectHydrator $hydrator): void
51
    {
52 13
        $this->hydrator = $hydrator;
53 13
    }
54
55
    public function saveMemory(bool $save = true): void
56
    {
57
        if ($this->hydrator instanceof MemorySavingHydrator) {
58
            $this->hydrator->saveMemory($save);
59
        }
60
    }
61
62 23
    public function current()
63
    {
64 23
        $this->open();
65 23
        return $this->current;
66
    }
67
68 12
    public function next(): void
69
    {
70 12
        $this->open();
71 12
        $this->iterator->next();
72 12
        $this->current = $this->fetch();
73 12
    }
74
75 6
    public function key(): int
76
    {
77 6
        $this->open();
78 6
        return $this->iterator->key();
79
    }
80
81 15
    public function valid(): bool
82
    {
83 15
        $this->open();
84 15
        return $this->iterator->valid();
85
    }
86
87 7
    public function rewind(): void
88
    {
89 7
        $this->close();
90 7
        $this->open();
91 7
    }
92
93 26
    public function close(): void
94
    {
95 26
        $this->current = null;
96 26
        if ($this->baseCursor) {
97 23
            $this->baseCursor = null;
98 23
            $this->iterator = null;
99
        }
100 26
    }
101
102 6
    public function execute(): void
103
    {
104 6
        $this->open();
105 6
        $this->close();
106 6
    }
107
108 23
    public function open(): void
109
    {
110 23
        if ($this->iterator) {
111 14
            return;
112
        }
113
        try {
114 23
            $this->baseCursor = $this->connection
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->connection->getBa...->prepare($this->query) can also be of type boolean. However, the property $baseCursor is declared as type PDOStatement. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
115 23
                ->getBaseConnection()
116 23
                ->prepare($this->query);
117 23
            $this->baseCursor->setFetchMode(\PDO::FETCH_ASSOC);
118 23
            $this->baseCursor->execute($this->params);
119 23
            $this->iterator = new IteratorIterator($this->baseCursor);
0 ignored issues
show
Bug introduced by
It seems like $this->baseCursor can also be of type boolean; however, parameter $iterator of IteratorIterator::__construct() does only seem to accept Traversable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
            $this->iterator = new IteratorIterator(/** @scrutinizer ignore-type */ $this->baseCursor);
Loading history...
120 23
            $this->iterator->rewind();
121 23
            $this->current = $this->fetch();
122
        } catch (\Exception $e) {
123
            throw CursorException::forExecutionFailure($this, $e->getMessage());
124
        }
125 23
    }
126
127
    public function toArray(): array
128
    {
129
        return iterator_to_array($this);
130
    }
131
132 23
    private function fetch()
133
    {
134 23
        $fetched = $this->iterator->current();
135 23
        if ($this->hydrator && $fetched !== null) {
136 10
            $fetched = $this->hydrator->hydrate($fetched);
137
        }
138
139 23
        return $fetched;
140
    }
141
142 26
    public function __destruct()
143
    {
144 26
        $this->close();
145 26
    }
146
}
147