These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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 \Iterator |
||
11 | { |
||
12 | /** |
||
13 | * The column headers. |
||
14 | * |
||
15 | * @var array |
||
16 | */ |
||
17 | private $headers; |
||
18 | |||
19 | /** |
||
20 | * The current file pointer position. |
||
21 | * |
||
22 | * @var integer |
||
23 | */ |
||
24 | private $position = 0; |
||
25 | |||
26 | /** |
||
27 | * The current row within the csv file. |
||
28 | * |
||
29 | * @var array|false|null |
||
30 | */ |
||
31 | private $current = null; |
||
32 | |||
33 | /** |
||
34 | * @var SplFileObject |
||
35 | */ |
||
36 | private $fileObject; |
||
37 | |||
38 | /** |
||
39 | * @var HeaderStrategyInterface |
||
40 | */ |
||
41 | private $headerStrategy; |
||
42 | |||
43 | /** |
||
44 | * Create a new Reader instance. |
||
45 | * |
||
46 | * @param string $file The full path to the csv file. |
||
47 | * @param HeaderStrategyInterface $headerStrategy Strategy for obtaining headers of the file. |
||
48 | * @param CsvOptions $csvOptions Options for the csv file. |
||
49 | * |
||
50 | * @throws \InvalidArgumentException Thrown if $file is not readable. |
||
51 | */ |
||
52 | public function __construct($file, HeaderStrategyInterface $headerStrategy = null, CsvOptions $csvOptions = null) |
||
53 | { |
||
54 | if (!is_readable((string)$file)) { |
||
55 | throw new \InvalidArgumentException( |
||
56 | '$file must be a string containing a full path to a readable delimited file' |
||
57 | ); |
||
58 | } |
||
59 | |||
60 | $csvOptions = $csvOptions ?? new CsvOptions(); |
||
61 | $this->headerStrategy = $headerStrategy ?? new DeriveHeaderStrategy(); |
||
62 | |||
63 | $this->fileObject = new SplFileObject($file); |
||
64 | $this->fileObject->setFlags(SplFileObject::READ_CSV); |
||
65 | $this->fileObject->setCsvControl( |
||
66 | $csvOptions->getDelimiter(), |
||
67 | $csvOptions->getEnclosure(), |
||
68 | $csvOptions->getEscapeChar() |
||
69 | ); |
||
70 | |||
71 | $this->headers = $this->headerStrategy->getHeaders($this->fileObject); |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Advances to the next row in this csv reader |
||
76 | * |
||
77 | * @return mixed |
||
78 | */ |
||
79 | public function next() |
||
80 | { |
||
81 | try { |
||
82 | $raw = $this->readLine(); |
||
83 | if ($this->current !== null) { |
||
84 | ++$this->position; |
||
85 | $this->current = array_combine($this->headers, $raw); |
||
86 | } |
||
87 | |||
88 | //Headers given, skip first line if header line |
||
89 | if ($this->headerStrategy->isHeaderRow($raw)) { |
||
0 ignored issues
–
show
|
|||
90 | $raw = $this->readLine(); |
||
91 | } |
||
92 | |||
93 | $this->current = array_combine($this->headers, $raw); |
||
94 | } catch (\Exception $e) { |
||
95 | $this->current = false; |
||
96 | return false; |
||
97 | } |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * Helper method to read the next line in the delimited file. |
||
102 | * |
||
103 | * @return array|false |
||
104 | * |
||
105 | * @throws \Exception Thrown if no data is returned when reading the file. |
||
106 | */ |
||
107 | private function readLine() |
||
108 | { |
||
109 | $raw = $this->fileObject->fgetcsv(); |
||
110 | if (empty($raw)) { |
||
111 | throw new \Exception('Empty line read'); |
||
112 | } |
||
113 | |||
114 | return $raw; |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Return the current element. |
||
119 | * |
||
120 | * @return array returns array containing values from the current row |
||
121 | */ |
||
122 | public function current() |
||
123 | { |
||
124 | if ($this->current === null) { |
||
125 | $this->next(); |
||
126 | } |
||
127 | |||
128 | return $this->current; |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Return the key of the current element. |
||
133 | * |
||
134 | * @return integer |
||
135 | */ |
||
136 | public function key() |
||
137 | { |
||
138 | return $this->position; |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * Rewind the Iterator to the first element. |
||
143 | * |
||
144 | * @return void |
||
145 | */ |
||
146 | public function rewind() |
||
147 | { |
||
148 | $this->fileObject->rewind(); |
||
149 | $this->position = 0; |
||
150 | $this->current = null; |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * Check if there is a current element after calls to rewind() or next(). |
||
155 | * |
||
156 | * @return bool true if there is a current element, false otherwise |
||
157 | */ |
||
158 | public function valid() |
||
159 | { |
||
160 | if ($this->current === null) { |
||
161 | $this->next(); |
||
162 | } |
||
163 | |||
164 | return !$this->fileObject->eof() && $this->current !== false; |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * Ensure file handles are closed when all references to this reader are destroyed. |
||
169 | * |
||
170 | * @return void |
||
171 | */ |
||
172 | public function __destruct() |
||
173 | { |
||
174 | $this->fileObject = null; |
||
175 | } |
||
176 | } |
||
177 |
This check looks for type mismatches where the missing type is
false
. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTime
object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalse
before passing on the value to another function or method that may not be able to handle afalse
.