Completed
Branch releases/v0.2 (d913c4)
by Luke
02:17
created

Flavor::hasHeader()   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 1
Bugs 0 Features 0
Metric Value
c 1
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
 *
5
 * Inspired by Python's CSV module and Frictionless Data and the W3C's CSV
6
 * standardization efforts, CSVelte was written in an effort to take all the
7
 * suck out of working with CSV.
8
 *
9
 * @version   v0.2
10
 * @copyright Copyright (c) 2016 Luke Visinoni <[email protected]>
11
 * @author    Luke Visinoni <[email protected]>
12
 * @license   https://github.com/deni-zen/csvelte/blob/master/LICENSE The MIT License (MIT)
13
 */
14
namespace CSVelte;
15
16
use \InvalidArgumentException;
17
use CSVelte\Exception\ImmutableException;
18
19
/**
20
 * CSV Flavor
21
 *
22
 * Represents a particular "flavor"  of CSV. Inspired by python's csv "dialects".
23
 * Also inspired by Frictionless Data's "dialect description" format and the W3C's
24
 * CSV on the Web Working Group and their work on CSV dialects.
25
 *
26
 * @package CSVelte
27
 * @subpackage Flavor
28
 * @since v0.1
29
 * @property-read string $delimiter The delimiter character
30
 * @property-read string $quoteChar The quoting character
31
 * @property-read string $lineTerminator The character sequence used to terminate rows of data
32
 * @property-read string $escapeChar The character used to escape quotes within a quoted string
33
 *     Mutually exclusive to $doubleQuote
34
 * @property-read bool $doubleQuote If true, quote characters will be escaped by preceding them
35
 *     with another quote character. Mutually exclusive to $escapeChar
36
 * @property-read string $quoteStyle One of four class constants that determine which cells are quoted
37
 * @property-read bool $header If true, first row should be treated as a header row
38
 */
