Completed
Push — master ( 5c1aea...9ef1c4 )
by Luke
03:03
created

AbstractRow::assertOffsetExists()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
crap 2
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.1
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 CSVelte\Collection;
16
use \Iterator;
17
use \Countable;
18
use \ArrayAccess;
19
20
use \InvalidArgumentException;
21
use CSVelte\Exception\ImmutableException;
22
23
use function CSVelte\collect;
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 collection of fields for this row
40
     * @var Collection
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
     */
55 58
    public function __construct($fields)
56
    {
57 58
        $this->setFields($fields)
58 57
             ->rewind();
59 57
    }
60
61
    /**
62
     * Set the row fields.
63
     *
64
     * Using either an array or iterator, set the fields for this row.
65
     *
66
     * @param array|Iterator $fields An array or iterator with the row's fields
67
     * @return $this
68
     */
69 58
    protected function setFields($fields)
70
    {
71 58
        if (!is_array($fields)) {
72 3
            if (is_object($fields) && method_exists($fields, 'toArray')) {
73 1
                $fields = $fields->toArray();
74 3
            } elseif ($fields instanceof Iterator) {
75 1
                $fields = iterator_to_array($fields);
76 1
            } else {
77 1
                throw new InvalidArgumentException(__CLASS__ . " requires an array, got: " . gettype($fields));
78
            }
79 2
        }
80 57
        $this->fields = collect(array_values($fields));
81 57
        return $this;
82
    }
83
84
    /**
85
     * Return a string representation of this object
86
     *
87
     * @return string
88
     */
89 1
    public function __toString()
90
    {
91 1
        return $this->join();
92
    }
93
94
    /**
95
     * Join fields together using specified delimiter
96
     *
97
     * @param string The delimiter character
98
     * @return string
99
     */
100 17
    public function join($delimiter = ',')
101
    {
102 17
        return $this->fields->join($delimiter);
103
    }
104
105
    /**
106
     * Convert object to an array
107
     *
108
     * @return array representation of the object
109
     */
110 25
    public function toArray()
111
    {
112 25
        return $this->fields->toArray();
113
    }
114
115
    /** Begin SPL Countable Interface Method **/
116
117
    /**
118
     * Count fields within the row
119
     *
120
     * @return integer The amount of fields
121
     */
122 20
    public function count()
123
    {
124 20
        return count($this->fields);
125
    }
126
127
    /** Begin SPL Iterator Interface Methods **/
128
129
    /**
130
     * Get the current column's data object
131
     *
132
     * @return string
133
     */
134 57
    public function current()
135
    {
136 57
        return $this->fields->getValueAtPosition($this->position);
137
    }
138
139
    /**
140
     * Get the current key (column number or header, if available)
141
     *
142
     * @return string The "current" key
143
     * @todo Figure out if this can return a CSVelte\Table\HeaderData object so long as it
144
     *     has a __toString() method that generated the right key...
145
     */
146
    public function key()
147
    {
148
        return $this->position;
149
    }
150
151
    /**
152
     * Advance the internal pointer to the next column's data object
153
     * Also returns the next column's data object if there is one
154
     *
155
     * @return mixed The "next" column's data
156
     */
157 7
    public function next()
158
    {
159 7
        $this->position++;
160 7
        if ($this->valid()) return $this->current();
161 6
    }
162
163
    /**
164
     * Return the internal pointer to the first column and return that object
165
     *
166
     * @return null|mixed|AbstractRow
167
     */
168 57
    public function rewind()
169
    {
170 57
        $this->position = 0;
171 57
        if ($this->valid()) {
172 57
            return $this->current();
173
        }
174
    }
175
176
    /**
177
     * Is the current position within the row's data fields valid?
178
     *
179
     * @return boolean
180
     */
181 57
    public function valid()
182
    {
183 57
        return $this->fields->hasPosition($this->position);
184
    }
185
186
    /** Begin SPL ArrayAccess Methods **/
187
188
    /**
189
     * Is there an offset at specified position
190
     *
191
     * @param mixed $offset The offset to check existence of
192
     * @return bool
193
     * @internal param Offset $integer
194
     */
195 2
    public function offsetExists($offset)
196
    {
197 2
        return $this->fields->hasPosition($offset);
198
    }
199
200
    /**
201
     * Retrieve offset at specified position or by header name
202
     *
203
     * @param mixed $offset The offset to get
204
     * @return mixed The data at the specified position
205
     */
206 1
    public function offsetGet($offset)
207
    {
208 1
        return $this->fields->getValueAtPosition($offset);
209
    }
210
211
    /**
212
     * Set offset at specified position
213
     *
214
     * @param mixed $offset The array offset to set
215
     * @param mixed $value The value to set $offset to
216
     * @throws ImmutableException
217
     */
218 2
    public function offsetSet($offset, $value)
219
    {
220
        // fields are immutable, cannot be set
221 2
        $this->raiseImmutableException();
222
    }
223
224
    /**
225
     * Unset offset at specified position/index
226
     *
227
     * @param mixed $offset The offset to unset
228
     * @throws ImmutableException
229
     * @todo I'm not sure if these objects will stay immutable or not yet...
230
     */
231 1
    public function offsetUnset($offset)
232
    {
233 1
        $this->raiseImmutableException();
234
    }
235
236
    /**
237
     * Raise (throw) immutable exception
238
     *
239
     * @param string $msg The message to pass to the exception
240
     * @throws ImmutableException
241
     */
242 3
    protected function raiseImmutableException($msg = null)
243
    {
244
        // fields are immutable, cannot be set
245 3
        throw new ImmutableException($msg ?: 'Cannot change immutable column data');
246
    }
247
}
248