Completed
Pull Request — master (#31)
by Chad
01:24
created

Reader::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 6
rs 9.4285
c 1
b 0
f 0
cc 1
eloc 4
nc 1
nop 3
1
<?php
2
3
namespace SubjectivePHP\Csv;
4
5
use SplFileObject;
6
7
/**
8
 * Simple class for reading delimited data files
9
 */
10
class Reader implements \IteratorAggregate
11
{
12
    /**
13
     * @var SplFileObject
14
     */
15
    private $fileObject;
16
17
    /**
18
     * @var HeaderStrategyInterface
19
     */
20
    private $headerStrategy;
21
22
    /**
23
     * Create a new Reader instance.
24
     *
25
     * @param string                  $file           The full path to the csv file.
26
     * @param HeaderStrategyInterface $headerStrategy Strategy for obtaining headers of the file.
27
     * @param CsvOptions              $csvOptions     Options for the csv file.
28
     *
29
     * @throws \InvalidArgumentException Thrown if $file is not readable.
30
     */
31
    public function __construct(string $file, HeaderStrategyInterface $headerStrategy = null, CsvOptions $csvOptions = null)
32
    {
33
        $this->fileObject = $this->getFileObject($file, $csvOptions ?? new CsvOptions());
34
        $this->headerStrategy = $headerStrategy ?? new DeriveHeaderStrategy();
35
        $this->headerStrategy->getHeaders($this->fileObject);
36
    }
37
38
    public function getIterator() : \Traversable
39
    {
40
        return $this->getOuterIterator(
41
            $this->getInnerIterator()
42
        );
43
    }
44
45
    private function getFileObject(string $filePath, CsvOptions $csvOptions) : SplFileObject
46
    {
47
        if (!is_readable($filePath)) {
48
            throw new \InvalidArgumentException(
49
                '$file must be a string containing a full path to a readable delimited file'
50
            );
51
        }
52
53
        $fileObject = new SplFileObject($filePath);
54
        $fileObject->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY);
55
        $fileObject->setCsvControl(
56
            $csvOptions->getDelimiter(),
57
            $csvOptions->getEnclosure(),
58
            $csvOptions->getEscapeChar()
59
        );
60
61
        return $fileObject;
62
    }
63
64
    public function __destruct()
65
    {
66
        $this->fileObject = null;
67
    }
68
69
    private function getInnerIterator() : \CallbackFilterIterator
70
    {
71
        $strategy = $this->headerStrategy;
72
        return new \CallbackFilterIterator(
73
            $this->fileObject,
74
            function ($current) use ($strategy) {
75
                return !$strategy->isHeaderRow($current);
76
            }
77
        );
78
    }
79
80
    private function getOuterIterator(\CallbackFilterIterator $innerIterator) : \IteratorIterator
81
    {
82
        return new class($innerIterator, $this->headerStrategy) extends \IteratorIterator
83
        {
84
            private $strategy;
85
86
            public function __construct(\Traversable $innerIterator, HeaderStrategyInterface $strategy)
87
            {
88
                parent::__construct($innerIterator);
89
                $this->strategy = $strategy;
90
            }
91
92
            public function current()
93
            {
94
                return $this->strategy->createDataRow(parent::current());
95
            }
96
        };
97
    }
98
}
99