Completed
Push — master ( 0a5b37...5c1aea )
by Luke
02:18
created

AbstractRow::count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * CSVelte: Slender, elegant CSV for PHP
4
 * Inspired by Python's CSV module and Frictionless Data and the W3C's CSV
5
 * standardization efforts, CSVelte was written in an effort to take all the
6
 * suck out of working with CSV.
7
 *
8
 * @version   v0.2
9
 * @copyright Copyright (c) 2016 Luke Visinoni <[email protected]>
10
 * @author    Luke Visinoni <[email protected]>
11
 * @license   https://github.com/deni-zen/csvelte/blob/master/LICENSE The MIT License (MIT)
12
 */
13
namespace CSVelte\Table;
14
15
use \Iterator;
16
use \Countable;
17
use \ArrayAccess;
18
use CSVelte\Utils;
19
use CSVelte\Flavor;
20
21
use \OutOfBoundsException;
22
use \InvalidArgumentException;
23
use CSVelte\Exception\ImmutableException;
24
25
/**
26
 * Table row abstract base class
27
 * Represents a row of tabular data (represented by CSVelte\Table\Data objects)
28
 *
29
 * @package CSVelte
30
 * @subpackage CSVelte\Table
31
 * @since v0.1
32
 * @todo On all of the ArrayAccess methods, the docblocks say that $offset can be
33
 *     either an integer offset or a string index, but that isn't true, they must
34
 *     be an integer offset. Fix docblocks.
35
 */
36
abstract class AbstractRow implements Iterator, Countable, ArrayAccess
37
{
38
    /**
39
     * An array of fields for this row
40
     * @var array
41
     */
42
    protected $fields;
43
44
    /**
45
     * Iterator position
46
     * @var int
47
     */
48
    protected $position;
49
50
    /**
51
     * Class constructor
52
     *
53
     * @param array|Iterator An array (or anything that looks like one) of data (fields)
54
     * @access public
55
     */
56 59
    public function __construct($fields)
57
    {
58 59
        $this->setFields($fields)
59 58
             ->rewind();
60 58
    }
61
62 59
    protected function setFields($fields)
63
    {
64 59
        if (!is_array($fields)) {
65 3
            if (is_object($fields) && method_exists($fields, 'toArray')) {
66 1
                $fields = $fields->toArray();
67 3
            } elseif ($fields instanceof Iterator) {
68 1
                $fields = iterator_to_array($fields);
69 1
            } else {
70 1
                throw new InvalidArgumentException(__CLASS__ . " requires an array, got: " . gettype($fields));
71
            }
72 2
        }
73 58
        $this->fields = array_values($fields);
74 58
        return $this;
75
    }
76
77 1
    public function __toString()
78
    {
79 1
        return $this->join();
80
    }
81
82
    /**
83
     * Join fields together using specified delimiter
84
     *
85
     * @param char The delimiter character
86
     * @return string
87
     * @access public
88
     */
89 17
    public function join($delimiter = ',')
90
    {
91 17
        return implode($delimiter, $this->fields);
92
    }
93
94
    /**
95
     * Convert object to an array
96
     *
97
     * @return array representation of the object
98
     * @access public
99
     */
100 27
    public function toArray()
101
    {
102 27
        return iterator_to_array($this);
103
    }
104
105
    /** Begin SPL Countable Interface Method **/
106
107
    /**
108
     * Count fields within the row
109
     *
110
     * @return integer The amount of fields
111
     * @access public
112
     */
113 21
    public function count()
114
    {
115 21
        return count($this->fields);
116
    }
117
118
    /** Begin SPL Iterator Interface Methods **/
119
120
    /**
121
     * Get the current column's data object
122
     *
123
     * @return string
124
     * @access public
125
     */
126 58
    public function current()
127
    {
128 58
        return $this->fields[$this->position];
129
    }
130
131
    /**
132
     * Get the current key (column number or header, if available)
133
     *
134
     * @return string The "current" key
135
     * @access public
136
     * @todo Figure out if this can return a CSVelte\Table\HeaderData object so long as it
137
     *     has a __toString() method that generated the right key...
138
     */
139 20
    public function key()
140
    {
141 20
        return $this->position;
142
    }
143
144
    /**
145
     * Advance the internal pointer to the next column's data object
146
     * Also returns the next column's data object if there is one
147
     *
148
     * @return CSVelte\Table\Data The "next" column's data
149
     * @access public
150
     */
151 32
    public function next()
152
    {
153 32
        $this->position++;
154 32
        if ($this->valid()) return $this->current();
155 31
    }
156
157
    /**
158
     * Return the internal pointer to the first column and return that object
159
     *
160
     * @return void
161
     * @access public
162
     */
163 58
    public function rewind()
164
    {
165 58
        $this->position = 0;
166 58
        if ($this->valid()) return $this->current();
167
    }
168
169
    /**
170
     * Is the current position within the row's data fields valid?
171
     *
172
     * @return boolean
173
     * @access public
174
     */
175 58
    public function valid()
176
    {
177 58
        return array_key_exists($this->position, $this->fields);
178
    }
179
180
    /** Begin SPL ArrayAccess Methods **/
181
182
    /**
183
     * Is there an offset at specified position
184
     *
185
     * @param integer Offset
186
     * @return boolean
187
     * @access public
188
     */
189 5
    public function offsetExists($offset)
190
    {
191
        try {
192 5
            Utils::array_get($this->fields, $offset, null, true);
193 5
        } catch (OutOfBoundsException $e) {
194 3
            return false;
195
        }
196 4
        return true;
197
    }
198
199
    /**
200
     * Retrieve offset at specified position or by header name
201
     *
202
     * @param integer|string Offset/index
203
     * @return CSVelte\Table\Data
204
     * @access public
205
     */
206 4
    public function offsetGet($offset)
207
    {
208 4
        $this->assertOffsetExists($offset);
209 3
        return $this->fields[$offset];
210
    }
211
212
    /**
213
     * Set offset at specified position
214
     *
215
     * @param integer|string Offset/index
216
     * @param CSVelte\Table\Data
217
     * @return void
218
     * @access public
219
     * @throws CSVelte\Exception\ImmutableException
220
     */
221 2
    public function offsetSet($offset, $value)
222
    {
223
        // fields are immutable, cannot be set
224 2
        $this->raiseImmutableException();
225
    }
226
227
    /**
228
     * Unset offset at specified position/index
229
     *
230
     * @param integer|string Offset/index
231
     * @return void
232
     * @access public
233
     * @throws CSVelte\Exception\ImmutableException
234
     * @todo I'm not sure if these objects will stay immutable or not yet...
235
     */
236 1
    public function offsetUnset($offset)
237
    {
238 1
        $this->raiseImmutableException();
239
    }
240
241
    /**
242
     * Throw exception unless offset/index exists
243
     *
244
     * @param integer|string Offset/index
245
     * @return void
246
     * @access protected
247
     * @throws \OutOfBoundsException
248
     */
249 4
    protected function assertOffsetExists($offset)
250
    {
251 4
        if (!$this->offsetExists($offset)) {
252 1
            throw new OutOfBoundsException("Undefined offset: " . $offset);
253
        }
254 3
    }
255
256
    /**
257
     * Raise (throw) immutable exception
258
     *
259
     * @param string Message
260
     * @return void
261
     * @access protected
262
     * @throws CSVelte\Exception\ImmutableException
263
     */
264 3
    protected function raiseImmutableException($msg = null)
265
    {
266
        // fields are immutable, cannot be set
267 3
        throw new ImmutableException($msg ?: 'Cannot change immutable column data');
268
    }
269
}
270