Skip to content

Commit

Permalink
Use HeaderStrategy in Reader
Browse files Browse the repository at this point in the history
  • Loading branch information
chadicus committed Jun 9, 2018
1 parent 8c1c292 commit 2d4bf0f
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 42 deletions.
61 changes: 24 additions & 37 deletions src/Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace SubjectivePHP\Csv;

use SplFileObject;

/**
* Simple class for reading delimited data files
*/
Expand All @@ -10,17 +12,10 @@ class Reader implements \Iterator
/**
* The column headers.
*
* @var array|null
* @var array
*/
private $headers;

/**
* File pointer to the csv file.
*
* @var resource
*/
private $handle;

/**
* The current file pointer position.
*
Expand All @@ -36,32 +31,39 @@ class Reader implements \Iterator
private $current = null;

/**
* @var CsvOptions
* @var SplFileObject
*/
private $csvOptions;
private $fileObject;

/**
* Create a new Reader instance.
*
* @param string $file The full path to the csv file.
* @param array $headers The column headers. If null, the headers will be derived from the first line in
* the file.
* @param CsvOptions $csvOptions Options for the csv file.
* @param string $file The full path to the csv file.
* @param HeaderStrategyInterface $headerStrategy Strategy for obtaining headers of the file.
* @param CsvOptions $csvOptions Options for the csv file.
*
* @throws \InvalidArgumentException Thrown if $file is not readable.
*/
public function __construct($file, array $headers = null, CsvOptions $csvOptions = null)
public function __construct($file, HeaderStrategyInterface $headerStrategy = null, CsvOptions $csvOptions = null)
{
if (!is_readable((string)$file)) {
throw new \InvalidArgumentException(
'$file must be a string containing a full path to a readable delimited file'
);
}

$this->csvOptions = $csvOptions ?? new CsvOptions();
$csvOptions = $csvOptions ?? new CsvOptions();
$headerStrategy = $headerStrategy ?? HeaderStrategy::derive();

$this->headers = $headers;
$this->handle = fopen((string)$file, 'r');
$this->fileObject = new SplFileObject($file);
$this->fileObject->setFlags(SplFileObject::READ_CSV);
$this->fileObject->setCsvControl(
$csvOptions->getDelimiter(),
$csvOptions->getEnclosure(),
$csvOptions->getEscapeChar()
);

$this->headers = $headerStrategy->getHeaders($this->fileObject);
}

/**
Expand All @@ -78,13 +80,6 @@ public function next()
$this->current = array_combine($this->headers, $raw);
}

if ($this->headers === null) {
//No headers given, derive from first line of file
$this->headers = $raw;
$this->current = array_combine($this->headers, $this->readLine());
return;
}

//Headers given, skip first line if header line
if ($raw === $this->headers) {
$raw = $this->readLine();
Expand All @@ -106,13 +101,7 @@ public function next()
*/
private function readLine()
{
$raw = fgetcsv(
$this->handle,
0,
$this->csvOptions->getDelimiter(),
$this->csvOptions->getEnclosure(),
$this->csvOptions->getEscapeChar()
);
$raw = $this->fileObject->fgetcsv();
if (empty($raw)) {
throw new \Exception('Empty line read');
}
Expand Down Expand Up @@ -151,7 +140,7 @@ public function key()
*/
public function rewind()
{
rewind($this->handle);
$this->fileObject->rewind();
$this->position = 0;
$this->current = null;
}
Expand All @@ -167,7 +156,7 @@ public function valid()
$this->next();
}

return !feof($this->handle) && $this->current !== false;
return !$this->fileObject->eof() && $this->current !== false;
}

/**
Expand All @@ -177,8 +166,6 @@ public function valid()
*/
public function __destruct()
{
if (is_resource($this->handle)) {
fclose($this->handle);
}
$this->fileObject = null;
}
}
64 changes: 59 additions & 5 deletions tests/ReaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace SubjectivePHPTest\Csv;

use SubjectivePHP\Csv\CsvOptions;
use SubjectivePHP\Csv\HeaderStrategy;
use SubjectivePHP\Csv\Reader;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -81,6 +82,47 @@ public function basicUsage(Reader $reader)
}
}

/**
* @test
*/
public function readNoHeaders()
{
$expected = [
[
'bk101',
'Gambardella, Matthew',
'XML Developer\'s Guide',
'Computer',
'44.95',
'2000-10-01',
'An in-depth look at creating applications with XML.',
],
[
'bk102',
'Ralls, Kim',
'Midnight Rain',
'Fantasy',
'5.95',
'2000-12-16',
'A former architect battles corporate zombies and an evil sorceress.',
],
[
'bk103',
'Corets, Eva',
'Maeve Ascendant',
'Fantasy',
'5.95',
'2000-11-17',
'Young survivors lay the foundation for a new society in England.',
],
];

$reader = new Reader(__DIR__ . '/_files/no_headers.csv', HeaderStrategy::none());
foreach ($reader as $key => $row) {
$this->assertSame($expected[$key], $row);
}
}

/**
* Data provider for basic usage test
*
Expand All @@ -91,10 +133,22 @@ public function getReaders()
$headers = ['id', 'author', 'title', 'genre', 'price', 'publish_date', 'description'];
return [
[new Reader(__DIR__ . '/_files/basic.csv')],
[new Reader(__DIR__ . '/_files/basic.csv', $headers)],
[new Reader(__DIR__ . '/_files/no_headers.csv', $headers)],
[new Reader(__DIR__ . '/_files/pipe_delimited.txt', $headers, new CsvOptions('|'))],
[new Reader(__DIR__ . '/_files/tab_delimited.txt', $headers, new CsvOptions("\t"))],
[new Reader(__DIR__ . '/_files/basic.csv', HeaderStrategy::provide($headers))],
[new Reader(__DIR__ . '/_files/no_headers.csv', HeaderStrategy::provide($headers))],
[
new Reader(
__DIR__ . '/_files/pipe_delimited.txt',
HeaderStrategy::provide($headers),
new CsvOptions('|')
)
],
[
new Reader(
__DIR__ . '/_files/tab_delimited.txt',
HeaderStrategy::provide($headers),
new CsvOptions("\t")
)
],
];
}

Expand Down Expand Up @@ -216,7 +270,7 @@ public function getEmptyFiles()
return [
[new Reader(__DIR__ . '/_files/empty.csv')],
[new Reader(__DIR__ . '/_files/headers_only.csv')],
[new Reader(__DIR__ . '/_files/headers_only.csv', $headers)],
[new Reader(__DIR__ . '/_files/headers_only.csv', HeaderStrategy::provide($headers))],
];
}
}

0 comments on commit 2d4bf0f

Please sign in to comment.