Skip to content

Commit 6894cae

Browse files
committed
Statement::process can now handle ResultSet instances too
1 parent d881d76 commit 6894cae

File tree

4 files changed

+87
-10
lines changed

4 files changed

+87
-10
lines changed

phpstan.src.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@ parameters:
1818
path: src/Stream.php
1919
- message: '#Unsafe usage of new static\(\).#'
2020
path: src/AbstractCsv.php
21+
- message: '#Instanceof between League\\Csv\\ResultSet and League\\Csv\\ResultSet will always evaluate to true.#'
22+
path: src/Statement.php
23+
- message: '#Result of && is always false.#'
24+
path: src/Statement.php
2125
reportUnmatchedIgnoredErrors: false

phpstan.tests.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@ parameters:
99
path: tests/DetectDelimiterTest.php
1010
- message: '#Parameter \#1 \$document of static method League\\Csv\\Polyfill\\EmptyEscapeParser::parse\(\) expects League\\Csv\\Stream\|SplFileObject, stdClass given.#'
1111
path: tests/Polyfill/EmptyEscapeParserTest.php
12+
- message: '#Parameter \#1 \$records of method League\\Csv\\Statement::process\(\) expects League\\Csv\\Reader\|League\\Csv\\ResultSet, stdClass given.#'
13+
path: tests/ResultSetTest.php
14+
1215
reportUnmatchedIgnoredErrors: true
1316
checkMissingIterableValueType: false

src/Statement.php

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
use CallbackFilterIterator;
1818
use Iterator;
1919
use LimitIterator;
20+
use TypeError;
2021
use function array_reduce;
21-
use function iterator_to_array;
2222

2323
/**
2424
* Criteria to filter a {@link Reader} object.
@@ -135,20 +135,63 @@ public function limit(int $limit): self
135135
/**
136136
* Execute the prepared Statement on the {@link Reader} object.
137137
*
138-
* @param string[] $header an optional header to use instead of the CSV document header
138+
* @param Reader|ResultSet $records
139+
* @param string[] $header an optional header to use instead of the CSV document header
139140
*/
140-
public function process(Reader $csv, array $header = []): ResultSet
141+
public function process($records, array $header = []): ResultSet
141142
{
143+
if (!($records instanceof Reader) && !($records instanceof ResultSet)) {
144+
throw new TypeError(sprintf(
145+
'%s::parse expects parameter 1 to be a %s or a %s object, %s given',
146+
Statement::class,
147+
ResultSet::class,
148+
Reader::class,
149+
get_class($records)
150+
));
151+
}
152+
142153
if ([] === $header) {
143-
$header = $csv->getHeader();
154+
$header = $records->getHeader();
144155
}
145156

146-
$iterator = array_reduce($this->where, [$this, 'filter'], $csv->getRecords($header));
157+
$iterator = $this->combineHeader($records, $header);
158+
$iterator = array_reduce($this->where, [$this, 'filter'], $iterator);
147159
$iterator = $this->buildOrderBy($iterator);
148160

149161
return new ResultSet(new LimitIterator($iterator, $this->offset, $this->limit), $header);
150162
}
151163

164+
/**
165+
* Combine the CSV header to each record if present.
166+
*
167+
* @param Reader|ResultSet $iterator
168+
* @param string[] $header
169+
*/
170+
protected function combineHeader($iterator, array $header): Iterator
171+
{
172+
if ($iterator instanceof Reader) {
173+
return $iterator->getRecords($header);
174+
}
175+
176+
if ($header === $iterator->getHeader()) {
177+
return $iterator->getRecords();
178+
}
179+
180+
$field_count = count($header);
181+
$mapper = static function (array $record) use ($header, $field_count): array {
182+
if (count($record) != $field_count) {
183+
$record = array_slice(array_pad($record, $field_count, null), 0, $field_count);
184+
}
185+
186+
/** @var array<string|null> $assocRecord */
187+
$assocRecord = array_combine($header, $record);
188+
189+
return $assocRecord;
190+
};
191+
192+
return new MapIterator($iterator, $mapper);
193+
}
194+
152195
/**
153196
* Filters elements of an Iterator using a callback function.
154197
*/
@@ -176,9 +219,12 @@ protected function buildOrderBy(Iterator $iterator): Iterator
176219
return $cmp ?? 0;
177220
};
178221

179-
$iterator = new ArrayIterator(iterator_to_array($iterator));
180-
$iterator->uasort($compare);
222+
$it = new ArrayIterator();
223+
foreach ($iterator as $offset => $value) {
224+
$it[$offset] = $value;
225+
}
226+
$it->uasort($compare);
181227

182-
return $iterator;
228+
return $it;
183229
}
184230
}

tests/ResultSetTest.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
use OutOfBoundsException;
2020
use PHPUnit\Framework\TestCase;
2121
use SplTempFileObject;
22+
use stdClass;
23+
use TypeError;
2224
use function array_reverse;
2325
use function current;
2426
use function in_array;
@@ -113,6 +115,13 @@ public function testStatementSameInstance(): void
113115
self::assertSame($stmt_alt, $this->stmt);
114116
}
115117

118+
public function testProcessThrowsOnUnsupportedFormat(): void
119+
{
120+
self::expectException(TypeError::class);
121+
$stmt = Statement::create();
122+
$stmt->process(new stdClass());
123+
}
124+
116125
/**
117126
* @covers League\Csv\Statement::limit
118127
*/
@@ -183,17 +192,32 @@ public function testIntervalThrowException(): void
183192
/**
184193
* @covers League\Csv\Statement::where
185194
* @covers League\Csv\Statement::create
195+
* @covers League\Csv\Statement::process
196+
* @covers League\Csv\Statement::combineHeader
186197
*/
187198
public function testFilter(): void
188199
{
189-
$func = function ($row) {
200+
$func1 = function ($row) {
190201
return !in_array('jane', $row, true);
191202
};
192203

204+
$func2 = function ($row) {
205+
return !in_array('john', $row, true);
206+
};
207+
208+
$stmt = Statement::create($func1);
209+
$result1 = $stmt->process($this->csv);
210+
211+
$result2 = $stmt->where($func2)->process($result1, ['foo', 'bar']);
212+
$result3 = $stmt->where($func2)->process($result2, ['foo', 'bar']);
213+
193214
self::assertNotContains(
194215
['jane', 'doe', '[email protected]'],
195-
iterator_to_array(Statement::create($func)->process($this->csv), false)
216+
iterator_to_array($result1, false)
196217
);
218+
219+
self::assertCount(0, $result2);
220+
self::assertEquals($result3, $result2);
197221
}
198222

199223
/**

0 commit comments

Comments
 (0)