Issues (41)

ResultEntity.php (1 issue)

1
<?php
2
3
namespace WebStream\Database;
4
5
use Doctrine\DBAL\Driver\Statement;
6
use WebStream\DI\Injector;
7
use WebStream\Exception\Extend\CollectionException;
8
9
/**
10
 * ResultEntity
11
 * @author Ryuichi TANAKA.
12
 * @since 2015/01/11
13
 * @version 0.7
14
 */
15
class ResultEntity implements \Iterator, \SeekableIterator, \ArrayAccess, \Countable
16
{
17
    use Injector;
18
19
    /**
20
     * @var Doctrine\DBAL\Statement ステートメント
21
     */
22
    private $stmt;
23
24
    /**
25
     * @var array<mixed> 列データ
26
     */
27
    private array $row;
28
29
    /**
30
     * @var array<mixed> キャッシュ化列データ
31
     */
32
    private array $rowCache;
33
34
    /**
35
     * @var int インデックス位置
36
     */
37
    private int $position;
38
39
    /**
40
     * @var EntityManager エンティティマネージャ
41
     */
42
    private EntityManager $entityManager;
43
44
    /**
45
     * コンストラクタ
46
     * @param Statement $stmt ステートメントオブジェクト
47
     * @param string $classpath クラスパス
48
     */
49 16
    public function __construct(Statement $stmt, string $classpath)
50
    {
51 16
        $this->stmt = $stmt;
52 16
        $this->position = 0;
53 16
        $this->rowCache = [];
54 16
        $this->entityManager = new EntityManager($classpath);
55 16
    }
56
57
    /**
58
     * デストラクタ
59
     */
60 16
    public function __destruct()
61
    {
62 16
        $this->stmt = null;
63 16
        $this->rowCache = [];
64 16
    }
65
66
    /**
67
     * 初期処理
68
     */
69 16
    public function initialize()
70
    {
71 16
        $this->entityManager->inject('logger', $this->logger)
72 16
            ->setColumnMeta($this->getColumnMeta());
73 16
    }
74
75
    /**
76
     * Implements Countable#count
77
     * @return integer 結果件数
78
     */
79
    public function count()
80
    {
81
        if ($this->stmt === null) {
82
            $count = count($this->rowCache);
83
        } else {
84
            $count = $this->stmt->rowCount();
85
            if ($count === 0) {
86
                $this->toArray();
87
                $count = count($this->rowCache);
88
            }
89
        }
90
91
        return $count;
92
    }
93
94
    /**
95
     * Implements SeekableIterator#seek
96
     * カーソル位置を移動する
97
     * @param mixed オフセット
98
     * @return mixed
99
     */
100
    public function seek($offset)
101
    {
102
        if ($this->stmt !== null) {
103
            // Mysql does not support scrollable cursor.
104
            // but if statement to array, it is accessable.
105
            $this->toArray();
106
        }
107
        if (array_key_exists($offset, $this->rowCache)) {
108
            return $this->rowCache[$offset];
109
        } else {
110
            throw new \OutOfBoundsException("Current cursor is out of range: " . $offset);
111
        }
112
    }
113
114
    /**
115
     * Implements Iterator#current
116
     * 現在の要素を返却する
117
     * @return array<string> 列データ
118
     * @throws \ReflectionException
119
     */
120 16
    public function current()
121
    {
122 16
        if ($this->stmt === null) {
123
            return array_key_exists($this->position, $this->rowCache) ? $this->rowCache[$this->position] : null;
124
        }
125
126 16
        return $this->entityManager->getEntity($this->row);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->entityMana...->getEntity($this->row) returns the type object which is incompatible with the documented return type string[].
Loading history...
127
    }
128
129
    /**
130
     * Implements Iterator#key
131
     * 現在の要素のキーを返却する
132
     * @return integer キー
133
     */
134 16
    public function key()
135
    {
136 16
        return $this->position;
137
    }
138
139
    /**
140
     * Implements Iterator#next
141
     * 次の要素に進む
142
     */
143 16
    public function next()
144
    {
145 16
        if ($this->stmt !== null) {
146 16
            $row = $this->stmt->fetch(\PDO::FETCH_ASSOC);
147 16
            $this->row = is_bool($row) && $row === false ? [] : $row;
148
        }
149 16
        $this->position++;
150 16
    }
151
152
    /**
153
     * Implements Iterator#rewind
154
     * イテレータを先頭に巻き戻す
155
     */
156 16
    public function rewind()
157
    {
158 16
        if ($this->stmt !== null) {
159 16
            $this->row = $this->stmt->fetch(\PDO::FETCH_ASSOC, \PDO::FETCH_ORI_FIRST);
160
        }
161 16
        $this->position = 0;
162 16
    }
163
164
    /**
165
     * Implements Iterator#valid
166
     * 現在位置が有効かどうかを調べる
167
     * @return bool 有効かどうか
168
     */
169 16
    public function valid()
170
    {
171 16
        return !empty($this->row);
172
    }
173
174
    /**
175
     * Implements ArrayAccess#offsetExists
176
     * オフセットの位置に値が存在するかどうか返却する
177
     * @param $offset
178
     * @return bool 値が存在するかどうか
179
     */
180
    public function offsetExists($offset)
181
    {
182
        if ($this->stmt !== null) {
183
            $this->toArray();
184
        }
185
186
        return array_key_exists($offset, $this->rowCache);
187
    }
188
189
    /**
190
     * Implements ArrayAccess#offsetGet
191
     * オフセットの位置の値を返却する
192
     * @param $offset
193
     * @return mixed 値
194
     */
195
    public function offsetGet($offset)
196
    {
197
        if ($this->stmt !== null) {
198
            $this->toArray();
199
        }
200
201
        return $this->rowCache[$offset];
202
    }
203
204
    /**
205
     * Implements ArrayAccess#offsetSet
206
     * オフセットの位置に値を設定する
207
     * @param mixed オフセット
208
     * @param mixed 値
209
     */
210
    public function offsetSet($offset, $value)
211
    {
212
        throw new CollectionException("Database results are read only.");
213
    }
214
215
    /**
216
     * Implements ArrayAccess#offsetUnSet
217
     * オフセットの設定を解除する
218
     * @param mixed オフセット
219
     */
220
    public function offsetUnSet($offset)
221
    {
222
        throw new CollectionException("Database results are read only.");
223
    }
224
225
    /**
226
     * 検索結果を全て配列として返却する
227
     * @return array<string> 検索結果
228
     */
229
    public function toArray()
230
    {
231
        $this->rowCache = $this->stmt->fetchAll(\PDO::FETCH_ASSOC);
232
        $this->logger->debug("All results to array and cached.");
233
        $this->stmt = null;
234
235
        return $this->rowCache;
236
    }
237
238
    /**
239
     * テーブルのメタデータを返却する
240
     * @return array<string> メタデータ
241
     */
242 16
    private function getColumnMeta()
243
    {
244 16
        $columnMeta = [];
245 16
        for ($index = 0; $index < $this->stmt->columnCount(); $index++) {
246 16
            $column = $this->stmt->getColumnMeta($index);
247 16
            if (array_key_exists('sqlite:decl_type', $column)) {
248
                // sqlite
249 4
                $columnMeta[$column['name']] = $column['sqlite:decl_type'];
250
            } else {
251
                // mysql, postgresql
252 12
                $columnMeta[$column['name']] = $column['native_type'];
253
            }
254
        }
255
256 16
        return $columnMeta;
257
    }
258
}
259