Completed
Push — master ( de8a99...be31cd )
by Ron
02:39
created

RunnableSelect::fetchObjects()   C

Complexity

Conditions 7
Paths 1

Size

Total Lines 26
Code Lines 18

Duplication

Lines 26
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 26
loc 26
rs 6.7272
cc 7
eloc 18
nc 1
nop 2
1
<?php
2
namespace Kir\MySQL\Builder;
3
4
use Closure;
5
use Generator;
6
use IteratorAggregate;
7
use Kir\MySQL\Builder\Helpers\DBIgnoreRow;
8
use Kir\MySQL\Builder\Helpers\FieldTypeProvider;
9
use Kir\MySQL\Builder\Helpers\FieldValueConverter;
10
use Kir\MySQL\Builder\Helpers\LazyRowGenerator;
11
use Kir\MySQL\Builder\Helpers\YieldPolyfillIterator;
12
use PDO;
13
use Traversable;
14
15
/**
16
 */
17
class RunnableSelect extends Select implements IteratorAggregate {
18
	/** @var array */
19
	private $values = array();
20
	/** @var bool */
21
	private $preserveTypes = false;
22
	/** @var int */
23
	private $foundRows = 0;
24
25
	/**
26
	 * @param array $values
27
	 * @return $this
28
	 */
29
	public function bindValues(array $values) {
30
		$this->values = array_merge($this->values, $values);
31
		return $this;
32
	}
33
34
	/**
35
	 * @param string $key
36
	 * @param string|int|bool|float|null $value
37
	 * @return $this
38
	 */
39
	public function bindValue($key, $value) {
40
		$this->values[$key] = $value;
41
		return $this;
42
	}
43
44
	/**
45
	 * @return $this
46
	 */
47
	public function clearValues() {
48
		$this->values = array();
49
		return $this;
50
	}
51
52
	/**
53
	 * @param bool $preserveTypes
54
	 * @return $this
55
	 */
56
	public function setPreserveTypes($preserveTypes = true) {
57
		$this->preserveTypes = $preserveTypes;
58
		return $this;
59
	}
60
61
	/**
62
	 * @param Closure $callback
63
	 * @return array[]
64
	 */
65 View Code Duplication
	public function fetchRows(Closure $callback = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
66
		return $this->createTempStatement(function (QueryStatement $statement) use ($callback) {
67
			$statement->setFetchMode(PDO::FETCH_ASSOC);
68
			$data = $statement->fetchAll();
69
			if($this->preserveTypes) {
70
				$columnDefinitions = FieldTypeProvider::getFieldTypes($statement);
71
				foreach($data as &$row) {
72
					$row = FieldValueConverter::convertValues($row, $columnDefinitions);
73
				}
74
			}
75
			if($callback !== null) {
76
				return call_user_func(function ($resultData = []) use ($data, $callback) {
77
					foreach($data as $row) {
78
						$result = $callback($row);
79
						if($result !== null && !($result instanceof DBIgnoreRow)) {
80
							$resultData[] = $result;
81
						} else {
82
							$resultData[] = $row;
83
						}
84
					}
85
					return $resultData;
86
				});
87
			}
88
			return $data;
89
		});
90
	}
91
92
	/**
93
	 * @param Closure $callback
94
	 * @return array[]|\Generator
95
	 */
96 View Code Duplication
	public function fetchRowsLazy(Closure $callback = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
97
		if(version_compare(PHP_VERSION, '5.5', '<')) {
98
			return new YieldPolyfillIterator($callback, $this->preserveTypes, function () {
99
				$statement = $this->createStatement();
100
				$statement->setFetchMode(PDO::FETCH_ASSOC);
101
				return $statement;
102
			});
103
		}
104
		$statement = $this->createStatement();
105
		$statement->setFetchMode(PDO::FETCH_ASSOC);
106
		$generator = new LazyRowGenerator($this->preserveTypes);
107
		return $generator->generate($statement, $callback);
108
	}
109
110
	/**
111
	 * @param Closure|null $callback
112
	 * @return string[]
113
	 * @throws \Exception
114
	 */
115 View Code Duplication
	public function fetchRow(Closure $callback = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
116
		return $this->createTempStatement(function (QueryStatement $statement) use ($callback) {
117
			$statement->setFetchMode(PDO::FETCH_ASSOC);
118
			$row = $statement->fetch();
119
			if(!is_array($row)) {
120
				return [];
121
			}
122
			if($this->preserveTypes) {
123
				$columnDefinitions = FieldTypeProvider::getFieldTypes($statement);
124
				$row = FieldValueConverter::convertValues($row, $columnDefinitions);
125
			}
126
			if($callback !== null) {
127
				$result = $callback($row);
128
				if($result !== null) {
129
					$row = $result;
130
				}
131
			}
132
			return $row;
133
		});
134
	}
135
136
	/**
137
	 * @param string $className
138
	 * @param Closure $callback
139
	 * @return \array[]
140
	 * @throws \Exception
141
	 */
142 View Code Duplication
	public function fetchObjects($className, Closure $callback = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
143
		return $this->createTempStatement(function (QueryStatement $statement) use ($className, $callback) {
144
			$statement->setFetchMode(PDO::FETCH_CLASS, $className);
145
			$data = $statement->fetchAll();
146
			if($this->preserveTypes) {
147
				$columnDefinitions = FieldTypeProvider::getFieldTypes($statement);
148
				foreach($data as &$row) {
149
					$row = FieldValueConverter::convertValues($row, $columnDefinitions);
150
				}
151
			}
152
			if($callback !== null) {
153
				return call_user_func(function ($resultData = []) use ($data, $callback) {
154
					foreach($data as $row) {
155
						$result = $callback($row);
156
						if($result !== null && !($result instanceof DBIgnoreRow)) {
157
							$resultData[] = $result;
158
						} else {
159
							$resultData[] = $row;
160
						}
161
					}
162
					return $resultData;
163
				});
164
			}
165
			return $data;
166
		});
167
	}
168
169
	/**
170
	 * @param string $className
171
	 * @param Closure $callback
172
	 * @return array[]|Generator
173
	 */
174 View Code Duplication
	public function fetchObjectsLazy($className, Closure $callback = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
175
		if(version_compare(PHP_VERSION, '5.5', '<')) {
176
			return new YieldPolyfillIterator($callback, $this->preserveTypes, function () use ($className) {
177
				$statement = $this->createStatement();
178
				$statement->setFetchMode(PDO::FETCH_CLASS, $className);
179
				return $statement;
180
			});
181
		}
182
		$statement = $this->createStatement();
183
		$statement->setFetchMode(PDO::FETCH_CLASS, $className);
184
		$generator = new LazyRowGenerator($this->preserveTypes);
185
		return $generator->generate($statement, $callback);
186
	}
187
188
	/**
189
	 * @param string $className
190
	 * @param Closure|null $callback
191
	 * @return string[]
192
	 * @throws \Exception
193
	 */
194 View Code Duplication
	public function fetchObject($className, Closure $callback = null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
195
		return $this->createTempStatement(function (QueryStatement $statement) use ($className, $callback) {
196
			$statement->setFetchMode(PDO::FETCH_CLASS, $className);
197
			$row = $statement->fetch();
198
			if(!is_array($row)) {
199
				return [];
200
			}
201
			if($this->preserveTypes) {
202
				$columnDefinitions = FieldTypeProvider::getFieldTypes($statement);
203
				$row = FieldValueConverter::convertValues($row, $columnDefinitions);
204
			}
205
			if($callback !== null) {
206
				$result = $callback($row);
207
				if($result !== null) {
208
					$row = $result;
209
				}
210
			}
211
			return $row;
212
		});
213
	}
214
215
	/**
216
	 * @param bool $treatValueAsArray
217
	 * @return mixed[]
218
	 */
219
	public function fetchKeyValue($treatValueAsArray = false) {
220
		return $this->createTempStatement(function (QueryStatement $statement) use ($treatValueAsArray) {
221
			if($treatValueAsArray) {
222
				$rows = $statement->fetchAll(\PDO::FETCH_ASSOC);
223
				$result = array();
224
				foreach($rows as $row) {
225
					list($key) = array_values($row);
226
					$result[$key] = $row;
227
				}
228
				return $result;
229
			}
230
			return $statement->fetchAll(\PDO::FETCH_KEY_PAIR);
231
		});
232
	}
233
234
	/**
235
	 * @param array $fields
236
	 * @return array
237
	 */
238
	public function fetchGroups(array $fields) {
239
		$rows = $this->fetchRows();
240
		$result = array();
241
		foreach($rows as $row) {
242
			$tmp = &$result;
243
			foreach($fields as $field) {
244
				$value = $row[$field];
245
				if(!array_key_exists($value, $tmp)) {
246
					$tmp[$value] = [];
247
				}
248
				$tmp = &$tmp[$value];
249
			}
250
			$tmp[] = $row;
251
		}
252
		return $result;
253
	}
254
255
	/**
256
	 * @return string[]
257
	 */
258
	public function fetchArray() {
259
		return $this->createTempStatement(function (QueryStatement $stmt) {
260
			return $stmt->fetchAll(\PDO::FETCH_COLUMN);
261
		});
262
	}
263
264
	/**
265
	 * @param mixed $default
266
	 * @return null|bool|string|int|float
267
	 */
268
	public function fetchValue($default = null) {
269
		return $this->createTempStatement(function (QueryStatement $stmt) use ($default) {
270
			$result = $stmt->fetch(\PDO::FETCH_NUM);
271
			if($result !== false) {
272
				return $result[0];
273
			}
274
			return $default;
275
		});
276
	}
277
278
	/**
279
	 * @return bool
280
	 */
281
	public function getFoundRows() {
282
		return $this->foundRows;
283
	}
284
285
	/**
286
	 * @param callback $fn
287
	 * @return mixed
288
	 * @throws \Exception
289
	 */
290
	private function createTempStatement($fn) {
291
		$stmt = $this->createStatement();
292
		$res = null;
293
		try {
294
			$res = call_user_func($fn, $stmt);
295
		} catch (\Exception $e) { // PHP 5.4 compatibility
296
			$stmt->closeCursor();
297
			throw $e;
298
		}
299
		$stmt->closeCursor();
300
		return $res;
301
	}
302
303
	/**
304
	 * @return QueryStatement
305
	 */
306
	private function createStatement() {
307
		$db = $this->db();
308
		$query = $this->__toString();
309
		$statement = $db->prepare($query);
310
		$statement->execute($this->values);
311
		if($this->getCalcFoundRows()) {
312
			$this->foundRows = (int) $db->query('SELECT FOUND_ROWS()')->fetchColumn();
313
		}
314
		return $statement;
315
	}
316
317
	/**
318
	 * @return Traversable|array[]|\Generator
319
	 */
320
	public function getIterator() {
321
		return $this->fetchRowsLazy();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->fetchRowsLazy(); of type Kir\MySQL\Builder\Helper...yfillIterator|Generator adds the type Generator to the return on line 321 which is incompatible with the return type declared by the interface IteratorAggregate::getIterator of type Traversable.
Loading history...
322
	}
323
}
324