Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.08% covered (success)
98.08%
51 / 52
93.75% covered (success)
93.75%
15 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
Result
98.08% covered (success)
98.08%
51 / 52
93.75% covered (success)
93.75%
15 / 16
27
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 __destruct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 free
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 isFree
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkIsFree
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 isBuffered
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 moveCursor
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 setFetchClass
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 fetch
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 fetchAll
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 fetchRow
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 fetchArray
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 fetchArrayAll
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 fetchArrayRow
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 numRows
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 fetchFields
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework Database Library.
4 *
5 * (c) Natan Felles <natanfelles@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10namespace Framework\Database;
11
12use Framework\Database\Result\Field;
13use LogicException;
14use mysqli_result;
15use OutOfBoundsException;
16use OutOfRangeException;
17
18/**
19 * Class Result.
20 *
21 * @package database
22 */
23class Result
24{
25    /**
26     * @var mysqli_result<int,array|false|null>
27     */
28    protected mysqli_result $result;
29    protected bool $buffered;
30    protected bool $free = false;
31    protected string $fetchClass = \stdClass::class;
32    /**
33     * @var array<mixed>
34     */
35    protected array $fetchConstructor = [];
36
37    /**
38     * Result constructor.
39     *
40     * @param mysqli_result<int,array|false|null> $result
41     * @param bool $buffered
42     */
43    public function __construct(mysqli_result $result, bool $buffered)
44    {
45        $this->result = $result;
46        $this->buffered = $buffered;
47    }
48
49    public function __destruct()
50    {
51        if ( ! $this->isFree()) {
52            $this->free();
53        }
54    }
55
56    /**
57     * Frees the memory associated with a result.
58     */
59    public function free() : void
60    {
61        $this->checkIsFree();
62        $this->free = true;
63        $this->result->free();
64    }
65
66    public function isFree() : bool
67    {
68        return $this->free;
69    }
70
71    protected function checkIsFree() : void
72    {
73        if ($this->isFree()) {
74            throw new LogicException('Result is already free');
75        }
76    }
77
78    public function isBuffered() : bool
79    {
80        return $this->buffered;
81    }
82
83    /**
84     * Adjusts the result pointer to an arbitrary row in the result.
85     *
86     * @param int $offset The field offset. Must be between zero and the total
87     * number of rows minus one
88     *
89     * @throws LogicException if is an unbuffered result
90     * @throws OutOfBoundsException for invalid cursor offset
91     *
92     * @return bool
93     */
94    public function moveCursor(int $offset) : bool
95    {
96        $this->checkIsFree();
97        if ( ! $this->isBuffered()) {
98            throw new LogicException('Cursor cannot be moved on unbuffered results');
99        }
100        if ($offset < 0 || ($offset !== 0 && $offset >= $this->result->num_rows)) {
101            throw new OutOfRangeException(
102                "Invalid cursor offset: {$offset}"
103            );
104        }
105        return $this->result->data_seek($offset);
106    }
107
108    /**
109     * @param string $class
110     * @param mixed ...$constructor
111     *
112     * @return static
113     */
114    public function setFetchClass(string $class, mixed ...$constructor) : static
115    {
116        $this->fetchClass = $class;
117        $this->fetchConstructor = $constructor;
118        return $this;
119    }
120
121    /**
122     * Fetches the current row as object and move the cursor to the next.
123     *
124     * @param string|null $class
125     * @param mixed ...$constructor
126     *
127     * @return object|null
128     */
129    public function fetch(string $class = null, mixed ...$constructor) : object | null
130    {
131        $this->checkIsFree();
132        $class ??= $this->fetchClass;
133        $constructor = $constructor ?: $this->fetchConstructor;
134        if ($constructor) {
135            return $this->result->fetch_object($class, $constructor);
136        }
137        return $this->result->fetch_object($class);
138    }
139
140    /**
141     * Fetches all rows as objects.
142     *
143     * @param string|null $class
144     * @param mixed ...$constructor
145     *
146     * @return array<int,object>
147     */
148    public function fetchAll(string $class = null, mixed ...$constructor) : array
149    {
150        $this->checkIsFree();
151        $all = [];
152        while ($row = $this->fetch($class, ...$constructor)) {
153            $all[] = $row;
154        }
155        return $all;
156    }
157
158    /**
159     * Fetches a specific row as object and move the cursor to the next.
160     *
161     * @param int $offset
162     * @param string|null $class
163     * @param mixed ...$constructor
164     *
165     * @return object|null
166     */
167    public function fetchRow(int $offset, string $class = null, mixed ...$constructor) : object | null
168    {
169        $this->checkIsFree();
170        $this->moveCursor($offset);
171        return $this->fetch($class, ...$constructor);
172    }
173
174    /**
175     * Fetches the current row as array and move the cursor to the next.
176     *
177     * @return array<string,int|string|null>|null
178     */
179    public function fetchArray() : ?array
180    {
181        $this->checkIsFree();
182        return $this->result->fetch_assoc();
183    }
184
185    /**
186     * Fetches all rows as arrays.
187     *
188     * @return array<int,array<mixed>>
189     */
190    public function fetchArrayAll() : array
191    {
192        $this->checkIsFree();
193        return $this->result->fetch_all(\MYSQLI_ASSOC);
194    }
195
196    /**
197     * Fetches a specific row as array and move the cursor to the next.
198     *
199     * @param int $offset
200     *
201     * @return array<string,int|string|null>
202     */
203    public function fetchArrayRow(int $offset) : array
204    {
205        $this->checkIsFree();
206        $this->moveCursor($offset);
207        return $this->result->fetch_assoc();
208    }
209
210    /**
211     * Gets the number of rows in the result set.
212     *
213     * @return int|string
214     */
215    public function numRows() : int | string
216    {
217        $this->checkIsFree();
218        return $this->result->num_rows;
219    }
220
221    /**
222     * Returns an array of objects representing the fields in a result set.
223     *
224     * @return array<int,Field>|false an array of objects which contains field
225     * definition information or false if no field information is available
226     */
227    public function fetchFields() : array | false
228    {
229        $this->checkIsFree();
230        $fields = $this->result->fetch_fields();
231        if ($fields === false) { // @phpstan-ignore-line
232            return false;
233        }
234        foreach ($fields as &$field) {
235            $field = new Field($field);
236        }
237        return $fields;
238    }
239}