Passed
Branch dev (db958f)
by Andy
02:18
created

Reader::key()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
namespace Palmtree\Csv;
4
5
use Palmtree\Csv\Formatter\FormatterInterface;
6
use Palmtree\Csv\Formatter\StringFormatter;
7
use Palmtree\Csv\Row\Row;
8
9
/**
10
 * Reads a CSV file by loading each line into memory
11
 * one at a time.
12
 */
13
class Reader extends AbstractCsv implements \Iterator, \Countable
14
{
15
    protected $fopenMode = 'r';
16
    /** @var FormatterInterface[] */
17
    protected $formatters = [];
18
    /** @var int */
19
    protected $index = 0;
20
    /** @var array */
21
    protected $headers;
22
    /** @var array */
23
    protected $row;
24
    /** @var string */
25
    protected $escapeCharacter = "\0";
26
27
    /**
28
     * @param $file
29
     *
30
     * @return array
31
     */
32
    public static function read($file)
33
    {
34
        $csv = new static($file);
35
36
        return iterator_to_array($csv);
37
    }
38
39
    /**
40
     * @return array
41
     */
42
    public function getHeaders()
43
    {
44
        return $this->headers;
45
    }
46
47
    /**
48
     * @param mixed              $key
49
     * @param FormatterInterface $formatter
50
     *
51
     * @return $this
52
     */
53
    public function addFormatter($key, FormatterInterface $formatter)
54
    {
55
        $this->formatters[$key] = $formatter;
56
57
        return $this;
58
    }
59
60
    /**
61
     * @param array|\Traversable $formatters
62
     *
63
     * @return $this
64
     */
65
    public function addFormatters($formatters)
66
    {
67
        foreach ($formatters as $key => $formatter) {
68
            $this->addFormatter($key, $formatter);
69
        }
70
71
        return $this;
72
    }
73
74
    /**
75
     * @param mixed $key
76
     *
77
     * @return null|FormatterInterface
78
     */
79
    public function getFormatter($key)
80
    {
81
        if (!isset($this->formatters[$key])) {
82
            $this->formatters[$key] = new StringFormatter();
83
        }
84
85
        return $this->formatters[$key];
86
    }
87
88
    /**
89
     * @return string
90
     */
91
    public function getEscapeCharacter()
92
    {
93
        return $this->escapeCharacter;
94
    }
95
96
    /**
97
     * @param string $escapeCharacter
98
     *
99
     * @return AbstractCsv
100
     */
101
    public function setEscapeCharacter($escapeCharacter)
102
    {
103
        $this->escapeCharacter = $escapeCharacter;
104
105
        return $this;
106
    }
107
108
    /**
109
     * Returns an array of cells for the next row.
110
     *
111
     * @param bool $raw
112
     *
113
     * @return Row
114
     */
115
    protected function getNextRow($raw = false)
116
    {
117
        if (!$this->getFileHandle()) {
118
            $this->createFileHandle();
119
        }
120
121
        $row = fgetcsv(
122
            $this->getFileHandle(),
123
            null,
124
            $this->getDelimiter(),
125
            $this->getEnclosure(),
126
            $this->getEscapeCharacter()
127
        );
128
129
        if ($raw || !$row) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $row of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
130
            return $row;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $row; (array) is incompatible with the return type documented by Palmtree\Csv\Reader::getNextRow of type Palmtree\Csv\Row\Row.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
131
        }
132
133
        $row = new Row($row, $this);
134
135
        return $row;
136
    }
137
138
    /**
139
     * @inheritDoc
140
     */
141
    public function current()
142
    {
143
        return $this->row;
144
    }
145
146
    /**
147
     * @inheritDoc
148
     */
149
    public function next()
150
    {
151
        ++$this->index;
152
    }
153
154
    /**
155
     * @inheritDoc
156
     */
157
    public function key()
158
    {
159
        return $this->index;
160
    }
161
162
    /**
163
     * @inheritDoc
164
     */
165
    public function valid()
166
    {
167
        $this->row = $this->getNextRow();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getNextRow() of type object<Palmtree\Csv\Row\Row> is incompatible with the declared type array of property $row.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
168
169
        return $this->row !== false && $this->row !== null;
170
    }
171
172
    /**
173
     * @inheritDoc
174
     */
175
    public function rewind()
176
    {
177
        if ($this->getFileHandle()) {
178
            rewind($this->getFileHandle());
179
        }
180
181
        $this->index = 0;
182
183
        if ($this->hasHeaders()) {
184
            $this->headers = $this->getNextRow(true);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getNextRow(true) of type object<Palmtree\Csv\Row\Row> is incompatible with the declared type array of property $headers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
185
        }
186
    }
187
188
    /**
189
     * @inheritDoc
190
     */
191
    public function count()
192
    {
193
        return count(iterator_to_array($this));
194
    }
195
}
196