Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace Doctrine\DBAL\Driver\Mysqli;
6
 
7
use Doctrine\DBAL\Driver\Exception;
8
use Doctrine\DBAL\Driver\FetchUtils;
9
use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
10
use Doctrine\DBAL\Driver\Result as ResultInterface;
11
use mysqli_sql_exception;
12
use mysqli_stmt;
13
 
14
use function array_column;
15
use function array_combine;
16
use function array_fill;
17
use function count;
18
 
19
final class Result implements ResultInterface
20
{
21
    private mysqli_stmt $statement;
22
 
23
    /**
24
     * Whether the statement result has columns. The property should be used only after the result metadata
25
     * has been fetched ({@see $metadataFetched}). Otherwise, the property value is undetermined.
26
     */
27
    private bool $hasColumns = false;
28
 
29
    /**
30
     * Mapping of statement result column indexes to their names. The property should be used only
31
     * if the statement result has columns ({@see $hasColumns}). Otherwise, the property value is undetermined.
32
     *
33
     * @var array<int,string>
34
     */
35
    private array $columnNames = [];
36
 
37
    /** @var mixed[] */
38
    private array $boundValues = [];
39
 
40
    /**
41
     * @internal The result can be only instantiated by its driver connection or statement.
42
     *
43
     * @throws Exception
44
     */
45
    public function __construct(mysqli_stmt $statement)
46
    {
47
        $this->statement = $statement;
48
 
49
        $meta = $statement->result_metadata();
50
 
51
        if ($meta === false) {
52
            return;
53
        }
54
 
55
        $this->hasColumns = true;
56
 
57
        $this->columnNames = array_column($meta->fetch_fields(), 'name');
58
 
59
        $meta->free();
60
 
61
        // Store result of every execution which has it. Otherwise it will be impossible
62
        // to execute a new statement in case if the previous one has non-fetched rows
63
        // @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
64
        $this->statement->store_result();
65
 
66
        // Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
67
        // it will have to allocate as much memory as it may be needed for the given column type
68
        // (e.g. for a LONGBLOB column it's 4 gigabytes)
69
        // @link https://bugs.php.net/bug.php?id=51386#1270673122
70
        //
71
        // Make sure that the values are bound after each execution. Otherwise, if free() has been
72
        // previously called on the result, the values are unbound making the statement unusable.
73
        //
74
        // It's also important that row values are bound after _each_ call to store_result(). Otherwise,
75
        // if mysqli is compiled with libmysql, subsequently fetched string values will get truncated
76
        // to the length of the ones fetched during the previous execution.
77
        $this->boundValues = array_fill(0, count($this->columnNames), null);
78
 
79
        // The following is necessary as PHP cannot handle references to properties properly
80
        $refs = &$this->boundValues;
81
 
82
        if (! $this->statement->bind_result(...$refs)) {
83
            throw StatementError::new($this->statement);
84
        }
85
    }
86
 
87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function fetchNumeric()
91
    {
92
        try {
93
            $ret = $this->statement->fetch();
94
        } catch (mysqli_sql_exception $e) {
95
            throw StatementError::upcast($e);
96
        }
97
 
98
        if ($ret === false) {
99
            throw StatementError::new($this->statement);
100
        }
101
 
102
        if ($ret === null) {
103
            return false;
104
        }
105
 
106
        $values = [];
107
 
108
        foreach ($this->boundValues as $v) {
109
            $values[] = $v;
110
        }
111
 
112
        return $values;
113
    }
114
 
115
    /**
116
     * {@inheritDoc}
117
     */
118
    public function fetchAssociative()
119
    {
120
        $values = $this->fetchNumeric();
121
 
122
        if ($values === false) {
123
            return false;
124
        }
125
 
126
        return array_combine($this->columnNames, $values);
127
    }
128
 
129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function fetchOne()
133
    {
134
        return FetchUtils::fetchOne($this);
135
    }
136
 
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function fetchAllNumeric(): array
141
    {
142
        return FetchUtils::fetchAllNumeric($this);
143
    }
144
 
145
    /**
146
     * {@inheritdoc}
147
     */
148
    public function fetchAllAssociative(): array
149
    {
150
        return FetchUtils::fetchAllAssociative($this);
151
    }
152
 
153
    /**
154
     * {@inheritdoc}
155
     */
156
    public function fetchFirstColumn(): array
157
    {
158
        return FetchUtils::fetchFirstColumn($this);
159
    }
160
 
161
    public function rowCount(): int
162
    {
163
        if ($this->hasColumns) {
164
            return $this->statement->num_rows;
165
        }
166
 
167
        return $this->statement->affected_rows;
168
    }
169
 
170
    public function columnCount(): int
171
    {
172
        return $this->statement->field_count;
173
    }
174
 
175
    public function free(): void
176
    {
177
        $this->statement->free_result();
178
    }
179
}