39
class Flavor
40
{
41
    /**
42
     * Quote all cells.
43
     * Set Flavor::$quoteStyle to this to quote all cells, regardless of data type
44
     * @var string
45
     */
46
    const QUOTE_ALL = 'quote_all';
47
48
    /**
49
     * Quote no cells.
50
     * Set Flavor::$quoteStyle to this to quote no columns, regardless of data type
51
     * @var string
52
     */
53
    const QUOTE_NONE = 'quote_none';
54
55
    /**
56
     * Quote minimal columns.
57
     * Set Flavor::$quoteStyle to this to quote only cells that contain special
58
     * characters such as newlines or the delimiter character
59
     * @var string
60
     */
61
    const QUOTE_MINIMAL = 'quote_minimal';
62
63
    /**
64
     * Quote non-numeric cells.
65
     * Set Flavor::$quoteStyle to this to quote only cells that contain
66
     * non-numeric data
67
     * @var string
68
     */
69
    const QUOTE_NONNUMERIC = 'quote_nonnumeric';
70
71
    /**
72
     * Delimiter character.
73
     * This is the character that will be used to separate data cells within a
74
     * row of CSV data. Usually a comma.
75
     * @var string
76
     */
77
    protected $delimiter = ",";
78
79
    /**
80
     * Quote character.
81
     * This is the character that will be used to enclose (quote) data cells. It
82
     * is usually a double quote character but single quote is allowed.
83
     * @var string
84
     */
85
    protected $quoteChar = '"';
86
87
    /**
88
     * Escape character.
89
     * This character will be used to escape quotes within quoted text. It is
90
     * mutually exclusive to the doubleQuote attribute. Usually a backspace.
91
     * @var string
92
     */
93
    protected $escapeChar = '\\';
94
95
    /**
96
     * Double quote escape mode.
97
     * If set to true, quote characters within quoted text will be escaped by
98
     * preceding them with the same quote character.
99
     * @var boolean
100
     */
101
    protected $doubleQuote = true;
102
103
    /**
104
     * Not yet implemented
105
     * @ignore
106
     */
107
    // protected $skipInitialSpace = false;
108
109
    /**
110
     * Quoting style.
111
     * This may be set to one of four values:
112
     *     * *Flavor::QUOTE_NONE* - To never quote data cells
113
     *     * *Flavor::QUOTE_ALL* - To always quote data cells
114
     *     * *Flavor::QUOTE_MINIMAL* - To only quote data cells that contain special characters such as quote character or delimiter character
115
     *     * *Flavor::QUOTE_NONNUMERIC* - To quote data cells that contain non-numeric data
116
     * @var string
117
     */
118
    protected $quoteStyle = self::QUOTE_MINIMAL;
119
120
    /**
121
     * Line terminator string sequence.
122
     * This is a character or sequence of characters that will be used to denote
123
     * the end of a row within the data
124
     * @var string
125
     */
126
    protected $lineTerminator = "\r\n";
127
128
    /**
129
     * Header.
130
     * If set to true, this means the first line of the CSV data is to be treated
131
     * as the column headers.
132
     * @var boolean
133
     */
134
    protected $header;
135
136
    /**
137
     * Class constructor
138
     *
139
     * The attributes that make up a flavor object can only be specified by
140
     * passing them in an array as key => value pairs to the constructor. Once
141
     * the flavor object is created, its attributes cannot be changed.
142
     *
143
     * @param array The attributes that define this particular flavor. These
144
     *     attributes are immutable. They can only be set here.
145
     */
146 42
    public function __construct($attributes = null)
147
    {
148 42
        if (!is_null($attributes)) {
149 30
            if (!is_array($attributes)) {
150
                // @todo throw exception?
151 1
                return;
152
            }
153 29
            foreach ($attributes as $attr => $val) {
154 29
                $this->assertValidAttribute($attr);
155 29
                $this->$attr = $val;
156 29
            }
157 29
        }
158 42
    }
159
160
    /**
161
     * Does this flavor of CSV have a header row?
162
     *
163
     * The difference between $flavor->header and $flavor->hasHeader() is that
164
     * hasHeader() is always going to give you a boolean value, whereas
165
     * $flavor->header may be null. A null value for header could mean that the
166
     * taster class could not reliably determine whether or not there was a
167
     * header row or it could simply mean that the flavor was instantiated with
168
     * no value for the header property.
169
     *
170
     * @param void
171
     * @return boolean
172
     * @access public
173
     */
174 1
    public function hasHeader()
175
    {
176 1
        return (bool) $this->header;
177
    }
178
179
    /**
180
     * Assert valid attribute name.
181
     * Assert that a particular attribute is valid (basically just that it exists)
182
     * and throw an exception otherwise
183
     *
184
     * @param string The attribute to check validity of
185
     * @return void
186
     * @access protected
187
     * @throws InvalidArgumentException
188
     * @internal
189
     * @todo This should accept a second parameter for value that asserts the value
190
     *     is a valid value
191
     */
192 38
    protected function assertValidAttribute($attr)
193
    {
194 38
        if (!property_exists(self::class, $attr))
195 38
            throw new InvalidArgumentException("Unknown attribute: " . $attr);
196 37
    }
197
198
    /**
199
     * Copy this flavor object
200
     *
201
     * Because flavor attributes are immutable, it is implossible to change their
202
     * attributes. If you need to change a flavor's attributes, call this method
203
     * instead, specifying which attributes are to be changed.
204
     *
205
     * @param array An array of attribute name/values to change in the copied flavor
206
     * @return CSVelte\Flavor A flavor object with your new attributes
207
     * @access public
208
     * @todo I may want to remove the array type-hint so that this can accept
209
     *     array-like objects and iterables as well. Not sure...
210
     */
211 7
    public function copy(array $attribs = array())
212
    {
213 7
        return new Flavor(array_merge($this->toArray(), $attribs));
214
    }
215
216
    /**
217
     * Attribute accessor magic method
218
     *
219
     * @param string The attribute to "get"
220
     * @return string The attribute value
221
     * @access public
222
     * @internal
223
     * @throws InvalidArgumentException
224
     */
225 32
    public function __get($attr)
226
    {
227 32
        $this->assertValidAttribute($attr);
228 31
        return $this->$attr;
229
    }
230
231
    /**
232
     * Attribute accessor (setter) magic method.
233
     * Disabled because attributes are immutable (read-only)
234
     *
235
     * @param string The attribute to "set"
236
     * @param string The attribute value
237
     * @return void
238
     * @access public
239
     * @internal
240
     * @throws CSVelte\Exception\ImmutableException
241
     */
242 1
    public function __set($attr, $val)
243
    {
244 1
        throw new ImmutableException("Cannot change attributes on an immutable object: " . self::class . "::\$" . $attr);
245
    }
246
247 9
    public function toArray()
248
    {
249 9
        return get_object_vars($this);
250
    }
251
252
}
253