Skip to content

Commit d57f115

Browse files
committed
Adding inspection engine
1 parent 137dcde commit d57f115

40 files changed

Lines changed: 3604 additions & 3 deletions

docs/9.0/connections/instantiation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Alternatively, you can use the <code>fromStream</code> method.</p>
6161
```php
6262
public static AbstractCsv::fromStream(SplFileObject|resource $stream): self
6363
```
64+
6465
Creates a new object from a stream resource or a streaming object.
6566

6667
```php

docs/9.0/reader/record-mapping.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ description: Converts your CSV records into PHP objects using PHP's powerful Ref
88

99
<p class="message-notice">New in version <code>9.12.0</code></p>
1010

11-
If you are working with a class which implements the `TabularDataReader` interface you can now deserialize
11+
If you are working with a class which implements the `TabularData` interface you can now deserialize
1212
your data using the `TabularDataReader::getRecordsAsObject` method. The method will convert your document records
1313
into objects using PHP's powerful Reflection API.
1414

phpstan-build.neon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,6 @@ parameters:
1919
treatPhpDocTypesAsCertain: false
2020
parallel:
2121
processTimeout: 300.0
22+
bootstrapFiles:
23+
- vendor/autoload.php
2224

phpstan.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,3 @@ parameters:
2222
treatPhpDocTypesAsCertain: false
2323
parallel:
2424
processTimeout: 300.0
25-

src/Buffer.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use Iterator;
1919
use League\Csv\Query\Constraint\Criteria;
2020
use League\Csv\Query\Predicate;
21+
use League\Csv\Schema\Inspector;
22+
use League\Csv\Schema\Schema;
2123
use League\Csv\Serializer\Denormalizer;
2224
use League\Csv\Serializer\MappingFailed;
2325
use League\Csv\Serializer\TypeCastingFailed;
@@ -203,6 +205,35 @@ public function map(callable $callback): Iterator
203205
return MapIterator::fromIterable($this->getRecords(), $callback);
204206
}
205207

208+
/**
209+
* @param callable(TInitial|null, array<mixed>, array-key=): TInitial $callback
210+
* @param TInitial|null $initial
211+
*
212+
* @template TInitial
213+
*
214+
* @throws SyntaxError
215+
*
216+
* @return TInitial|null
217+
*/
218+
public function reduce(callable $callback, mixed $initial = null): mixed
219+
{
220+
foreach ($this->getRecords() as $offset => $record) {
221+
$initial = $callback($initial, $record, $offset);
222+
}
223+
224+
return $initial;
225+
}
226+
227+
public function inferSchema(?Inspector $inspector = null, array $header = []): Schema
228+
{
229+
return ($inspector ?? Inspector::default())->schema($this, $header);
230+
}
231+
232+
public function inferRecords(?Inspector $inspector = null, array $header = []): Iterator
233+
{
234+
return $this->inferSchema($inspector, $header)->parse($this);
235+
}
236+
206237
/**
207238
* @param non-negative-int $nth
208239
*

src/Reader.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use Deprecated;
1919
use Iterator;
2020
use JsonSerializable;
21+
use League\Csv\Schema\Inspector;
22+
use League\Csv\Schema\Schema;
2123
use League\Csv\Serializer\Denormalizer;
2224
use League\Csv\Serializer\MappingFailed;
2325
use League\Csv\Serializer\TypeCastingFailed;
@@ -416,6 +418,16 @@ public function map(callable $callback): Iterator
416418
return MapIterator::fromIterable($this, $callback);
417419
}
418420

421+
public function inferSchema(?Inspector $inspector = null, array $header = []): Schema
422+
{
423+
return ($inspector ?? Inspector::default())->schema($this, $header);
424+
}
425+
426+
public function inferRecords(?Inspector $inspector = null, array $header = []): Iterator
427+
{
428+
return $this->inferSchema($inspector, $header)->parse($this);
429+
}
430+
419431
/**
420432
* @param positive-int $recordsCount
421433
*

src/ResultSet.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use Generator;
2121
use Iterator;
2222
use JsonSerializable;
23+
use League\Csv\Schema\Inspector;
24+
use League\Csv\Schema\Schema;
2325
use League\Csv\Serializer\Denormalizer;
2426
use League\Csv\Serializer\MappingFailed;
2527
use League\Csv\Serializer\TypeCastingFailed;
@@ -206,6 +208,16 @@ public function map(callable $callback): Iterator
206208
return MapIterator::fromIterable($this, $callback);
207209
}
208210

211+
public function inferSchema(?Inspector $inspector = null, array $header = []): Schema
212+
{
213+
return ($inspector ?? Inspector::default())->schema($this, $header);
214+
}
215+
216+
public function inferRecords(?Inspector $inspector = null, array $header = []): Iterator
217+
{
218+
return $this->inferSchema($inspector, $header)->parse($this);
219+
}
220+
209221
/**
210222
* @param positive-int $recordsCount
211223
*

src/Schema/BooleanField.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
/**
4+
* League.Csv (https://csv.thephpleague.com)
5+
*
6+
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace League\Csv\Schema;
15+
16+
use function filter_var;
17+
use function in_array;
18+
use function is_bool;
19+
use function is_string;
20+
use function trim;
21+
22+
use const FILTER_NULL_ON_FAILURE;
23+
use const FILTER_VALIDATE_BOOLEAN;
24+
25+
final class BooleanField extends FieldEvaluator implements Field
26+
{
27+
public function type(): FieldType
28+
{
29+
return FieldType::Boolean;
30+
}
31+
32+
public function name(): string
33+
{
34+
return FieldType::Boolean->value;
35+
}
36+
37+
public function parse(mixed $value): ?bool
38+
{
39+
if (is_bool($value)) {
40+
return $value;
41+
}
42+
43+
if (!is_string($value) && !in_array($value, [0, 1], true)) {
44+
return null;
45+
}
46+
47+
$value = trim((string) $value);
48+
if ('' === $value) {
49+
return null;
50+
}
51+
52+
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
53+
}
54+
55+
public function metadata(): FieldMetadata
56+
{
57+
return new FieldMetadata();
58+
}
59+
}

src/Schema/BooleanFieldTest.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/**
4+
* League.Csv (https://csv.thephpleague.com)
5+
*
6+
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace League\Csv\Schema;
15+
16+
use PHPUnit\Framework\Attributes\CoversClass;
17+
use PHPUnit\Framework\Attributes\DataProvider;
18+
use PHPUnit\Framework\TestCase;
19+
20+
#[CoversClass(BooleanField::class)]
21+
final class BooleanFieldTest extends TestCase
22+
{
23+
private BooleanField $field;
24+
25+
protected function setUp(): void
26+
{
27+
$this->field = new BooleanField();
28+
}
29+
30+
public static function provideBooleanValues(): array
31+
{
32+
return [
33+
[true, true],
34+
[false, false],
35+
['true', true],
36+
['false', false],
37+
['1', true],
38+
['0', false],
39+
[' true ', true],
40+
['', null],
41+
[' ', null],
42+
['foo', null],
43+
[[], null],
44+
[123, null],
45+
];
46+
}
47+
48+
#[DataProvider('provideBooleanValues')]
49+
public function testParse(mixed $input, ?bool $expected): void
50+
{
51+
$result = $this->field->parse($input);
52+
53+
null === $expected
54+
? self::assertNull($result)
55+
: self::assertSame($expected, $result);
56+
}
57+
58+
public function test_metadata_contains_expected_structure(): void
59+
{
60+
$field = new BooleanField();
61+
62+
self::assertTrue($field->metadata()->isEmpty());
63+
}
64+
}

src/Schema/CallbackFieldParser.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/**
4+
* League.Csv (https://csv.thephpleague.com)
5+
*
6+
* (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace League\Csv\Schema;
15+
16+
use Closure;
17+
18+
/**
19+
* @template T
20+
*/
21+
final class CallbackFieldParser implements FieldParser
22+
{
23+
/** @var Closure(mixed): ?T */
24+
private Closure $callback;
25+
26+
/**
27+
* @param (Closure(mixed): ?T)|(callable(mixed): ?T) $callback
28+
*/
29+
public function __construct(Closure|callable $callback)
30+
{
31+
if (!$callback instanceof Closure) {
32+
$callback = $callback(...);
33+
}
34+
35+
$this->callback = $callback;
36+
}
37+
38+
/**
39+
* @returns ?T
40+
*/
41+
public function parse(mixed $value): mixed
42+
{
43+
return ($this->callback)($value);
44+
}
45+
}

0 commit comments

Comments
 (0)